API Reference · Materials and textures
Material
public sealed class MaterialThe surface appearance of a MeshNode: metallic-roughness PBR, the same model glTF 2.0 defines, so a loaded model looks the way its author intended rather than the way a viewer guessed.
Every slot has a scalar and an optional texture, and the texture multiplies the scalar. A material with no textures at all is still a complete PBR material — Material.Metallic and Material.Roughness alone are enough to describe brushed steel or matte plastic, which is why the demo's material scenes need no image assets.
Two of these are shading-model choices worth knowing about. Material.Metallic is a blend, not a flag: at 0 the surface is a dielectric with a white 4% specular reflection and its own diffuse colour, at 1 it has no diffuse at all and reflects Material.BaseColor tinted. Values in between are physically meaningless for a pure substance but are how authored assets fake rust, wear and dust, so the renderers interpolate rather than threshold. And Material.Roughness is perceptual — it is squared before it reaches the GGX distribution, which is what makes the low end of the slider useful instead of collapsing to a mirror.
Properties
| Member | Description |
|---|---|
| Fragments whose base-colour alpha falls below this are discarded. Zero means the material is opaque and nothing is discarded. Foliage and cut-out details need it; without it they draw as solid cards. |
| Linear RGBA, multiplied with For a metal this is the reflectance tint — gold is roughly (1.00, 0.77, 0.34). For a dielectric it is the diffuse albedo. |
| UV set and transform for |
| A program that computes Assigning one points the map at the program's own texture, and everything downstream — the caches, the uploads, the shaders — then treats it as the ordinary texture it is. Changing the program's See |
| Base colour map. Treated as sRGB-encoded and linearised by the renderer. |
| How this material's fragments combine with what is already in the frame. Ordering is per object, not per triangle: within one |
| How far |
| A height field that perturbs the shading normal by its screen-space gradient. Unlike |
| Which faces are rasterised. The two cases that want otherwise are a sphere seen from inside — a sky dome, where only the far hemisphere should draw, so The default is for materials built in code. A material loaded from a glTF file gets this from the file's own |
| A constant nudge to this surface's depth, in the smallest step the depth buffer can represent. Positive pushes it away from the camera, negative pulls it forward. Zero, the default, is exactly what this control has always done. What it is for: geometry that is coplanar with something else and has to lose, or win, consistently. The case that produces it is a hull drawn as triangles with its panel seams drawn as a Two things a caller reaches for first, both worse. Turning the lines' The unit is the depth buffer's own, which is why this pairs with |
| The same nudge, scaled by how steeply the surface recedes from the camera. Zero by default. The reason both exist: a polygon seen edge-on spans many depth values within a single pixel, so the error a constant bias has to cover grows with the slope. The slope-scaled term measures that and the constant term covers the rest, which is why the fixed-function offset every GPU implements takes exactly these two numbers and why the pair is the whole of the feature.
Where it applies: filled triangles, which is what the offset is defined for on every backend, the CPU fallback included. It has no effect on The browser can do this now. It could not before: |
| Whether fragments are tested against the depth buffer. Off draws over everything already there. |
| Whether fragments write depth. On by default, which is right for opaque geometry and wrong for almost anything blended: a transparent surface that writes depth occludes whatever is drawn after it, including the parts of the scene meant to show through. |
| How far away the detail layer fades out, in metres. Zero is automatic, which is the useful setting. Automatic means by footprint rather than by distance, and the difference matters. What makes a detail layer sparkle is its texels falling below the size of a pixel, and that depends on the window, the field of view and how obliquely the surface is seen — not only on how far away it is. A fade tuned in metres at one resolution sparkles at another. So by default the layer fades on its own texel footprint, which is the quantity that actually governs the fault. A non-zero value is an additional artistic limit on top of that, for a caller who wants the layer gone before it would have faded anyway. |
| How far the detail field perturbs this surface's normal. Zero is off, and off is the default. The fine end of the surface: what a tiling map cannot carry at any resolution anybody can afford. One is a strong, obviously-textured surface; a tenth is the amount that stops something reading as plastic without anybody noticing why. |
| How far the detail field perturbs this surface's roughness. Zero is off. The answer to the third of the three complaints. "Blurry, tiled, not PBR" is three faults, and the third one is almost always a constant roughness: a surface that is equally rough everywhere reads as plastic under any lighting model, because no real surface is. This costs nothing extra — the value is already fetched for |
| How many times the scene's detail field repeats per metre on this surface. Zero takes the scene's own Independent of |
| How far the detail field modulates this surface's colour. Zero is off. A value of 0.5 in the field is no change, so the modulation is signed and a flat field is the identity. What it buys is tonal variation, which is most of what separates generated masonry from tile — and it is the cheapest weathering there is, because the value is already fetched. |
| How far the detail layer's roughness rises as its normal detail fades out. One by default. The part nobody does, and the reason fading is not just switching off. Normal variation and roughness are the same quantity at two scales: a surface covered in slopes too small to see is a rough surface. So a detail normal that fades with distance must not simply vanish — the microfacet response it stood for has to go somewhere, or the surface goes smooth and plastic at range, which is the complaint the layer was added to fix, returning at a different distance. Lost normal detail is converted into roughness instead. At full strength this is exactly today's answer; fully faded, the surface is as rough as the detail made it look. There is no distance at which the material changes character. Zero switches it off, for a caller who wants the layer to disappear rather than to settle. |
| Whether back faces are lit as if they faced the camera. On by default because exported CAD assemblies routinely have inconsistent winding, and thin geometry like drapes reads as black without it. This is about shading, not about which faces are drawn — |
| Linear RGB light the surface emits on its own, added after lighting and unaffected by it. This is what makes screens, indicator lamps and hot metal read as lit in an otherwise dark scene. |
| UV set and transform for |
| Restricts What it is for: an emissive map carrying city lights or lava has to be invisible in daylight. Unmasked, a planet's cities glow straight through its own day side. |
| The value of Below |
| The value of |
| A program that computes The one most animated surfaces want. A screen, a terminal, a sign and a readout are light rather than surface, and every one of them was a stack of baked frames before this existed. |
| Emissive map, sRGB-encoded, multiplied with |
| The image sampled by the view-space normal under A photograph or render of a lit sphere filling the frame. The centre is the part of the surface facing the camera and the rim is the part turning away, so the useful content is a disc and the corners are never read. |
| Whether the matcap takes a sampler unit of its own or shares the base colour's. |
| 0 for a dielectric (plastic, wood, painted metal), 1 for bare metal. Clamped to 0..1. |
| UV set and transform for |
| A program that computes |
| Combined metallic-roughness map, in the glTF packing: green is roughness, blue is metallic, red and alpha unused. Linear data — the renderers do not gamma-correct it, and a viewer that does gets uniformly too-glossy surfaces. |
| Optional name, carried through from a source file to make diagnostics readable. |
| UV set and transform for |
| A program that computes |
| How far |
| Tangent-space normal map, linear, with +Z out of the surface. Needs |
| UV set and transform for |
| How much of |
| Baked ambient occlusion in the red channel, linear. Attenuates only the ambient and environment terms, never the key light — occlusion darkens crevices that the sky cannot reach, and applying it to direct light double-darkens whatever the shading already handles. |
| Linear RGB of the rim glow. Has no effect until |
| Strength of the rim term, and the switch for it — zero disables the whole calculation. The term is |
| How much of the rim follows the key light, 0..1. At 0 the halo is even all the way round; at 1 it is entirely on the lit side. The default leaves the unlit limb faintly visible, which is what stops the glow ending in a hard edge at the terminator. |
| How tightly the rim hugs the silhouette: |
| Perceptual roughness: 0 is a mirror, 1 is chalk. Clamped to 0.03..1 rather than 0..1, because a true zero makes the GGX highlight a single sub-pixel point that aliases into flicker as the camera moves. 0.03 is visually a mirror and numerically stable. |
| How this surface turns light into colour. |
| What colour the wrapped light turns on its way through, as a linear multiplier. White — the default — leaves it the colour it arrived. Ignored entirely when The tint applies only to the light the wrap added, never to light the surface would have caught anyway. That is what makes it a scattering tint rather than a second albedo: the lit side stays the colour the material says it is, and the softened band past the terminator picks up the colour of whatever the light passed through to get there. For skin that is blood, so It is a multiplier and not a colour in its own right, so a channel above 1 is legal and brightens the wrapped band. Nothing clamps it on the way in. |
| How far light wraps past the terminator, from 0 for a Lambertian surface to 1 for one lit all the way round. Clamped to 0..1, and 0 — the default — is bit-for-bit the shading every material had before this property existed. This is the cheap stand-in for subsurface scattering, and it is worth being exact about what it approximates. Light entering skin, wax, leaves, milk or thin cloth does not stop at the surface: it scatters underneath and leaves somewhere else, which is why a lit face has no hard edge between its light and dark sides and why the rim of an ear glows against a lamp behind it. A Lambertian surface has a hard terminator by definition, so a head shaded with one reads as painted stone however good the geometry under it is. What this does is widen the diffuse response — Values worth starting from: 0.3–0.5 for skin, 0.5–0.7 for wax and thin leaves, 0.2 for cloth. Past about 0.7 a surface stops reading as lit from any particular direction, which is occasionally the point and usually a mistake. |
| How many steps the light response is quantised into under Deliberately a count rather than a ramp texture. A ramp would be more expressive and would cost the last guaranteed sampler unit; a count plus the material's own colours covers the case that exists, and the case that exists is a consumer of this control splitting every hull into up to three meshes to get exactly this. Below two it stops banding at all, which is the honest answer to "one band". |
| Slides the bands towards the light or away from it, under Zero leaves them where the light falls. Positive widens the lit areas and pushes the terminator round towards the dark side; negative does the reverse. It is the one control that decides whether a cel-shaded shape reads as bright with a thin shadow or dark with a highlight, and there is no right default for it, so the default is none. |
|
Kept because it is what every existing caller says, and because "unlit or not" reads better than an enum comparison at the two dozen call sites that only ever wanted the one model. Setting it false returns an unlit material to |
| How many times the texture repeats per metre under a projection. One by default; ignored under Per metre rather than per surface, which is the whole point: it is the same number on a bolt and on a bulkhead, and the density comes out right by construction instead of by arithmetic at every call site. |
| How far this material's texture coordinates are slid, after Animating this is what a scrolling surface is: a conveyor, a waterfall, a jet's exhaust. It is the cheapest animation in the library — one vector a frame, no geometry touched, no texture re-uploaded — and its absence is why a consumer of this control ended up baking a twelve-frame flipbook of one plume, at 384 KB and half a million Applied in the vertex shader, so it costs a vertex uniform rather than a fragment one and takes nothing from the light budget. See |
| What The rotation only. Never reaches a shader. The three properties are composed into one affine map when the scene is flattened, and a pivot is two translations either side of that map — so this costs a caller nothing at run time and saves them the arithmetic that The middle rather than the origin because that is what a caller means. Rotating an atlas frame about the origin sends it off the sheet; rotating it about its own centre turns the picture on it. |
| How far this material's texture coordinates are turned, in radians, anticlockwise about The half of a texture transform that every engine leaves out and everybody then writes by hand. glTF's A rotation is what breaks a repeat that the eye has already learned. A phase offset slides a pattern; the pattern is still the same pattern. A quarter-turn on every other instance of a crate is what makes forty of them stop reading as forty copies, and it costs nothing that It is applied to the sampled normal as well as to the coordinate, which is the part nobody does. A normal map's x and y are a gradient in texture space; turn the coordinate and the gradient has to turn with it or the surface lights from the wrong direction — and it reads as a lighting fault, so it gets chased in the lighting. The renderers here apply the inverse transpose of this transform to the sampled normal, which is exact and costs two multiplies. Anticlockwise and in radians, matching |
| How far this material's texture coordinates are stretched before they are sampled. Shared settings move all maps together. Each map may first select its own UV set and transform using BaseColorMapping, NormalMapping and the corresponding mapping properties for other maps. Two above one and the texture repeats twice across the surface, which needs the texture's wrap mode to be a repeating one to look like anything. |
| How sharply The exponent the surface normal is raised to before the three weights are normalised. One is a wide, soft blend that shows all three projections over most of a curved surface; eight is nearly a hard switch at each forty-five degree line. Four is a good default for hard-surface geometry and a lower number suits something organic. |
| Where this material's texture coordinates come from: the mesh's own, or a projection. What a projection solves. Every pathology of a mesh's own parameterisation, at once. A projected material has no seam, no pole, no stretch, and no dependence on how the mesh was unwrapped — so a sphere stops wearing a sunburst at its poles, a rounded box stops wearing one on its top, and geometry generated in code needs no texture coordinates at all. It also changes what a scale means. Under a projection the density is What it costs. |
| Whether a projection is fixed to the object or to the world. Object is the safe default and the useful one. A world-space projection on a ship that flies makes the plating swim across the hull as it moves, which is the feature not working on the thing it is most wanted for. Object space travels with the geometry, so a crate wears the same patch of texture wherever it is put. World space earns its keep on static level geometry, where it does something object space cannot: two identical walls at two positions read different parts of the texture, so a repeat breaks up for free. And after |
| What the alpha of |
Events
| Member | Description |
|---|---|
| Raised when a Why this exists, and why it is this narrow. A material is the one thing in the scene graph that is not watched. A node invalidates its scene when it moves, a texture carries a A program is not that. It is a live thing that has to be found and ticked, and the order that reads most naturally — build the scene, show it, then hand a screen its program — produced a screen that stayed black for ever, because the walk that finds programs runs when the scene changes and assigning to a material is not a scene change. So this is raised for exactly that, and |
Methods
| Member | Description |
|---|---|
| A copy, so a caller can tint or highlight one node without disturbing the others sharing it. |
| A plain untextured dielectric of the given linear colour. |
| Announce that this material has been changed, for hosts that mutate one already on screen. The counterpart of |
| Points
|