Mar 22, 2025
Learning graphics by writing the renderer yourself
In modern engines like Unity or Unreal, rendering a scene is practically effortless: import a model, place a camera, add a light, and press play. While that convenience is essential for shipping games, it hides the exact layer I wanted to understand—how a stream of vertices and mathematical matrices actually becomes pixels on a screen.
To demystify that pipeline, I went the low-level route: first building an OpenGL flight simulator in C++ during university, and later constructing a raw Vulkan renderer from the ground up.
OpenGL and procedural terrain in university
My first deep dive into real-time rendering was for a Computer Graphics course at Dalhousie University. We were tasked with building a 3D application from scratch in C++ using raw OpenGL and FreeGLUT—no engine scaffolding, just an API context, math, and a window.
I built a flight simulator. Rather than writing a full aerodynamic physics engine, the movement was straightforward: a constant forward velocity coupled with vertical and lateral velocity driven by keyboard inputs. To make the aircraft feel alive, I animated its roll, pitch, and yaw in response to turns, along with the physical control surfaces (the ailerons, elevators, and rudder) on the plane model.
The core technical challenge was implementing the graphics fundamentals:
- Rendering 3D polygons directly from vertex lists and configuring vertex buffer objects.
- Applying texture coordinates and configuring lighting models to give depth to the scene.
- Writing a custom random noise algorithm from scratch to procedurally generate undulating terrain beneath the plane.
It took weeks of manual debugging, calculating transformation matrices, and wrestling with OpenGL boilerplate. Looking back now, it is amusing to realize that much of that initial scaffolding could probably be generated in an afternoon with modern AI developer tools. But doing it manually by hand forced me to understand the camera, view-projection math, and rasterization pipeline from the inside out.
Stepping down to the metal with Vulkan
More than a year after finishing the flight sim, I wanted to understand where modern real-time graphics had evolved. Vulkan is the industry successor to OpenGL, designed to eliminate driver overhead and give developers explicit control over the GPU.
To tackle the steep learning curve, I followed Brendan Galea’s excellent Vulkan C++ tutorial series on YouTube, building an engine architecture step by step using GLFW for window management and GLM for vector and matrix math.
If OpenGL is a forgiving state machine that does heavy lifting behind your back, Vulkan is uncompromising. You manage everything explicitly:
- Memory allocation and buffer staging.
- Command buffers, render passes, and framebuffers.
- Pipeline layouts, descriptor sets, and synchronization barriers.
In Vulkan, writing and binding SPIR-V shaders is not a background detail—it is central to configuring the immutable graphics pipeline. Every line of code that felt tedious at first was actually revealing work that OpenGL and graphics drivers had silently handled for years. Vulkan simply puts you in direct control of the hardware queue.
Why low-level graphics matters for high-level engines
I don’t write raw Vulkan when I want to build a quick game prototype; engines like Unity, Godot, or Unreal exist for good reason. But having built custom renderers changes how you work with high-level tools:
- Demystifying the engine: You understand what draw calls, batching, depth testing, and material passes are doing under the hood.
- Better performance intuition: Optimization stops being guesswork because you know where GPU synchronization stalls, memory bandwidth bottlenecks, and shader overhead actually live.
- Architecture clarity: Concepts in modern game engines stop feeling like arbitrary black boxes and start feeling like logical abstractions over real hardware.
The flight sim was never meant to be a commercial flight game, and the Vulkan renderer wasn’t built to compete with commercial engines. Their value was peeling back the curtain on the GPU—and that mental model has stayed with me ever since.