Deferred Shading & Screen-Space Effects

Pros: many lights cheaply, decoupled geometry/lighting.

29.1 Deferred shading

Geometry pass: render all objects once into a G-buffer (multiple render targets): position/depth, normal, albedo, material parameters.

Lighting pass: for each pixel, read the G-buffer and accumulate all lights (fullscreen pass, light volumes, or tiled/clustered).

Pros: many lights cheaply, decoupled geometry/lighting. Cons: memory bandwidth, transparency and MSAA are harder, limited material variety. Alternatives: Forward+ / clustered forward (light lists per screen tile/cluster built by a compute shader), visibility buffer rendering.

// Geometry pass fragment shader
layout (location = 0) out vec4 gAlbedoMetal;     // rgb albedo, a metallic
layout (location = 1) out vec4 gNormalRough;     // xyz normal (or octahedral-encoded), w roughness
layout (location = 2) out vec4 gEmissive;
void main() {
    gAlbedoMetal = vec4(texture(uAlbedo, vUV).rgb, metallic);
    gNormalRough = vec4(normalize(vTBN * sampledNormal) * 0.5 + 0.5, roughness);
    gEmissive = vec4(emissive, 1.0);
}
// Lighting pass: reconstruct world position from depth + inverse view-projection
vec3 worldFromDepth(vec2 uv, float depth) {
    vec4 ndc = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
    vec4 world = uInvViewProj * ndc;
    return world.xyz / world.w;
}

29.2 Screen-space ambient occlusion (SSAO)

Modern variants: HBAO+, GTAO (ground-truth AO).

uniform sampler2D gDepth, gNormal, uNoise;
uniform vec3 uSamples[64];                  // hemisphere kernel, denser near the origin
uniform mat4 uProjection;
float ssao(vec3 viewPos, vec3 viewN, vec2 uv) {
    vec3 randomVec = texture(uNoise, uv * uNoiseScale).xyz;
    vec3 T = normalize(randomVec - viewN * dot(randomVec, viewN));
    mat3 TBN = mat3(T, cross(viewN, T), viewN);
    float occlusion = 0.0;
    for (int i = 0; i < 64; ++i) {
        vec3 samplePos = viewPos + TBN * uSamples[i] * uRadius;
        vec4 offset = uProjection * vec4(samplePos, 1.0);
        offset.xy = (offset.xy / offset.w) * 0.5 + 0.5;
        float sceneZ = viewSpaceZFromDepth(texture(gDepth, offset.xy).r);
        float rangeCheck = smoothstep(0.0, 1.0, uRadius / abs(viewPos.z - sceneZ));
        occlusion += (sceneZ >= samplePos.z + 0.025 ? 1.0 : 0.0) * rangeCheck;
    }
    return 1.0 - occlusion / 64.0;           // blur afterwards to remove noise
}

29.3 Other screen-space techniques

SSR (screen-space reflections): ray march the depth buffer along the reflection vector (hierarchical Z for speed).

Screen-space shadows / contact shadows.

Temporal anti-aliasing (TAA): jitter the projection each frame, reproject the previous frame with motion vectors, clamp history to neighborhood colors.

Motion blur & depth of field using velocity and depth buffers.

Decals (deferred decals project textures onto the G-buffer).