Home / Features / Materials and PBR
Materials come from glTF and stay in glTF's model: metallic-roughness, with normal, occlusion and emissive maps. There is no material editor and no shader graph — the surface you authored in Blender is the surface the engine renders.
Every opaque and transparent surface goes through the same metallic-roughness BRDF. That is a deliberate narrowing: no material permutation matrix, no shader variants to warm up, and an asset that looks right in a glTF viewer looks right here.
| Input | Handling |
|---|---|
| Base colour + factor | sRGB texture, multiplied by the per-material factor and per-instance colour. |
| Metallic / roughness | Packed glTF channels, sampled directly; no remap layer. |
| Normal map | Tangent-space, with optional Toksvig normal-variance mips to tame specular aliasing. |
| Occlusion | Applied to indirect diffuse only, so it does not double-count with GTAO. |
| Emissive | Added after lighting, unaffected by shadows and AO. |
| Alpha | Opaque, mask and blend modes; blended geometry is drawn after opaque in the same pass. |
| Unlit | Model.Unlit bypasses lighting entirely — useful for debug markers and UI-in-world. |
An imported material is a starting point, not a contract. Every map and factor has an
override on Model, so a shared asset can be recoloured or
re-textured per instance without touching the GLB.
| Property | Type | Replaces |
|---|---|---|
MaterialColor | Vector4? | Whole-model colour multiplier, applied on top of the base colour factor. |
BaseColorOverride | TextureUpdateSource | The base colour map. |
NormalOverride | TextureUpdateSource | The normal map. |
MetallicRoughnessOverride | TextureUpdateSource | The packed metallic-roughness map. |
OcclusionOverride | TextureUpdateSource | The ambient occlusion map. |
EmissiveTextureOverride | TextureUpdateSource | The emissive map. |
MetallicOverride | float? | The metallic factor. |
RoughnessOverride | float? | The roughness factor. |
EmissiveFactorOverride | Vector4? | The emissive factor. |
Unlit | bool | Skips lighting for the whole model. |
Overrides are nullable or empty by default, which means "use whatever the file says". A
texture override takes a TextureUpdateSource, so the
replacement can be a file, a decoded buffer, or a texture another effect produced.
Sampling quality is a project-wide decision rather than a per-material one, so it lives on
RenderQuality.
| Setting | Default | Effect |
|---|---|---|
TextureMipmaps | on | Generates the mip chain at upload. |
TextureMipMinSize | 64 | Stops generating mips below this edge length. |
TextureMaxAnisotropy | 16 | Anisotropic filter taps, clamped to what the device reports. |
TextureNormalVariance | on | Toksvig-style roughness widening in normal-map mips. |
TextureLodBias | -0.5 | Slight sharpening bias; TAA absorbs the extra aliasing. |
The negative LOD bias and normal-variance mips are paired on purpose. Biasing towards sharper mips reintroduces specular sparkle, and widening roughness where the normal map is busy is what pays for it. Turn one off and you probably want to turn the other off too.
Scene colour is HDR by default. Lighting accumulates in linear space at floating-point precision, and the Post pass applies exposure and tonemapping on the way to the swapchain — which is what lets bloom threshold against real radiance values instead of clipped ones.
| Setting | Default | Notes |
|---|---|---|
HdrSceneColor | on | Off falls back to an 8-bit scene target; bloom and TAA quality drop with it. |
HdrExposure | 1.0 | Linear multiplier applied before the tonemap curve. |
KhrLightIntensityScale | 0.05 | Scales punctual lights imported from glTF, whose photometric units are far larger than the engine's. |
KhrLightIntensityScale exists because
KHR_lights_punctual specifies intensity in candela and lux,
and a light exported at those magnitudes will blow out a scene lit in engine units. A model
can further scale its own imported lights with
Model.LightIntensityScale.
Constant ambient is the default and the cheapest option. When a scene deserves better,
EnvironmentMap loads six cube faces and projects them into
spherical harmonics for diffuse ambient, optionally keeping the radiance cube for specular.
| Mode | What is applied |
|---|---|
Off | Constant ambient from SceneLighting.Ambient only. |
Diffuse | Nine-coefficient irradiance SH replaces the constant ambient term. |
DiffuseSpecular | As above, plus the radiance cube for specular ambient. |
Cube faces load as Rgba8Unorm or
Rgba16Float;
SkyIntensity and
DiffuseIntensity scale the two contributions independently,
which is the usual way to keep an HDRI from over-lighting a scene that also has a sun.