← Back to Graphics

Deferred PBR Renderer

Real-time physically based renderer with screen-space reflection

Date February 2025
Tags
c++openglglsl

Overview

This is a real-time physically based renderer built in C++, OpenGL, Qt, and GLSL. The project extends a microfacet PBR image-based lighting renderer with a deferred G-buffer pipeline, screen-space reflection, rough reflection blur, and ACES tone mapping.

Cerberus PBR material demo

Render Pipeline

  1. Environment setup -> convert an equirectangular HDR map to a cubemap, then precompute diffuse and glossy irradiance cubemaps.
  2. Geometry pass -> G-buffer storing world position, normal, albedo, metallic/roughness/mask, and the environment-lit PBR result.
  3. SSR pass -> ray march reflected directions against the G-buffer in screen space and store reflected scene color.
  4. Reflection blur pass -> repeatedly apply a Gaussian blur to the SSR buffer to create rougher reflection levels.
  5. Composite pass -> mix environment IBL and SSR terms based on reflection visibility and roughness, then apply ACES tone mapping and gamma correction.

Screen Space Reflection

Screen-space reflection demo

I implemented SSR by marching in pixel space instead of taking uniform world-space steps. For each shaded pixel, the shader reads world position and normal from the G-buffer, reflects the view ray, projects the start and end of that reflected ray into screen coordinates, and steps one pixel at a time along the dominant screen axis.

At each step, it reconstructs the marched ray depth with perspective-correct interpolation and compares it against the scene depth derived from the G-buffer’s world-position texture. When the marched depth crosses behind a visible surface within a small tolerance, the shader treats it as a hit and samples the precomputed PBR color at that hit UV. Misses return transparent reflection data, so the final composite can fall back to the environment map.

Diffuse Convolution

Sphere Diffuse Convolution
Diffuse environment lighting is precomputed into a low-resolution irradiance cubemap. Each cubemap texel represents a surface normal direction. For that normal, the shader builds a local tangent frame and numerically integrates the HDR environment over the upper hemisphere.

The integration samples spherical directions around the normal and accumulates:

environmentRadiance(sampleDirection) * cos(theta) * sin(theta)

cos(theta) is the Lambertian cosine term, and sin(theta) is the spherical-coordinate area term. After averaging the samples and multiplying by pi, the result is stored in the diffuse irradiance cubemap. During PBR shading, the renderer samples this cubemap by surface normal and multiplies it by albedo and the diffuse energy term kD.

Glossy Convolution

Sphere Diffuse Convolution
Glossy convolution prefilters the same HDR environment map for rough specular reflection. Instead of one irradiance value per normal, the renderer stores a mipmapped glossy cubemap where each mip level corresponds to a roughness value from 0 to 1.

For each cubemap texel and roughness level, the shader assumes N = V = R, then takes 4096 GGX importance samples using a Hammersley sequence. Each sample generates a halfway vector, reflects the view direction around it to get an incoming light direction, and weights the sampled environment radiance by NdotL.

To reduce fireflies, the shader also estimates each sample’s solid angle from the GGX PDF and chooses an appropriate source environment mip level with textureLod. The weighted average becomes the prefiltered glossy color for that cubemap texel and roughness level.

At shading time, the renderer samples this glossy cubemap with:

textureLod(glossyIrradianceMap, reflectionDirection, roughness * maxMipLevel)

Then it combines the result with a 2D BRDF lookup texture for the split-sum approximation. This is separate from the screen-space reflection blur pass: glossy convolution handles reflections of the distant environment, while the Gaussian-blurred SSR buffers approximate rough reflections of visible scene geometry.

Environment Lighting Comparison

Environment 1Environment 2
Cerberus model under environment lighting 1Cerberus model under environment lighting 2

Metallic And Roughness Sweep

Metallic 0.0 / Roughness 0.0Metallic 0.0 / Roughness 0.5Metallic 0.0 / Roughness 1.0
Metallic 0.0 roughness 0.0Metallic 0.0 roughness 0.5Metallic 0.0 roughness 1.0
Metallic 0.5 / Roughness 0.0Metallic 1.0 / Roughness 0.0Metallic 1.0 / Roughness 0.25
Metallic 0.5 roughness 0.0Metallic 1.0 roughness 0.0Metallic 1.0 roughness 0.25