|
| 1 | += Forward, Forward+, and Deferred — choosing the right path |
| 2 | + |
| 3 | +Vulkan lets you build many kinds of pipelines. In practice, most real‑time engines gravitate toward one of three shading architectures: Forward, Forward+, or Deferred. |
| 4 | + |
| 5 | +This page explains what each one is, why this sample chooses Forward+, and where the relevant pieces live in the code. |
| 6 | + |
| 7 | +== Forward rendering |
| 8 | + |
| 9 | +Forward draws each object with its lighting in a single pass. It’s the most direct model: bind a material, bind lights (uniforms or textures/SSBOs), draw. It’s easy to reason about and integrates well with transparency and MSAA. |
| 10 | + |
| 11 | +Pros: |
| 12 | + |
| 13 | +* Simple and predictable. |
| 14 | +* Good with transparent objects and MSAA. |
| 15 | +* Great for small light counts or baked lighting. |
| 16 | + |
| 17 | +Cons: |
| 18 | + |
| 19 | +* Per‑pixel light loops can get expensive as the number of lights grows. |
| 20 | +* You evaluate lights even when most don’t affect the pixel. |
| 21 | + |
| 22 | +== Forward+ (what we use for dynamic lights) |
| 23 | + |
| 24 | +Forward+ partitions the screen into tiles and assigns lights to those tiles with a compute pass. The main pass then shades with only the lights relevant to the pixel’s tile. In this sample we use a lightweight Forward+ that focuses on emissive/simplified lights to keep the code approachable. |
| 25 | + |
| 26 | +Pros: |
| 27 | + |
| 28 | +* Scales to many local lights; you only evaluate lights that might affect the pixel. |
| 29 | +* Keeps forward’s strengths (transparency/MSAA friendliness). |
| 30 | + |
| 31 | +Cons: |
| 32 | + |
| 33 | +* Requires a pre‑pass or depth info and a compute dispatch to build the tile lists. |
| 34 | +* More moving parts than plain forward. |
| 35 | + |
| 36 | +== Deferred shading (when to consider it) |
| 37 | + |
| 38 | +Deferred writes material properties (G‑Buffer) in the first pass, then lights that buffer in a second pass. That turns lighting cost into “cost per lighted pixel” and tends to excel with many lights, but it makes transparency and MSAA trickier. |
| 39 | + |
| 40 | +Pros: |
| 41 | + |
| 42 | +* Many dynamic lights at high performance. |
| 43 | +* Clear separation of material/write and light/evaluate. |
| 44 | + |
| 45 | +Cons: |
| 46 | + |
| 47 | +* Transparent objects must be handled separately (often with a forward pass). |
| 48 | +* MSAA is more complex; memory bandwidth can be high. |
| 49 | + |
| 50 | +== What the sample uses (and why) |
| 51 | + |
| 52 | +We use Forward+ for small, dynamic lights and a forward material path for everything else. That keeps the code compact while still letting you place many little lights around the scene. Transparency (glass) is shaded in a second forward pass so order and blending are correct. |
| 53 | + |
| 54 | +If your project needs hundreds of shadowed lights and complex post‑lighting, explore a deferred path or a hybrid: deferred for opaque, forward for transparent. |
| 55 | + |
| 56 | +== Implementation highlights in this codebase |
| 57 | + |
| 58 | +* A small compute pass builds per‑tile light lists. |
| 59 | +* Per‑frame SSBOs hold tile headers/light indices; the main PBR pass reads those to loop only relevant lights. |
| 60 | +* Descriptor updates happen at the frame’s safe point so we don’t touch in‑use sets. |
| 61 | + |
| 62 | +== Where to look in the code |
| 63 | + |
| 64 | +* Forward/Forward+ render loop integration: |
| 65 | +** `renderer_rendering.cpp` |
| 66 | +* Pipeline + descriptor layout setup: |
| 67 | +** `renderer_pipelines.cpp` |
| 68 | +* Main PBR shader (reads per-tile light lists when Forward+ is enabled): |
| 69 | +** `shaders/pbr.slang` |
| 70 | + |
| 71 | +NOTE: The tile/cluster build shader is wired in `renderer_pipelines.cpp`. Start there and follow which compute pipeline is created for the Forward+ light assignment pass. |
| 72 | + |
| 73 | +== Choosing for your project |
| 74 | + |
| 75 | +Use Forward if: |
| 76 | + |
| 77 | +* Light count is low, transparency/MSAA are priorities, and you want the simplest pipeline. |
| 78 | + |
| 79 | +Use Forward+ if: |
| 80 | + |
| 81 | +* You want many local lights but still want forward’s strengths. |
| 82 | + |
| 83 | +Use Deferred if: |
| 84 | + |
| 85 | +* You need to scale to many dynamic lights with complex lighting, and you’re ready to solve transparency/MSAA separately. |
| 86 | + |
| 87 | +There’s no one answer; pick the simplest that meets your needs. You can always grow the pipeline later. |
| 88 | + |
| 89 | +== Future work ideas |
| 90 | + |
| 91 | +If you want to expand the lighting system beyond “readable sample”: |
| 92 | + |
| 93 | +* Add clustered Forward+ (3D clusters using depth slices) instead of 2D tiles. |
| 94 | +* Add shadows (start with a single directional shadow map, then add point/spot shadows). |
| 95 | +* Add a small deferred path for opaque only (keep transparent as forward). |
| 96 | +* Add ray query helpers for selective effects (reflection rays, shadow rays, or AO probes) without building a full RT pipeline. |
| 97 | + |
| 98 | +== What to read next |
| 99 | + |
| 100 | +* `Rendering_Pipeline_Overview.adoc` |
| 101 | +* `ForwardPlus_Rendering.adoc` |
| 102 | +* `Synchronization_2_Frame_Pacing.adoc` |
0 commit comments