I can't wait to work through this. I agree so much with this philosophy:
"I do not want to show how to write applications for OpenGL. I want to show how OpenGL works. I am deeply convinced that it is impossible to write efficient applications using 3D libraries without understanding this."
This is very impressive, I love it when code has minimal (or no) dependencies, you really see what's going on under the hood. Could have used some more comments in the code itself though (there are basically none but the code is clean). I first thought the code would render into a window using OpenGL. But this is a plain software renderer with no hardware acceleration/OpenGL at all and it simply stores the rendered image into a picture file. There is no window or stuff like that.
EDIT: The lessons in the wiki explain things so well that comments in the code really aren't necessary at all.
There's one piece of information about the OpenGL pipeline I never quite understood:
Years ago when I was playing writing my own operating system, I wanted to add accelerated video drivers, and obviously OpenGL. I now know it's out of the reach of hobby OSes, but I could never figure out what's the relationship with OpenGL and the actual hardware.
Is OpenGL just an API, with the actual API -> hardware command translation done by the video driver? Or does the hardware understand some part of the OpenGL spec? Rephrased, does the OpenGL spec mandate some ABI to talk with the video card?
I seem to think the former's the right answer, as I never managed to find any of the actual OpenGL -> HW command translation source code anywhere. At the time Intel wasn't a player in the video card space, so nVidia and ATI were the only option, but alas, closed source.
The former is the right answer. Now, there are open source options, e.g. Nouveau for Nvidia originally for Linux, but has been ported to FreeBSD.
Gallium should make it easier to do what you wanted as well - providing the glue between a video driver and APIs such as OpenGL / DirectX ( - there is a gallium implementation of DirectX 9 for Wine).
As far as I understand the history of 3D APIs, the very early GL-like APIs (in the IrisGL and Silicon Graphics days), were quite closely modelled to the hardware, e.g. the hardware was a state machine with many individual knobs and gears which were manipulated by the many different OpenGL functions.
Over time GPUs became more and more like universal (but heavily parallelized) CPUs with less and less fixed-function-parts, especially after the introduction of vertex- and pixel-shaders
OpenGL's model matched the underlying hardware less and less, which lead to a lot of software complexity in the driver layer (e.g. changing a single render state would lead to re-building big blocks of associated state). This was when the new APIs like D3D12, Vulkan and Metal were introduced which basically rebootet the complicated driver software layer and are again closer to what the underlying GPU hardware looks like.
Hopefully the evolution continues and in a few years the software translation layer will be gone completely, and the GPUs will be exposed just as an instruction set and memory model, like regular CPUs today.
I love the approach for teaching! I also just recently wrote a reference GL-SW renderer. It's not much code.. 400 loc? But it really drives home the (simple) math. There are some things that are hard to do in SW - derivatives. Also writing SW today is very different than back in the day! Just Lerp everything, no DDA, no "divide only every 8th pixel".