Back home 🏡 WGSL Spec Bevy WGSL Functions

pbr_lighting.wgsl

Import path

Constants

Name Type Value
LAYER_BASE # u32 0
LAYER_CLEARCOAT # u32 1

Structures

LayerLightingInput

#
Input to a lighting function for a single layer (either the base layer or the clearcoat layer).
Name Type
N vec3<f32>
R vec3<f32>
NdotV f32
perceptual_roughness f32
roughness f32

LightingInput

#
Input to a lighting function (point_light`, `spot_light, directional_light).
Name Type Shader Def
layers array<LayerLightingInput, 2> 🟢 STANDARD_MATERIAL_CLEARCOAT
layers array<LayerLightingInput, 1> 🟢 STANDARD_MATERIAL_CLEARCOAT
P vec3<f32>
V vec3<f32>
diffuse_color vec3<f32>
F0_ vec3<f32>
F_ab vec2<f32>
clearcoat_strength f32 🟢 STANDARD_MATERIAL_CLEARCOAT
anisotropy f32 🟢 STANDARD_MATERIAL_ANISOTROPY
Ta vec3<f32> 🟢 STANDARD_MATERIAL_ANISOTROPY
Ba vec3<f32> 🟢 STANDARD_MATERIAL_ANISOTROPY

DerivedLightingInput

#
Values derived from the LightingInput for both diffuse and specular lights.
Name Type
H vec3<f32>
NdotL f32
NdotH f32
LdotH f32

Functions

getDistanceAttenuation

#
distanceAttenuation is simply the square falloff of light intensity combined with a smooth attenuation at the edge of the light radius light radius is a non-physical construct for efficiency purposes, because otherwise every light affects every fragment in the scene
fn getDistanceAttenuation(distanceSquare: f32, inverseRangeSquared: f32) -> f32
Parameter Type
distanceSquare f32
inverseRangeSquared f32
Returns: f32

D_GGX

#
Simple implementation, has precision problems when using fp16 instead of fp32 see https://google.github.io/filament/Filament.html#listing_speculardfp16
fn D_GGX(roughness: f32, NdotH: f32, h: vec3<f32>) -> f32
Parameter Type
roughness f32
NdotH f32
h vec3<f32>
Returns: f32

D_GGX_anisotropic

#
An approximation of the anisotropic GGX distribution function. 1 D(𝐡) = ─────────────────────────────────────────────────── παₜα_b((𝐡 ⋅ 𝐭)² / αₜ²) + (𝐡 ⋅ 𝐛)² / α_b² + (𝐡 ⋅ 𝐧)²)² * T = 𝐭 = the tangent direction = the direction of increased roughness. * B = 𝐛 = the bitangent direction = the direction of decreased roughness. * at = αₜ = the alpha-roughness in the tangent direction. * ab = α_b = the alpha-roughness in the bitangent direction. This is from the KHR_materials_anisotropy spec: <https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md#individual-lights>
fn D_GGX_anisotropic(at: f32, ab: f32, NdotH: f32, TdotH: f32, BdotH: f32) -> f32
Parameter Type
at f32
ab f32
NdotH f32
TdotH f32
BdotH f32
Returns: f32

V_SmithGGXCorrelated

#
Visibility function (Specular G) V(v,l,a) = G(v,l,α) / { 4 (n⋅v) (n⋅l) } such that f_r becomes f_r(v,l) = D(h,α) V(v,l,α) F(v,h,f0) where V(v,l,α) = 0.5 / { n⋅l sqrt((n⋅v)^2 (1−α2) + α2) + n⋅v sqrt((n⋅l)^2 (1−α2) + α2) } Note the two sqrt's, that may be slow on mobile, see https://google.github.io/filament/Filament.html#listing_approximatedspecularv
fn V_SmithGGXCorrelated(roughness: f32, NdotV: f32, NdotL: f32) -> f32
Parameter Type
roughness f32
NdotV f32
NdotL f32
Returns: f32

V_GGX_anisotropic

#
The visibility function, anisotropic variant.
fn V_GGX_anisotropic(at: f32, ab: f32, NdotL: f32, NdotV: f32, BdotV: f32, TdotV: f32, TdotL: f32, BdotL: f32) -> f32
Parameter Type
at f32
ab f32
NdotL f32
NdotV f32
BdotV f32
TdotV f32
TdotL f32
BdotL f32
Returns: f32

V_Kelemen

#
A simpler, but nonphysical, alternative to Smith-GGX. We use this for clearcoat, per the Filament spec. https://google.github.io/filament/Filament.html#materialsystem/clearcoatmodel#toc4.9.1
fn V_Kelemen(LdotH: f32) -> f32
Parameter Type
LdotH f32
Returns: f32

F_Schlick_vec

#
Fresnel function see https://google.github.io/filament/Filament.html#citation-schlick94 F_Schlick(v,h,f_0,f_90) = f_0 + (f_90 − f_0) (1 − v⋅h)^5
fn F_Schlick_vec(f0: vec3<f32>, f90: f32, VdotH: f32) -> vec3<f32>
Parameter Type
f0 vec3<f32>
f90 f32
VdotH f32
Returns: vec3<f32>

F_Schlick

#
fn F_Schlick(f0: f32, f90: f32, VdotH: f32) -> f32
Parameter Type
f0 f32
f90 f32
VdotH f32
Returns: f32

fresnel

#
fn fresnel(f0: vec3<f32>, LdotH: f32) -> vec3<f32>
Parameter Type
f0 vec3<f32>
LdotH f32
Returns: vec3<f32>

specular_multiscatter

#
Given distribution, visibility, and Fresnel term, calculates the final specular light. Multiscattering approximation: <https://google.github.io/filament/Filament.html#listing_energycompensationimpl>
fn specular_multiscatter(input: ptr<function, LightingInput>, D: f32, V: f32, F: vec3<f32>, specular_intensity: f32) -> vec3<f32>
Parameter Type
input ptr<function, LightingInput>
D f32
V f32
F vec3<f32>
specular_intensity f32
Returns: vec3<f32>

derive_lighting_input

#
N, V, and L must all be normalized.
fn derive_lighting_input(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> DerivedLightingInput
Parameter Type
N vec3<f32>
V vec3<f32>
L vec3<f32>
Returns: DerivedLightingInput

compute_specular_layer_values_for_point_light

#
Returns L in the xyz` components and the specular intensity in the `w component.
fn compute_specular_layer_values_for_point_light(input: ptr<function, LightingInput>, layer: u32, V: vec3<f32>, light_to_frag: vec3<f32>, light_position_radius: f32) -> vec4<f32>
Parameter Type
input ptr<function, LightingInput>
layer u32
V vec3<f32>
light_to_frag vec3<f32>
light_position_radius f32
Returns: vec4<f32>

specular

#
Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) }
fn specular(input: ptr<function, LightingInput>, derived_input: ptr<function, DerivedLightingInput>, specular_intensity: f32) -> vec3<f32>
Parameter Type
input ptr<function, LightingInput>
derived_input ptr<function, DerivedLightingInput>
specular_intensity f32
Returns: vec3<f32>

specular_clearcoat

#
Calculates the specular light for the clearcoat layer. Returns Fc, the Fresnel term, in the first channel, and Frc, the specular clearcoat light, in the second channel. <https://google.github.io/filament/Filament.html#listing_clearcoatbrdf>
fn specular_clearcoat(input: ptr<function, LightingInput>, derived_input: ptr<function, DerivedLightingInput>, clearcoat_strength: f32, specular_intensity: f32) -> vec2<f32>
Parameter Type
input ptr<function, LightingInput>
derived_input ptr<function, DerivedLightingInput>
clearcoat_strength f32
specular_intensity f32
Returns: vec2<f32>

specular_anisotropy

#

Shader defs:

🟢 STANDARD_MATERIAL_ANISOTROPY

fn specular_anisotropy(input: ptr<function, LightingInput>, derived_input: ptr<function, DerivedLightingInput>, L: vec3<f32>, specular_intensity: f32) -> vec3<f32>
Parameter Type
input ptr<function, LightingInput>
derived_input ptr<function, DerivedLightingInput>
L vec3<f32>
specular_intensity f32
Returns: vec3<f32>

Fd_Burley

#
Diffuse BRDF https://google.github.io/filament/Filament.html#materialsystem/diffusebrdf fd(v,l) = σ/π * 1 / { |n⋅v||n⋅l| } ∫Ω D(m,α) G(v,l,m) (v⋅m) (l⋅m) dm simplest approximation float Fd_Lambert() { return 1.0 / PI; } vec3 Fd = diffuseColor * Fd_Lambert(); Disney approximation See https://google.github.io/filament/Filament.html#citation-burley12 minimal quality difference
fn Fd_Burley(input: ptr<function, LightingInput>, derived_input: ptr<function, DerivedLightingInput>) -> f32
Parameter Type
input ptr<function, LightingInput>
derived_input ptr<function, DerivedLightingInput>
Returns: f32

F_AB

#
Scale/bias approximation https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile TODO: Use a LUT (more accurate)
fn F_AB(perceptual_roughness: f32, NdotV: f32) -> vec2<f32>
Parameter Type
perceptual_roughness f32
NdotV f32
Returns: vec2<f32>

EnvBRDFApprox

#
fn EnvBRDFApprox(F0: vec3<f32>, F_ab: vec2<f32>) -> vec3<f32>
Parameter Type
F0 vec3<f32>
F_ab vec2<f32>
Returns: vec3<f32>

perceptualRoughnessToRoughness

#
fn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32
Parameter Type
perceptualRoughness f32
Returns: f32

point_light

#
fn point_light(light_id: u32, input: ptr<function, LightingInput>) -> vec3<f32>
Parameter Type
light_id u32
input ptr<function, LightingInput>
Returns: vec3<f32>

spot_light

#
fn spot_light(light_id: u32, input: ptr<function, LightingInput>) -> vec3<f32>
Parameter Type
light_id u32
input ptr<function, LightingInput>
Returns: vec3<f32>

directional_light

#
fn directional_light(light_id: u32, input: ptr<function, LightingInput>) -> vec3<f32>
Parameter Type
light_id u32
input ptr<function, LightingInput>
Returns: vec3<f32>