Skip to content

Instantly share code, notes, and snippets.

@LukasKalbertodt
Last active January 24, 2024 08:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LukasKalbertodt/0b809d501ee0262c361616a85dec2846 to your computer and use it in GitHub Desktop.
Save LukasKalbertodt/0b809d501ee0262c361616a85dec2846 to your computer and use it in GitHub Desktop.
// Project the triangle's vertices from light space to our disk space. The grid we rasterize
// to lives between x/y 0 to 1.
let vl0 = v0l - pl;
let vl1 = v1l - pl;
let vl2 = v2l - pl;
var v0 = vl0.xy / (pc.angular_radius.sin * vl0.z);
var v1 = vl1.xy / (pc.angular_radius.sin * vl1.z);
var v2 = vl2.xy / (pc.angular_radius.sin * vl2.z);
v0 = (rotmat * v0 + 1.0) * 0.5;
v1 = (rotmat * v1 + 1.0) * 0.5;
v2 = (rotmat * v2 + 1.0) * 0.5;
// Determine line equation for each edge of the triangle. `s` is the slope of the edge,
// but run over rise instead of the typical rise over run. `b` is the base, i.e. the x value
// for y = 0. Further down, after multiplying by 16, we want to round this value towards the
// nearest integer. To make that easier, we already add 0.5 * (1 / 16) now. Finally,
// `xor_mask` determines whether the inside of the triangle is on the left or right of the
// edge. It's either 0 or all 1s.
//
// Note that special vertical edge case (v0.y == v1.y) is not handled here. It probably
// should, but I haven't found any artifacts resulting from this and since speed is
// important here, I didn't handle the special case.
let s0 = (v0.x - v1.x) / (v0.y - v1.y);
let b0 = v0.x - v0.y * s0 + (1.0 / 32.0);
let xor_mask0 = u32(-(i32((v1.y < v0.y) != (vl0.z < 0.0) != (vl1.z < 0.0))));
let s1 = (v1.x - v2.x) / (v1.y - v2.y);
let b1 = v1.x - v1.y * s1 + (1.0 / 32.0);
let xor_mask1 = u32(-(i32((v2.y < v1.y) != (vl2.z < 0.0) != (vl1.z < 0.0))));
let s2 = (v2.x - v0.x) / (v2.y - v0.y);
let b2 = v2.x - v2.y * s2 + (1.0 / 32.0);
let xor_mask2 = u32(-(i32((v0.y < v2.y) != (vl0.z < 0.0) != (vl2.z < 0.0))));
// Two grid lines (one `u32`) are handled at the same time.
for (var dl = 0u; dl < 8u; dl += 1u) {
// The y value for the two grid lines.
let upper_y = (1.0 / 32.0) + (f32(2u * dl) * (1.0 / 16.0));
let lower_y = (1.0 / 32.0) + (f32(2u * dl + 1u) * (1.0 / 16.0));
// Determine a value for each edge separately, that is 0 bits for the triangle's inside,
// and 1s outside. The `b0 + y * s0` part calculates the x coordinate for where the edge
// intersects the grid line. We use that to shift 16 1s to the right, meaning that
// left of the edge are all 0s and right of it are all 1s. Finally, we use the xor_mask
// to flip this in case the triangles insides are right of the edge instead.
//
// This is of course also done for both grid lines, with the one grid line using the
// upper 16 bits of an u32, and the other using the lower 16 bits.
let upper0 = xor_mask0 ^ (0xFFFF0000u >> u32(saturate(b0 + upper_y * s0) * 16.0));
let lower0 = xor_mask0 ^ (0xFFFFu >> u32(saturate(b0 + lower_y * s0) * 16.0));
let upper1 = xor_mask1 ^ (0xFFFF0000u >> u32(saturate(b1 + upper_y * s1) * 16.0));
let lower1 = xor_mask1 ^ (0xFFFFu >> u32(saturate(b1 + lower_y * s1) * 16.0));
let upper2 = xor_mask2 ^ (0xFFFF0000u >> u32(saturate(b2 + upper_y * s2) * 16.0));
let lower2 = xor_mask2 ^ (0xFFFFu >> u32(saturate(b2 + lower_y * s2) * 16.0));
// For each grid line, bitwise-or together all edge values which results in 0s only
// inside of the triangle, and 1s outside. Another bitwise-or with a constant to avoid
// polluting the other grid line's bits. The result is then bitwise-and'ed onto the
// existing grid, meaning that we potentially clear more bits if this triangle occluded
// previously non-occluded grid pixels.
grid[dl] &= ((upper0 | upper1 | upper2) | 0xFFFFu) & (lower0 | lower1 | lower2 | 0xFFFF0000u);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment