Gouraud Triangle
Rendering & Graphics
Every filled, shaded triangle in 3-D graphics comes down to one question asked per pixel: am I
inside, and if so, how much of each corner do I get? The answer is the
barycentric edge-function rasteriser — three signed
cross products that decide inside/outside and weight the shading. Here it tumbles a
Gouraud-shaded triangle across a Super Nintendo, all in integer fixed point so it's bit-exact. Runs
on its own; no joypad needed. Written in C and compiled with the
llvm-mos-based 65816 toolchain
(+mos-a16, 16-bit accumulator mode).
Self-running demo — the shaded triangle tumbles.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
For every pixel in the triangle's bounding box, three edge functions are evaluated. If all three share the triangle's winding sign, the pixel is inside — and those three values, normalised by the area, are exactly how much each corner contributes:
E(A,B,P) = (Bx-Ax)(Py-Ay) - (By-Ay)(Px-Ax); // signed edge fn inside = E0>=0 && E1>=0 && E2>=0; // all three agree I = (E0*a0 + E1*a1 + E2*a2) / area; // barycentric shade
Give the corners three brightnesses and that per-pixel blend is Gouraud shading — a smooth gradient with no seams. It's all integer cross products and one divide, identical across every codegen mode.
Compiler stress-test
| Item | What it exercises |
|---|---|
| edge functions | Each triangle edge defines a signed cross product: positive on one side, negative on the other. A pixel is inside the triangle exactly when all three edge functions agree in sign — no per-scanline clipping, just three multiply-subtracts per pixel. |
| barycentric weights | Those same three edge values are the barycentric weights of the pixel. Divide them by the triangle area and you have how much each corner contributes — the exact blend used to interpolate colour (or depth, or texture) across the face. |
| Gouraud shading | Give the three corners three brightnesses and the interpolated weight paints a smooth gradient across the triangle — classic Gouraud shading, the basis of shaded 3-D long before pixel shaders. |
| integer, bit-exact | Positions are 16-bit, the cross products and the divide are 32-bit — no floats, so the field is identical on the host and the 65816. The build gate rasterises the same triangles and asserts the fold matches to the bit. |
Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg
(and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build
gate's WRAM assert (gate CRC 0xC5E9 — a fold of barycentric-shaded pixels over two
tumble orientations) live in this tab. No far pointers — the same source passes every way: host ==
default == +mos-a16 == +mos-xy16 == bsnes-jg.