Skip to content

Instantly share code, notes, and snippets.

@bit-hack
Last active May 10, 2018 23:55
Show Gist options
  • Save bit-hack/ce1a65c5000497a385ffd7e3df57a535 to your computer and use it in GitHub Desktop.
Save bit-hack/ce1a65c5000497a385ffd7e3df57a535 to your computer and use it in GitHub Desktop.
very simple rasterizer
#pragma once
#include <cstdint>
#include <algorithm>
struct float2 {
float x, y;
};
struct float3 {
float x, y, z;
};
struct frame_t {
uint32_t *_pixels;
int32_t _width;
int32_t _height;
};
namespace {
static constexpr float shift = 1 << 20;
constexpr float edgeEval(const float3 &a, const float3 &b, const float2 &c) {
return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x));
}
struct rect_t {
int32_t x0, y0;
int32_t x1, y1;
};
rect_t boundTri(const frame_t &frame, const float3 &v0, const float3 &v1,
const float3 &v2) {
rect_t out;
// Compute triangle bounding box
out.x0 = std::min({int32_t(v0.x), int32_t(v1.x), int32_t(v2.x)});
out.y0 = std::min({int32_t(v0.y), int32_t(v1.y), int32_t(v2.y)});
out.x1 = std::max({int32_t(v0.x), int32_t(v1.x), int32_t(v2.x)});
out.y1 = std::max({int32_t(v0.y), int32_t(v1.y), int32_t(v2.y)});
// Clip against screen bounds
out.x0 = std::max(out.x0, 0);
out.y0 = std::max(out.y0, 0);
out.x1 = std::min(out.x1, frame._width - 1);
out.y1 = std::min(out.y1, frame._height - 1);
return out;
}
} // namespace
void drawTri(const frame_t &frame, const float3 &v0, const float3 &v1,
const float3 &v2, const uint32_t rgb) {
// Compute triangle bounding box
const rect_t rect = boundTri(frame, v0, v1, v2);
// the signed triangle area
const float area =
1.f / ((v1.x - v0.x) * (v2.y - v0.y) - (v2.x - v0.x) * (v1.y - v0.y));
if (area <= 0.f) {
return;
}
// Triangle setup
const int32_t sx01 = int32_t((v0.y - v1.y) * area * shift);
const int32_t sy01 = int32_t((v1.x - v0.x) * area * shift);
const int32_t sx12 = int32_t((v1.y - v2.y) * area * shift);
const int32_t sy12 = int32_t((v2.x - v1.x) * area * shift);
const int32_t sx20 = int32_t((v2.y - v0.y) * area * shift);
const int32_t sy20 = int32_t((v0.x - v2.x) * area * shift);
// Barycentric coordinates at minX/minY corner
const float2 p{float(rect.x0), float(rect.y0)};
int32_t w0_row = int32_t(edgeEval(v1, v2, p) * area * shift);
int32_t w1_row = int32_t(edgeEval(v2, v0, p) * area * shift);
int32_t w2_row = int32_t(edgeEval(v0, v1, p) * area * shift);
// pointer to upper left corner
uint32_t *dst = frame._pixels + rect.y0 * frame._width;
// Rasterize
for (int32_t y = rect.y0; y <= rect.y1; y++) {
// Barycentric coordinates at start of row
int32_t w0 = w0_row;
int32_t w1 = w1_row;
int32_t w2 = w2_row;
for (int32_t x = rect.x0; x <= rect.x1; x++) {
// If p is on or inside all edges, render pixel.
if (0 == ((w0 | w1 | w2) & 0x80000000)) {
uint32_t c = ((w0 << 4) & 0xff0000) |
((w1 >> 4) & 0x00ff00) |
((w2 >> 12) & 0x0000ff);
// renderPixel(p, w0, w1, w2);
dst[x] = c;
}
// One step to the right
w0 += sx12;
w1 += sx20;
w2 += sx01;
}
// One row step
w0_row += sy12;
w1_row += sy20;
w2_row += sy01;
// step y axis
dst += frame._width;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment