Talos docs

Materials

Since 0.6

A material file is a physical material a person or an agent writes by hand: a small JSON file whose name ends in .material.json.

Plain text

A material file is a physical material a person or an agent writes by hand: a small JSON file whose name ends in .material.json. Its keys are glTF 2.0's metallic-roughness material and its KHR_materials_* extensions, flattened and without the Factor suffix, so a glTF material maps onto it key for key. A preset gives tuned starting values for common surfaces, and any key the file sets overrides the preset.

{ "preset": "brushedMetal", "roughness": 0.25 }

The schema is packages/contract/src/material/ (Rust mirror: crates/contract/src/material.rs). packages/contract/material.schema.json is the generated JSON Schema. It carries the defaults and presets under x-argus-material.

Using one

Declare the file as a scene asset of kind material, then name it in a mesh component's material. The path follows the scene's asset path rule: it is relative to the scene file, with no absolute path, drive or .. (docs/PROJECT-MANIFEST.md, "Asset paths inside a scene").

"assets": {
  "crate":  { "kind": "glb", "path": "assets/crate.glb" },
  "steel":  { "kind": "material", "path": "materials/brushed-steel.material.json" }
},
"entities": [
  { "id": "crate-1", "components": [ { "type": "mesh", "asset": "crate", "material": "steel" } ] }
]

A material goes on a built-in mesh the same way: builtin:cube, builtin:sphere, builtin:capsule, builtin:plane, builtin:cylinder and builtin:cone are the six. Any other builtin: mesh name is refused (asset-builtin-unknown).

The override replaces every submesh's material, as a glTF material override always has. mesh.color still multiplies into the base colour. A material asset that is not a .material.json is read as glTF and contributes its first material, as before.

Examples are in fixtures/materials/examples/:

  • brushed-steel.material.json: a preset and one override.
  • tiles/tiles.material.json: colour, ORM and normal maps beside it, tiled 4×4.
  • neon.material.json: an emissive, blended, double-sided sign.

Keys

Every key is optional; {} is the default material. Colours are linear RGB(A), not sRGB and not hex. Unknown keys are refused (see Validation).

KeyglTF sourceRangeDefaultNotes
preset–a preset namenoneSee Presets.
namenameany stringnoneInformational.
baseColorbaseColorFactor4 numbers, each 0..1[0.8, 0.8, 0.8, 1]Three numbers are accepted and stored with alpha 1.
baseColorTexturebaseColorTexturetexture refnonesRGB; multiplies baseColor; its alpha is opacity.
metallicmetallicFactor0..10
roughnessroughnessFactor0..10.6Perceptual.
metallicRoughnessTexturemetallicRoughnessTexturetexture refnoneLinear: G roughness, B metallic.
ormTexture–texture refnoneLinear: R occlusion, G roughness, B metallic (an "ARM" map). Set this or metallicRoughnessTexture, not both.
occlusionTextureocclusionTexturetexture refnoneLinear, R. Packed into the ORM's R when the file also has an ORM or MR map.
occlusionStrengthocclusionTexture.strength0..11
normalTexturenormalTexturetexture refnoneLinear, tangent space, +Y up (OpenGL/glTF).
normalScalenormalTexture.scaleany finite number1Scales X and Y.
emissiveemissiveFactor3 numbers, each 0..1[0, 0, 0]
emissiveTextureemissiveTexturetexture refnonesRGB; multiplies emissive.
emissiveStrengthKHR_materials_emissive_strength0 or more11 is as bright as an unlit white surface.
alphaModealphaModeopaque, mask, blendopaqueLower case (glTF writes BLEND).
alphaCutoffalphaCutoff0..10.5Only with mask.
doubleSideddoubleSidedbooleanfalse
clearcoatKHR_materials_clearcoat0..10
clearcoatRoughnessKHR_materials_clearcoat0..10
clearcoatOrangePeel–0..10Strength of the clear coat's procedural wavy normal, the dimpled look of sprayed lacquer. Needs clearcoat.
clearcoatOrangePeelScale–millimetres, 2..308Wavelength of the orange peel, in object space with the instance's scale included.
specularKHR_materials_specular0..11Strength of a dielectric's reflection.
specularColorKHR_materials_specular3 numbers, each 0 or more[1, 1, 1]
iorKHR_materials_ior1 or more1.5
transmissionKHR_materials_transmission0..10
sheenColorKHR_materials_sheen3 numbers, each 0..1[0, 0, 0]HD only; see What each renderer draws.
sheenRoughnessKHR_materials_sheen0..10HD only.
subsurface–0..10Share of the diffuse light that scatters under the surface, as in skin. HD blurs it in screen space; see Skin and hair. More than 0 turns the clear coat off.
subsurfaceRadius–3 numbers in metres, each 0 or more[0.0022, 0.00072, 0.00034]How far light scatters per channel (Burley's diffusion distance d). The default is skin's: red travels furthest.
anisotropyStrengthKHR_materials_anisotropy0..10HD only. The highlight stretches along the direction, as on brushed metal.
anisotropyRotationKHR_materials_anisotropyradians0Turns the direction from the mesh tangent toward the bitangent.
anisotropyTextureKHR_materials_anisotropytexture refnoneLinear: RG a tangent-space direction, B scales anisotropyStrength.
anisotropyPattern–uv, radial, circularuvWhere the brushed direction points: along the mesh tangent (uv, as in glTF), outward from the part's centre (radial), or around its axis (circular, machined wheel faces). The centre is the mesh's local origin unless anisotropyTexture is given.
anisotropyAxis–x, y, zyThe mesh-local axis radial and circular turn about. Wheels from DCC tools often spin about local X, so a scene sets this; wheelAluminium leaves it unset.
iridescenceFactorKHR_materials_iridescence0..10HD only. A thin film's interference colours, as on soap bubbles or oil.
iridescenceIorKHR_materials_iridescence1 or more1.3The film's index of refraction.
iridescenceThicknessMaximumKHR_materials_iridescencenanometres, 0 or more400glTF's thickness runs from a minimum to this maximum through a thickness map. HD reads no map yet and uses this as the film's thickness.
diffuseTransmissionFactorKHR_materials_diffuse_transmission0..10HD only. Share of the diffuse light that passes through a thin surface lit from behind: leaves, paper, lampshades.
diffuseTransmissionColorFactorKHR_materials_diffuse_transmission3 numbers, each 0..1[1, 1, 1]Tint of the transmitted light.
flakeColor–3 numbers, each 0..1none (off)Colour of the metallic flakes under the clear coat. The carPaint preset sets it to the resolved baseColor × 1.2.
flakeDensity–0..10.5Share of the surface the flakes cover. Needs flakeColor.
flakeSize–millimetres, 0.05..20.3Flake cell size in object space, instance scale included, so it does not swim with the UV scale. Below about one pixel the flakes fade to their averaged tint. Needs flakeColor.
flakeSparkle–0..10.5How strongly each flake's random orientation makes it glint (the spread of the flake normals). Needs flakeColor.
flipColor–3 numbers, each 0..1none (off)Colour seen at grazing angles: a two-tone or colour-flip paint, blended by view angle.
flipStrength–0..11How far flipColor replaces the base colour at grazing angles. Needs flipColor.
layers–up to 3 of { material, mask, contrast }noneHD only. Materials blended over this one before lighting, in order; see Layers.
layerMask–texture refnoneLinear. The layers' mask, read with uvTransform. Absent: the vertex colour masks the layers.
uvTransformKHR_texture_transform{ offset, scale, rotation }identityApplies to every texture. scale is tiling and is non-zero; rotation is radians.
shadingKHR_materials_unlit for unlitpbr, unlit, vertex, toon, hairthe preset's, else the scene'sHow the surface is lit; see Shading and filtering.
toonSteps–whole number 1..163Light bands of toon shading.
filter–linear, nearest, three-pointthe scene'sHow the textures are filtered; see Shading and filtering.
shader–asset id or pathnoneA *.shader.wgsl whose surface() draws the material; see Shader files.
params–objectnoneValues for the shader's param and texture names; needs shader.
castShadows–true, falsetrueWhether the surface casts shadows.

The defaults are glTF's, with three exceptions: baseColor, metallic and roughness are the engine's builtin grey dielectric. glTF's own defaults describe a white rough metal. A hand-written file that sets only baseColor means paint, not metal. A glTF importer writes every key it has, so the difference never reaches an import.

uvTransform follows KHR_texture_transform: uv' = offset + R(rotation) × (scale × uv). A glTF import has already baked its transform into the model's UVs, so an imported material never carries one.

Shading and filtering

shading and filter say how the surface is drawn rather than what it is made of. Absent, a material takes its preset's shading (only hair sets one), else the scene's meta.render.shading and meta.render.filter (docs/RENDERING.md, "Stylised looks"), which default to pbr and linear. So one scene-level word restyles every material, and a material that sets its own keeps it.

shadingWhat it draws
pbrLit per pixel: GGX specular, Lambert diffuse, the sky's image-based light.
unlitbaseColor × colour map × vertex colour, exactly: no light, no tonemapping, no exposure.
vertexGouraud: diffuse light and the sky's ambient worked out at each vertex and interpolated across the triangle, no specular. The sun's shadow still falls per pixel. The late-1990s look.
toonDiffuse light in toonSteps bands plus a rim.
hairHair cards: fibre highlights running across the strands, which follow the mesh tangent (HD); see Skin and hair. The standard renderer draws it as pbr.

Every mode multiplies in the mesh's vertex colours (glTF COLOR_0).

filterWhat it draws
linearBilinear with mipmaps, or whatever a glTF model's sampler says.
nearestThe nearest texel: hard texel edges when a texture is magnified.
three-pointEach pixel blends the three nearest texels (a triangle, not a square), the filter some late-1990s console hardware used. Four texture reads instead of one.

A glTF model's texture sampler (its filters and its wrap modes: repeat, clamp, mirrored repeat) is honoured; a material file's textures repeat and filter linearly unless filter says otherwise.

{ "baseColorTexture": "bricks.png", "shading": "vertex", "filter": "nearest" }

This is not a fourth material. unlit and toon are the engine's unlit and toon materials, so HD draws them too. vertex, filter and the scene defaults are drawing styles of the standard renderer's one surface pipeline; HD lights a vertex material per pixel and filters linearly.

To see a material before placing it, capture it on a preview shape: POST /capture with {"kind": "material", "material": "<asset id or the file's JSON>", "shape": "sphere"} (play.capture takes the same), drawn alone under the scene's sky, sun and look.

Texture references

A texture reference is either of these:

  • A scene asset id of kind texture, e.g. "brick-albedo". The file is that asset's path. Use this for textures several materials share.
  • A path relative to the material file's folder, e.g. "tiles-albedo.png" for a file beside it. As with scene asset paths, an absolute path, a drive or a .. segment is refused (material-texture-invalid), so the material moves with its folder. Keep a material and its maps in one folder: materials/tiles/tiles.material.json beside materials/tiles/tiles-*.png.

The id is tried first. PNG, JPEG and KTX2 load through the same loader as glTF textures, and read from the exported payload in a shipped game. Colour and emission maps are read as sRGB and every other map as linear data, whatever the file says.

A texture that does not load leaves its slot empty and prints one warning. The material still loads, even in strict mode. An editor build (Play, test workers, export) counts every texture a material file names by path as one of the scene's files. The build packs each one, refuses to run when one is missing, and marks the artifact stale when one changes.

Shader files

A material with a shader is drawn by that shader instead of by glTF's factors and maps. The shader is a *.shader.wgsl file (asset kind shader): a short header of //! lines, then WGSL with a surface() function. docs/SHADERS.md has the format; this section is what the material file adds.

{ "shader": "shaders/dissolve.shader.wgsl",
  "params": { "edgeColor": [0.2, 0.6, 1.0], "albedo": "crate.png" },
  "castShadows": true }
  • shader is a scene asset id of kind shader, or a path relative to the material file, with the texture path rule (no leading slash, drive or ..). A file that is not there is material-shader-missing; a fault in its header is reported under the header's own code (shader-header-invalid, shader-param-invalid, shader-texture-invalid, shader-model-mismatch) with the shader's file:line:col.
  • Each params key must be a param or texture the shader declares, else material-shader-param-unknown (it suggests the nearest name; an instance value is set per entity by a behaviour with ctx.render.setShaderParam, not here). A value of the wrong type is shader-param-invalid; outside the declared [min, max] it is material-shader-param-range.
  • A colour is 3 linear numbers or #RRGGBB. A texture is white, black, flat, builtin:noise or a path relative to the material file.
  • baseColor, every texture map, shading and layers do nothing on a material with a shader (material-field-ignored, a warning). doubleSided, alphaCutoff, castShadows and the extension-lobe keys still apply. params without a shader also warns.

Presets

A preset sets only the keys it lists; the rest are the defaults. Colours are linear. carPaint also derives its flakeColor at expansion: the resolved baseColor RGB × 1.2, so a file's own baseColor paints the flakes too; a flakeColor in the file wins.

PresetWhat it is
plasticSmooth-ish white dielectric, a moulded plastic housing; set baseColor for the colour.
paintedMetalSatin paint over metal: a dielectric (the paint is what you see), a little rougher than plastic.
brushedMetalBrushed aluminium: full metal, medium roughness (the brushing direction is not modelled).
polishedMetalPolished steel or chrome: full metal, near-mirror.
rubberBlack rubber: dark, rough, a weak reflection.
glassClear glass: smooth, fully transmissive, ior 1.5 (HD draws it as thin glass).
carPaintMetallic car paint: a coloured base with flakes (flakeColor = baseColor * 1.2, whatever baseColor the file sets) under a full clear coat with a light orange peel.
wheelAluminiumMachined aluminium wheel face: full metal, brushed in circles about the wheel's axis. The axis is unset (y): wheels from DCC tools often spin about local X, so a scene sets anisotropyAxis.
wheelGlossBlackGloss black wheel: near-black dielectric under a smooth clear coat.
tyreRubberTyre rubber: dark, rough, with a dusty grey sheen. Lettering comes from the metallicRoughnessTexture or ormTexture roughness channel (smoother and darker where the letters are).
fabricCloth: fully rough with a soft sheen and a weak reflection (HD draws the sheen).
woodUnfinished wood: a warm brown dielectric, fairly rough; pair it with a grain texture.
concreteCast concrete: light grey, matte.
emissiveLightA lit panel or bulb: warm white emission at 5x unlit white, so it blooms.
skinHuman skin: light scatters under the surface (HD), and the reflection is weak (F0 0.028); set baseColor for the tone.
hairHair cards: fibre highlights along the mesh tangent (HD), alpha-masked strands, both sides; set baseColor for the hair colour.

The numbers are the MATERIAL_PRESETS table in packages/contract/src/material/presets.ts. It is emitted into crates/contract/src/gen.rs and material.schema.json, so both languages resolve a preset from one table. Resolution happens in three layers, and each later layer wins: the defaults, then the preset, then the file's keys.

Validation

Both languages report the same violations, with the same codes, pointers and messages (fixtures/materials/manifest.json is the shared list; argus-validate --material <file> prints the Rust verdict and the resolved material).

CodeSeverityWhen
schema-shapeerrorNot JSON, not an object, a wrong type, an unknown key, or a preset / alphaMode / shading / filter that is not a member.
material-value-rangeerrorA number outside its range, as in the table above; more than 3 layers; a layer's contrast below 1.
material-texture-invaliderrorA texture reference, or a layer's material reference, is empty, absolute, names a drive or has a .. segment; or ormTexture and metallicRoughnessTexture are both set.
material-field-ignoredwarningA key that has no effect: alphaCutoff without mask, normalScale without a normal map, occlusionStrength without an occlusion or ORM map, an emissiveTexture or emissiveStrength on a black emissive, clearcoatRoughness with no clear coat, sheenRoughness with no sheen, clearcoat on a material with subsurface above 0, subsurfaceRadius while subsurface is 0, subsurface with hair shading, anisotropyRotation, anisotropyTexture, anisotropyPattern or anisotropyAxis while anisotropyStrength is 0 (the axis also with uv brushing), clearcoatOrangePeel with no clear coat, clearcoatOrangePeelScale with no orange peel or clear coat, flakeDensity, flakeSize or flakeSparkle without flakeColor, flipStrength without flipColor, iridescenceIor or iridescenceThicknessMaximum while iridescenceFactor is 0, diffuseTransmissionColorFactor while diffuseTransmissionFactor is 0, layerMask without layers, sheenColor, anisotropyStrength, iridescenceFactor or diffuseTransmissionFactor on a skin (subsurface above 0) or hair material, toonSteps without toon shading, a filter with no texture.

Unknown keys and names come with the key they were probably meant as:

unknown key "baseColorFactor"; did you mean "baseColor"?
unknown key "normal_texture"; did you mean "normalTexture"?
uvTransform: unknown key "tiling"; did you mean "scale"?
preset "car-paint" is not one of plastic, paintedMetal, …; did you mean "carPaint"?
alphaMode "BLEND" is not one of opaque, mask, blend; did you mean "blend"?
shading "gouraud" is not one of pbr, unlit, vertex, toon; did you mean "vertex"?

The suggestion is chosen in this order. First comes glTF's own spelling of the key, plus a few common guesses (color, albedo, metalness, tiling). Then the camelCase of a snake_case or kebab-case spelling, then a case-insensitive match, then the closest key within two edits.

glTF's trap is worth knowing. emissiveFactor defaults to black, and the emission map multiplies it, so an emissiveTexture on its own glows nowhere. material-field-ignored says so. Set emissive to [1, 1, 1] to use the map as it is.

At load, a material file that does not validate fails a strict load (LoadOpts::placeholder_assets off) with its violations, as a model that does not load does. Otherwise (the default) the builtin material stands in, and one warning names the file and the first violation. Warnings print once per key.

Skin and hair

Characters use two presets, and each keeps working when its keys are overridden.

{ "preset": "skin", "baseColor": [0.62, 0.42, 0.33] }
{ "preset": "hair", "baseColorTexture": "strands.png", "baseColor": [0.2, 0.12, 0.06] }
  • Skin (subsurface above 0). subsurfaceRadius per channel. The kernel is the screen-space separable one from Jimenez et al. 2015, fitted to Burley's diffusion profile. edges, softened pores. stays sharp. when no skin is in view. Quality knob materials.sss (off on Low).
    • HD takes that share of the pixel's diffuse light and spreads it over the surface around it, by
    • The scattering moves light and keeps its total: soft terminators, red bleeding into shadow
    • The colour map is applied half before and half after the scattering, so texture detail
    • A scattering material has no clear coat.
    • Cost on an RTX 5080 at 1440p: about 0.06 ms when skin fills half the screen, and nothing
  • Hair cards (shading: "hair"). model's tangents), and the highlights run across them. and light through the fibres from behind. below it is dithered, and the temporal upscaler turns the dither into soft coverage. transparency pass instead. It looks softer at silhouettes, and costs about three times as much.
    • Each card's strands run along its mesh tangent (the direction of increasing U in a glTF
    • The lobes follow Karis 2016: a white highlight toward the root, a tinted one toward the tip,
    • Use alphaMode: "mask". Texels at or above alphaCutoff are opaque. The thinner fringe
    • The quality knob hair.blendedFringe (Ultra) blends the fringe in the order-independent
    • Cost on an RTX 5080: about 0.09 ms for a close-up head of 200 cards.
  • Non-character uses. Both are general. subsurface suits wax, marble, jade or leaves; hair shading suits fur, grass cards and any strand-like card.

Layers

layers blends up to three other materials over this one, in order, before the surface is lit. This is how dirt over paint, wear over metal or a wet patch over asphalt is drawn. Each entry is:

KeyValuesDefaultNotes
materiala scene asset id of kind material, or a .material.json path relative to this filerequiredThe layer's own layers and HD-only effects are not read, so layers cannot cycle.
maskr, g, b, arThe mask channel.
contrast1 or more1Sharpens the mask around 0.5; 1 keeps it as is.

The mask is layerMask, read with this material's uvTransform. Without it, the mesh's vertex colour is the mask, and it no longer tints the surface. Each layer reads its own textures with its own uvTransform, so a dirt layer can tile on its own. Base colour, normal, roughness, metallic, occlusion, emission, clear coat and specular blend.

A layer that does not load is left out, with one warning. A layer file, or a texture it names, that the project does not hold is also said before the run: build.material-layer-missing on the scene write and in the build. The standard renderer draws the base material only.

{ "preset": "paintedMetal", "layers": [{ "material": "rust.material.json", "contrast": 3 }], "layerMask": "wear.png" }

Terrain layers

A terrain blends up to eight layers, each an existing material file named in the terrain's layers (material, a scene asset id or a project-relative path). What a layer takes from its file: baseColor times baseColorTexture (the colour), normalTexture, roughness, and occlusionTexture as its height map (without one, the colour's luminance). Everything else in the file is ignored for a layer. The layer's own uvScaleM is the metres one tile covers, and triplanar: "auto" (the default) lets it map from two world directions on faces steeper than 40 degrees; "off" keeps it top-down.

A pixel of terrain shades two layers, however many the terrain has: the two heaviest among the four samples around it, blended by height (the higher layer's texture fills between the lower one's), whether the standard renderer or HD draws it. The standard renderer swaps to a baked far colour map (one texel a metre) beyond 64 m; HD shades the layers directly at every distance.

What each renderer draws

Standard rendererHD (render.profile: "hd")
Base colour, colour map, metallic, roughness, normal mapyesyes
ORM / metallic-roughness / occlusion maps, occlusion strengthnoyes
Emission colour and strengthyes: added to the lit colour before exposure and the tonemap; unlit surfaces ignore ityes
Emission mapnoyes
Alpha mask and blend, double-sidedyes (blend sorted back to front per object)yes
shadingyesunlit and toon; pbr and vertex are lit per pixel
filter, a glTF sampleryeslinear
Clear coat, specular, specular colour, iorno (a second lobe only for the car keys below)yes (ior folds into the specular tint)
Transmissionnoas thin glass: blended by what it does not transmit, no refraction
uvTransformno (the mesh's own UVs)yes, in every pass: raster, shadow, virtual geometry and ray tracing
Sheennoyes (Charlie sheen over the base, KHR_materials_sheen)
Anisotropyonly with the car keys below: a stock shader stretches the highlight along anisotropyPattern/anisotropyAxisyes (not on skin or hair materials)
Iridescence, diffuse transmissionnoyes (not on skin or hair materials)
Flakes (flakeColor and tuning), orange peel, flip colour, clear coat on the new car keysyes: a stock surface/light module the engine ships, drawn through the shader path (cell-noise flake normals in object-space millimetres, faded to the averaged tint below about one pixel; clear coat as a second specular lobe)yes: HD's car-paint layers
layersno: the base materialyes, blended before lighting
subsurface, subsurfaceRadiusnoyes: screen-space subsurface scattering (quality knob materials.sss)
hair shadingas pbryes: fibre lobes along the tangent; the fringe under alphaCutoff dithered, or blended with hair.blendedFringe

Car paint, wheels and tyres in the standard renderer. A material file that uses a car preset (carPaint, wheelAluminium, wheelGlossBlack, tyreRubber) or any car key (flakeColor, flipColor, clearcoatOrangePeel, a non-uv anisotropyPattern), on an opaque pbr material without a shader, is drawn with a stock surface module the engine ships, through the same path as a user shader. It is built for the keys the material uses, so a plain clear coat does not pay for flakes. Flakes are a cell noise of random normals sized in object-space millimetres (the instance scale is included) with a glint under each light, and fade to their averaged tint below about a pixel, so they do not shimmer at distance. Orange peel is a low-amplitude wavy normal on the clear coat, faded the same way. The colour flip blends by view angle. The clear coat is a second specular lobe; anisotropy bends the normal along the pattern (the highlight stretches along the pattern's direction, as in glTF; radial and circular turn about anisotropyAxis of the mesh's local origin). Sheen is a dusty rim. The clear coat reflects the scene's sky or environment, and flakes glint under the sun and every local light. The stock modules draw no normal map. A web, Android or iOS build, whose shader cost cap the full module exceeds, gets a lighter set (no orange peel, colour flip or anisotropy; flakes as a static speckle with an averaged glint; one lobe whose roughness follows the clear coat). The module ships in an exported game's shader pack and pre-warm list, so a shipped build draws it too (an HD export draws these keys natively and needs none). Materials without these keys draw exactly as before.

The standard renderer draws a material file exactly as it draws a glTF material. It uses the PBR-lite of the colour, roughness, metallic, colour map and normal map (PbrMaterial::lite), with the material's shading, filter, alpha and sidedness (a MaterialStyle registered beside it). Its frames of existing opaque scenes are unchanged. When a material file sets something only HD draws, the standard renderer prints one line naming those keys. The maps only HD reads (ORM, occlusion, emission) are loaded only under HD. Emission colour times strength is the exception: the standard renderer adds it to a lit surface's colour (pbr, toon and vertex), so a glowing material needs no unlit shading. A material with no emission renders byte for byte as before.

Alpha in the standard renderer: mask cuts texels below alphaCutoff out of the colour, depth and id buffers; blend draws after every opaque surface and the sky, each blended object on its own, farthest first by its centre, without writing depth. Two limits: shadows are cast by the whole surface either way, and a blended surface keeps the tonemap state of what is behind it, so under aces or neutral an unlit blended surface over a lit one is tonemapped with it (with tonemap: "none" there is no difference).

Reloading

The running world caches a material file per asset id. It re-reads the file when the scene's asset entry changes, which a hot reload and a scene switch both detect, and on a lost device. Editing the .material.json in place, or one of its textures, is not seen by a running argus run: its watcher watches the scene file, as it does for models. The exception is a material with a shader: under --watch the watcher also covers its shader file (reloaded in the background, the old version drawing until the new one is built) and the material file's params, which are re-read and written to the shader's buffer (docs/SHADERS.md, "Editing live"); a changed shader key or texture still needs a scene reload. In the editor, the material file and every texture it names are build inputs, so an edit makes the current artifact stale and the next Play loads it.