Skip to content

Instantly share code, notes, and snippets.

@Philosoph228
Created June 6, 2025 22:56
Show Gist options
  • Save Philosoph228/e6e27158fa24fc4e4462738ced27a84b to your computer and use it in GitHub Desktop.
Save Philosoph228/e6e27158fa24fc4e4462738ced27a84b to your computer and use it in GitHub Desktop.
void RasterizeTriangleTextured(
WindowData* pDat,
Vector3 pts[3], // screen.x,y in pixels; .z = NDC.z [-1..+1]
float invWs[3], // 1/clip.w at each vertex
Vector2 uvs[3], // UV coords [0..1]
BYTE* texture, // raw 24-bit BMP data (BGR, row aligned)
int texWidth,
int texHeight)
{
// 1) Compute bounding box in integer pixels
int minX = (int)fmaxf(0.0f, floorf(fminf(fminf(pts[0].x, pts[1].x), pts[2].x)));
int maxX = (int)fminf((float)(pDat->width - 1), ceilf(fmaxf(fmaxf(pts[0].x, pts[1].x), pts[2].x)));
int minY = (int)fmaxf(0.0f, floorf(fminf(fminf(pts[0].y, pts[1].y), pts[2].y)));
int maxY = (int)fminf((float)(pDat->height - 1), ceilf(fmaxf(fmaxf(pts[0].y, pts[1].y), pts[2].y)));
Vector2 a = { pts[0].x, pts[0].y };
Vector2 b = { pts[1].x, pts[1].y };
Vector2 c = { pts[2].x, pts[2].y };
// Early exit if degenerate triangle (optional)
Barycentric dummy = ComputeBarycentricsSIMD(a, b, c, a); // just to compute denom
if (fabsf(dummy.lambda1 + dummy.lambda2 + dummy.lambda3 - 1.0f) > 1.01f) {
// Could alternatively check denom inside ComputeBarycentrics
return;
}
// 3) Loop over every pixel in bounding box
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
Vector2 p = { (float)x + 0.5f, (float)y + 0.5f }; // sample center of pixel
// 3.a) Compute barycentric coordinates using your helper
Barycentric bary = ComputeBarycentricsSIMD(p, a, b, c);
// 3.b) Cull pixels outside triangle
if (bary.lambda1 < 0.0f || bary.lambda2 < 0.0f || bary.lambda3 < 0.0f) {
continue;
}
// 4) Reconstruct NDC.z at pixel
float ndcZ = bary.lambda1 * pts[0].z +
bary.lambda2 * pts[1].z +
bary.lambda3 * pts[2].z;
int index = y * pDat->width + x;
// 5) Depth test
if (ndcZ >= pDat->zBuffer[index]) {
continue;
}
// 6 & 7) Interpolate perspective-correct UV using your helper
Vector2 uv = InterpolateUV(uvs, invWs, &bary);
// 8) Clamp UV [0..1]
uv.x = fmaxf(0.0f, fminf(1.0f, uv.x));
uv.y = fmaxf(0.0f, fminf(1.0f, uv.y));
// 9) Convert UV to texel indices
int texX = (int)(uv.x * (texWidth - 1));
int texY = (int)((1.0f - uv.y) * (texHeight - 1)); // flip v
// 10) Fetch texel from BMP texture (4-byte aligned rows)
int rowBytes = ((texWidth * 3 + 3) & ~3);
BYTE* pixel = &texture[texY * rowBytes + texX * 3];
// 11) Write pixel & update zBuffer
pDat->zBuffer[index] = ndcZ;
unsigned char* dst = (unsigned char*)pDat->pPixels + index * 4;
dst[0] = pixel[0]; // B
dst[1] = pixel[1]; // G
dst[2] = pixel[2]; // R
dst[3] = 255; // A
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment