pbr
The engine's standard physically-based material — the full glTF metallic-roughness workflow lit by the shared `@builtin::shaderModules.pbr_shading` model. This is the default surface shader a model uses when its material doesn't specify another, and it is also registered under th…
pbr
The engine's standard physically-based material — the full glTF metallic-roughness
workflow lit by the shared @builtin::shaderModules.pbr_shading model. This is the default surface shader a
model uses when its material doesn't specify another, and it is also registered
under the legacy alias standard (the name many materials reference).
It is a surface() shader: it fills a PbrSurface and the engine generates every
pipeline entry point and lights it. Every parameter defaults to a neutral value, so
a material that sets nothing renders as a clean dielectric; the advanced lobes only
cost anything when a material opts into them.
Properties
| Property | Type | Default | Meaning |
|---|---|---|---|
base_color | color | [1,1,1,1] | Albedo tint, multiplied by base_color_texture and vertex colour. |
emissive | color | [0,0,0,1] | Emissive tint, multiplied by emissive_texture and emissive_intensity. |
emissive_intensity | float | 1.0 | HDR multiplier on the emissive output. |
roughness | range | 0.5 | Perceptual roughness, multiplied by the green channel of roughness_metallic_texture. |
metallic | range | 0.0 | Metalness, multiplied by the blue channel of roughness_metallic_texture. |
reflectance | range | 0.5 | Dielectric reflectance knob (neutral 0.5 → 4% F0). |
ior | float | 1.5 | Index of refraction → dielectric F0 and the refraction direction. |
normal_scale | float | 1.0 | Strength of the tangent-space normal_texture. |
occlusion_strength | range | 1.0 | Blend of the occlusion_texture (R) into the ambient term. |
clearcoat | range | 0.0 | Strength of a second dielectric clearcoat specular lobe. |
clearcoat_roughness | range | 0.0 | Roughness of the clearcoat lobe. |
anisotropy | float | 0.0 | Anisotropic highlight stretch along the tangent ([-1,1]). |
sheen_color | color | [0,0,0,1] | Tint of the sheen lobe — the retroreflective rim of velvet, satin and brushed fabric. Black leaves the base untouched. |
sheen_roughness | range | 0.3 | Width of the sheen lobe. |
iridescence | range | 0.0 | Blend toward the thin-film response, the hue that shifts with view angle. |
iridescence_thickness | float | 400.0 | Film thickness in nanometres — which wavelengths cancel and which reinforce. |
iridescence_ior | float | 1.3 | Index of refraction of the film. |
subsurface_color | color | [0,0,0,1] | Tint of the light that entered the surface, scattered beneath it and left again. Black leaves the base untouched. |
subsurface_radius | float | 0.5 | How far the diffuse falloff wraps past the terminator, taken per channel against subsurface_color. |
fibre_color | color | [0,0,0,1] | Tint of the second of the two highlights a hair or fur strand gives. Black leaves the base untouched. |
fibre_specular | range | 0.35 | Strength of the first strand highlight, in the light's own colour. |
fibre_shift | float | 0.06 | How far apart the two strand highlights sit along the fibre. |
fibre_roughness | range | 0.1 | Width of the first strand highlight. |
transmission | range | 0.0 | Specular transmission — refracts the environment through the surface. |
thickness | float | 0.0 | Volume thickness for Beer–Lambert transmission absorption. |
attenuation_color | color | [1,1,1,1] | Transmission absorption tint. |
attenuation_distance | float | large | Beer–Lambert absorption distance. |
uv_scale | float2 | [1,1] | UV tiling applied before every texture sample. |
uv_offset | float2 | [0,0] | UV offset applied before every texture sample. |
alpha_cutoff | range | 0.0 | Mask threshold — fragments with alpha < this are discarded (0 = no cutoff). |
double_sided | bool | false | Flip the shading normal on back faces. |
receives_fog | bool | true | Whether the screen-space fog passes (height fog, aerial perspective) reach this surface. false leaves an opaque deferred draw at the colour it shaded to, however deep in the medium it stands — see below. |
Emissive authoring
The glow output is emissive × emissive_intensity (× the emissive_texture). The glow
colour is emissive, a vec; the intensity over it is emissive_intensity, a
scalar. So { emissive = {0.1, 0.9, 1, 1}, emissive_intensity = 5 } is a cyan glow at 5×,
and it is these two names that getProperties() lists and setProperty writes.
A spelling another engine's convention uses for the same role — emissive_color,
emission, emissive_tint — is resolved onto emissive when the material is authored,
so content carried in from glTF, Unity or Godot reaches the property under the name this
shader declares. Where two such spellings meet on one of the two names, the shape of each
value says which of them it is: a vec is the colour and a number is the intensity, so
{ emissive_color = {0.2, 0.8, 1, 1}, emissive = 5 } is the same cyan glow at 5x. Two
values of one shape are one property's, and the one written under the name the shader
declares applies while the other is named in a warning.
A bare positive scalar emissive with no colour beside it is the "turn emission on"
shorthand and means a white glow at that intensity, so emissive = 20 on a white material
lights up.
The lobes over the base layer
Beyond metallic-roughness, the shading model carries six layers this material declares. Each is gated on its own neutral value, so a material that sets none of them shades as a plain dielectric and each one costs only where it is asked for.
| Layer | Turned on by | What it looks like |
|---|---|---|
| Clearcoat | clearcoat > 0 | A second dielectric specular lobe on top — car paint, lacquer, a varnished surface. |
| Anisotropy | anisotropy ~= 0 | The highlight stretches along the tangent — brushed metal, a vinyl record. |
| Sheen | sheen_color above black | A retroreflective rim that brightens toward grazing angles — velvet, satin, dusty cloth. |
| Iridescence | iridescence > 0 | The specular hue shifts with view angle — an oil slick, a soap film, anodised metal, a beetle shell. Thickness picks the colours. |
| Subsurface | subsurface_color above black | Light wraps past the terminator and glows through thin geometry lit from behind — skin, wax, marble, a leaf. thickness gates how much reaches the far side. |
| Fibre | fibre_color above black | The two offset highlights a strand gives instead of the one a surface gives — hair, fur. Taken around the world tangent as the fibre axis. |
All six are evaluated inside the engine's own light loop, once for every light that reaches the surface, so each one is shadowed, attenuated and cone-masked with the base layer it sits over.
An oil-slicked wet surface is iridescence = 1, an iridescence_thickness
around 300-500 nm and a low roughness over a dark base_color. Skin is a warm
subsurface_color with a subsurface_radius near 0.5. Velvet is a sheen_color
at the fabric's own hue over a dark base.
Standing outside the fog
receives_fog = 0 declares that the screen-space fog passes — heightFog and
aerialPerspective in @builtin::systems.atmosphere — leave this surface at the
colour it shaded to.
The declaration travels to those passes as a per-pixel surface flag written into
the alpha of the G-buffer normal (@builtin::shaderModules.surface_flags). Only
the deferred G-buffer fragment entry writes that channel, so the declaration
reaches the fog from an opaque draw on the deferred path. A draw the
renderer sends through the forward fragment entry — every draw on the forward
path, and the transparent phase on either path — writes no G-buffer channel to
carry it, and its pixels take the fog like any other.
Textures (glTF metallic-roughness set)
| Slot | Default | Channels |
|---|---|---|
base_color_texture | white | RGBA albedo (sRGB). |
roughness_metallic_texture | white | G = roughness, B = metallic (glTF packing). |
emissive_texture | white | RGB emissive (sRGB). |
normal_texture | flat-normal | Tangent-space normal map. |
occlusion_texture | white | R = ambient occlusion. |
Because the engine holds the full PbrSurface, the capture tool's debug passes
(albedo / roughness / metallic / AO / emissive / world-normal / shadow / depth /
motion) all read real material data.
Scoped to this part · feeds back into the world's score.