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).

loading core…

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

ItemWhat it exercises
edge functionsEach 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 weightsThose 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 shadingGive 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-exactPositions 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.