Last active
May 10, 2018 23:55
-
-
Save bit-hack/ce1a65c5000497a385ffd7e3df57a535 to your computer and use it in GitHub Desktop.
very simple rasterizer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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