Skip to content

Lighting

The lighting category bundles the small, pure WGSL building blocks that shading models are made of — diffuse terms (Lambert, half-Lambert, Oren-Nayar, Disney/Burley), specular lobes (Phong, Blinn-Phong, Cook-Torrance), the microfacet primitives that feed PBR pipelines (GGX normal distribution, Smith masking-shadowing, Schlick Fresnel and its roughness-aware variant), plus a couple of rim and falloff helpers (rim, inverse-square / range attenuation).

Each function takes the bare vectors and parameters it needs (n, l, v, roughness, f0, …) and returns just its slice of the lighting equation, so you can mix and match: a Cook-Torrance specular alongside a Disney diffuse, a rim term layered over Lambert, a point light driven by a range-clamped attenuation. Pull the slug into a composition and call the function from your fragment stage:

let main = r#"
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
// ... compute n, l, v from your geometry / G-buffer ...
let diffuse = albedo * lambert(n, l);
let spec = vec3<f32>(1.0) * blinn_phong(n, l, v, 64.0);
return vec4<f32>(diffuse + spec, 1.0);
}
"#;
let shader = Shader::new(&["lighting/lambert", "lighting/blinn_phong", main])?;

Each preview below shades a flat sphere lit from the upper-right with a single directional or point light, so the shape of each term is visible in isolation.

Physically-based 1 / d² falloff with a min_d guard against the singularity at the light position.

fn attenuation_inverse_square(distance: f32, min_d: f32) -> f32

lighting/attenuation_inverse_square preview

Inverse-square falloff clamped to a finite range (Unreal-style window function). Smoothly fades to zero at the cutoff.

fn attenuation_range(distance: f32, range: f32) -> f32

lighting/attenuation_range preview

Blinn half-vector specular term. Cheaper and usually nicer-looking than classic Phong; shine is the specular exponent.

fn blinn_phong(n: vec3<f32>, l: vec3<f32>, v: vec3<f32>, shine: f32) -> f32

lighting/blinn_phong preview

Microfacet specular BRDF: D · G · F / (4 · NdV · NdL) with inlined GGX distribution, Smith geometry, and Schlick Fresnel. Pass surface f0 (reflectance at normal incidence) and roughness in [0, 1].

fn cook_torrance(n: vec3<f32>, l: vec3<f32>, v: vec3<f32>, f0: vec3<f32>, roughness: f32) -> vec3<f32>

lighting/cook_torrance preview

Burley’s Disney diffuse term — energy-preserving rough-surface diffuse with a slightly brighter Fresnel-aware grazing edge.

fn disney_diffuse(n: vec3<f32>, l: vec3<f32>, v: vec3<f32>, roughness: f32) -> f32

lighting/disney_diffuse preview

Schlick’s approximation to the Fresnel reflectance: f0 + (1 - f0) · (1 - cosθ)⁵. Use cosθ = max(dot(n, v), 0) for direct lighting.

fn fresnel_schlick(cos_theta: f32, f0: vec3<f32>) -> vec3<f32>

lighting/fresnel_schlick preview

Lagarde’s roughness-aware Schlick variant. Used with pre-integrated environment maps so rough surfaces don’t get an unrealistic grazing-angle spike.

fn fresnel_schlick_roughness(cos_theta: f32, f0: vec3<f32>, roughness: f32) -> vec3<f32>

lighting/fresnel_schlick_roughness preview

GGX / Trowbridge-Reitz normal distribution function. Returns the microfacet density at the given half-vector.

fn ggx_d(n: vec3<f32>, h: vec3<f32>, roughness: f32) -> f32

lighting/ggx_d preview

Valve-style wrapped diffuse: ((n·l) · 0.5 + 0.5)². Softer terminator, useful for stylized or NPR shading.

fn half_lambert(n: vec3<f32>, l: vec3<f32>) -> f32

lighting/half_lambert preview

Classic Lambertian diffuse: max(n · l, 0). Pass unnormalized vectors; they are normalized inside.

fn lambert(n: vec3<f32>, l: vec3<f32>) -> f32

lighting/lambert preview

Oren-Nayar diffuse for rough surfaces. Reduces the retroreflective bleed that pure Lambert produces and gives a flatter, dustier look.

fn oren_nayar(n: vec3<f32>, l: vec3<f32>, v: vec3<f32>, roughness: f32) -> f32

lighting/oren_nayar preview

Classic Phong specular: reflect the light vector around the normal and take max(r · v, 0) to the shine power.

fn phong(n: vec3<f32>, l: vec3<f32>, v: vec3<f32>, shine: f32) -> f32

lighting/phong preview

Rim / back-light term proportional to (1 - n · v)^power. Layer over a diffuse base for an edge glow.

fn rim(n: vec3<f32>, v: vec3<f32>, power: f32) -> f32

lighting/rim preview

Smith masking-and-shadowing geometry term, with Schlick-GGX G1 applied in both the view and light directions and combined as a product.

fn smith_g(n: vec3<f32>, v: vec3<f32>, l: vec3<f32>, roughness: f32) -> f32

lighting/smith_g preview