It’s time to bring depth and atmosphere into our scene through Dynamic Lighting. In a typical rendering pipeline, lighting also works closely with shadows.
The light types I am aiming for are:
- Point Light
- Directional Light
- Spot Light
- HDRI Light
Analytical lights (point, directional, and spot) use uniform buffer data. In contrast, HDRI lighting is image-based (IBL) and requires a cubemap texture (
texture_cube<f32>) and sampler rather than a standard uniform buffer.
Rust Implementation: lighting.rs
In lighting.rs, we create a unified structure to upload the light parameters to the GPU:
-
light_type(identifying which mode to evaluate) direction-
inner_coneandouter_cone(for spot light boundaries) - color, rangle, intensity (basic things about light)
Just like we did for the depth buffer and post-processing passes, we create a GPU buffer to hold the light information, construct a bind group layout and bind group, and instantiate the light controller in the scene.
WGSL Shader Computation: shader.wgsl
Next, we update the fragment shader to compute illumination based on the active light_type:
- Directional Light->> Provides incoming light ray directions (vector L) uniformly everywhere across the scene, independent of position.
- Point Light->>
Computes the direction from the fragment's world position toward the light position (vector L). It uses an inverse-falloff calculation to dim the light over distance, using
clampto avoid infinite brightness when objects get too close. - Spot Light (Cone Light)->> Calculates the dot product between the light vector vector L and the negative spot direction to get the cosine of the angle. This angle is then compared against the cosine of the inner and outer cone limits to smoothly attenuate the beam.
Diffuse Shading & Material Interaction
To calculate diffuse shading, we compute the dot product between the surface normal vector and the light direction vector, clamping negative values to zero.
Finally, lighting interacts directly with materials. When returning the surface color, we combine all terms together by multiplying:
Final Color = Base Color * Light Color * Light Intensity * Diffuse * Attenuation
Image-Based Lighting (HDRI) and PBR Integration
An HDR image is loaded as an environment texture and mapped using spherical coordinates to project across the scene. However, real-time renderers do not use random sampling at runtime to compute the light. Random sampling is meant for offline path-tracers and causes heavy frame lag and noise.
Instead you prefilter once into:
- A diffuse irradiance cubemap that supplies smooth ambient light from all directions.
- A pre-filtered specular cubemap that stores reflections blurred across different roughness levels.
HDRI requires a Physically Based Rendering (PBR) workflow to look correct. Without physical material properties like roughness, metallic values, and viewing-angle Fresnel reflection, an HDR image merely acts as a flat ambient tint:
- Roughness determines which blurred reflection level is sampled.
- Fresnel calculates how reflective the surface becomes at grazing angles.
- Metallic controls whether the surface reflects dielectric colors or tinted metallic highlights.
Floating-Point Texture Pipeline Setup
Standard textures clip brightness values at pure white (1.0). Because HDR images store illumination values far higher than 1.0, precision must be maintained throughout the pipeline:
The HDR texture is uploaded using a high-precision 16-bit floating-point format (Rgba16Float).
The offscreen scene texture and the 3D scene pipeline color target format are also set to Rgba16Float instead of the default window surface format. This prevents bright HDR highlights from clipping during the lighting pass.
The post-processing pass reads this 16-bit floating-point scene texture, applies tone mapping to compress the high range down to displayable limits, and outputs the final color to the standard screen format.
Next PBR implementation:
- F0 = mix(vec3(0.04), base_color.rgb, metallic)
- Fresnel (Schlick)
- GGX normal distribution
- Smith geometry
Because covering the full pipeline takes significant space, we will wrap up analytical lighting and basic PBR here, and continue with Image-Based Lighting (IBL) environment integration in the next part!

Load the HDRI+PBR texture working together


Top comments (0)