Week 3 — Meshes, Buffers, Lighting, Materials, and Depth
Overview
This week the renderer becomes useful: it draws real 3D meshes with correct transforms, depth, and shading, turning the abstract pipeline of Week 2 into an interpretable scene. You will set up vertex/index/uniform buffers and descriptor sets, load mesh assets, add a depth buffer for correct occlusion, and implement basic lighting and materials so the world reads as 3D. This consolidates what were two separate weeks in the longer version of the course — the buffer/asset plumbing and the shading — because once the descriptor and pipeline machinery is in place, lighting is a shader and a few more uniforms.
By the end you have a lit, depth-correct scene of loaded meshes viewed through a movable camera. That is the canvas on which the road world (Week 4) and everything after is built.
Readings
- FCG: meshes, viewing, lighting/shading models (Lambert, Blinn-Phong), materials, and depth buffering. Extract: the shading equations and why a depth buffer is needed.
- Vulkan (MGPV / tutorials): vertex/index/uniform buffers, descriptor set layouts and sets, push constants, depth attachments, and pipeline depth state. Extract: how data gets from CPU memory to a shader.
- 3DMP (review): the camera and transform pipeline. (The underlying linear algebra: assumed from Course 5.)
Key Concepts
Buffers and descriptors
Vertex and index buffers feed geometry; uniform buffers (and push constants for small, frequently-changing data like the MVP matrix) feed per-frame/per-object parameters. Descriptor sets bind these resources to shader slots. The mental model: descriptors are the contract between CPU-side resources and GPU-side shader inputs, validated at pipeline creation.
Depth buffering
Without depth testing, draw order determines occlusion — wrong for a 3D scene. A depth attachment stores per-pixel depth; the pipeline’s depth test discards fragments behind existing geometry. Note Vulkan’s \([0,1]\) clip-space Z (the GLM_FORCE_DEPTH_ZERO_TO_ONE from Week 1) and the precision implications of the near/far planes (z-fighting from a too-distant far plane).
Lighting and materials
A basic Blinn-Phong model: ambient + diffuse (\(\max(0,\mathbf{n}\cdot\mathbf{l})\)) + specular, with per-material parameters (color, shininess). Normals must be transformed by the inverse-transpose of the model matrix, not the model matrix — a classic bug when non-uniform scaling is present. This is enough to make the road world legible; physically based rendering is explicitly out of scope.
Asset loading
Load meshes with tinyobjloader/Assimp into your vertex/index buffers; load textures with stb_image. Keep an asset abstraction so the road-mesh generator (Week 4) and vehicle meshes plug in uniformly.
Theory Exercises
- Derive the Blinn-Phong shading equation and explain each term’s visual contribution.
- Show why normals transform by the inverse-transpose of the model matrix; give a scaling example where using the model matrix fails.
- Explain z-fighting in terms of depth-buffer precision and the near/far planes; propose a fix.
- Describe the data path from a CPU-side vertex array to a shader input via buffers and descriptors.
- Compute the GPU memory footprint of a mesh with \(V\) vertices and \(F\) triangles given your vertex format.
Implementation
Add vertex/index/uniform buffers, descriptor sets, and push constants. Create a depth attachment and enable depth testing. Implement a Blinn-Phong shader with per-material uniforms. Load a few meshes and render them lit, depth-correct, through a movable camera. Verify normals under non-uniform scale.
Benchmark
Measure: draw time vs object count (scaling), per-frame uniform/descriptor update cost, and GPU memory used. Compare one-draw-per-object vs batching. Capture a frame in RenderDoc to confirm depth and descriptor bindings.
Expected baselines: frame time scales with draw-call count (motivating Week 10 instancing); descriptor/uniform updates are cheap if done right, expensive if per-object allocation creeps in. Depth test produces correct occlusion; normals are correct under scaling.
Connections
This lit, depth-correct renderer is the canvas for the road world (Week 4), traffic and vehicles (Weeks 5–6), and the camera sensor (Week 7, which renders this scene to an offscreen target). The draw-call scaling observed here is the problem Week 10’s instancing/culling solves. Course 5’s linear algebra underlies every transform used.
Further Reading
- FCG chapters on shading and viewing.
- vkguide.dev — buffers, descriptors, and depth.
- Vulkan spec sections on descriptor sets and depth/stencil state.