Topics2 min read

Physically Based Rendering (PBR)

Build metallic and dielectric materials with Cook-Torrance lighting, Fresnel, and the bundled PBR module.

Reading Time
2 min
Word Count
245
Sections
5
Try It Live

Remix the technique

Use the playground as a lab: swap constants, trim passes, and see which parts of the effect are structural versus just polish.

Open Technique Lab

Physically Based Rendering models how light interacts with a surface instead of faking a highlight shape per material. In BWSL that usually means a Cook-Torrance specular term, energy-conserving diffuse, and a small material parameter set: albedo, roughness, metallic, and ao.

The live demos below keep the math inline so you can tweak it directly. In production shaders, the same workflow maps cleanly to the bundled PBR module.

Metallic and Roughness

This sphere sweeps roughness from left to right and metallic from bottom to top so you can see how the specular lobe tightens and the base color moves from diffuse to reflective.

Fresnel and Edge Response

At grazing angles the specular term rises sharply. That edge lift is what makes polished materials feel solid instead of flat.

Material Split

The same lighting model can describe matte paint, brushed metal, or a polished conductor just by changing the material inputs.

Using the Bundled Module

The docs site ships a PBR module with the common BRDF helpers already implemented. In a normal pipeline you can import it and hand it a material struct plus your world-space normal, view direction, and light direction:

bwsl
pipeline PBRSurface {
import PBR
attributes {
position: float3
normal: float3
texcoord: float2
}
pass "Main" {
use attributes { position, normal, texcoord }
vertex {
float4 worldPos = resources.modelMatrix * float4(attributes.position, 1.0);
output.position = resources.projectionMatrix * resources.viewMatrix * worldPos;
output.worldPos = worldPos.xyz;
output.normal = normalize(resources.normalMatrix * attributes.normal);
output.uv = attributes.texcoord;
}
fragment {
PBR::PBRMaterial mat;
mat.albedo = float3(0.92, 0.64, 0.22);
mat.roughness = 0.28;
mat.metallic = 0.9;
mat.ao = 1.0;
float3 N = normalize(input.normal);
float3 V = normalize(resources.cameraPosition - input.worldPos);
float3 L = normalize(resources.lightPosition - input.worldPos);
float3 radiance = resources.lightColor * 2.0;
float3 color = PBR::calculateDirectLighting(mat, N, V, L, radiance);
color = color + mat.albedo * 0.04;
output.color = float4(color, 1.0);
}
}
}

Keep Inputs in World Space

The BRDF only behaves predictably when N, V, and L are all normalized in the same space. World space is usually the easiest choice for material docs and debugging.

See Also

  • Modules - Importing and organizing reusable shader code
  • dot - Core lighting term for N·L, N·V, and V·H
  • reflect - Useful for mirror-like materials and environment work