Material
What a 3D surface looks like. The built-in looks are static factories — Material.lit (PBR),
Material.unlit (flat), Material.video, Material.shadow — and custom compiled shaders load as
materials too. Uniforms are set through .set() / the uniforms proxy, which lowers each JS
value to the right native uniform call. Assign to a mesh via mesh.material — see
Mesh.
At a glance
const tex = await Texture.load(asset('./crate.jpg'))
const crate = Mesh.box({ material: Material.lit({ color: '#ffffff', map: tex }) })
const marker = Mesh.sphere({ material: Material.unlit({ color: '#0f0' }) })
scene.add(crate, marker)
crate.material.set('roughness', 0.3).set('metallic', 1)Factories
Material.lit(options?: { color?: ColorInput, map?: Texture | Canvas | null,
roughness?: number, metallic?: number }): Material // PBR lit
Material.unlit(options?: { color?: ColorInput, map?: Texture | Canvas | null }): Material // flat, ignores lights
Material.video(map?: Texture): Material // samples a VideoPlayer texture
Material.shadow(color = '#000000aa'): Material // shadow-catcher: invisible except where shadows fall
Material.load(url: string): Promise<Material> // custom compiled shader (.mat / filamat)litneeds light to be visible — IBL and/or aLight.sun(). Its PBR scalars are 0–1:roughness0 = mirror, 1 = matte;metallic0 = dielectric, 1 = metal (both are plain uniforms — unset means the shader's default, and.set('roughness', v)changes them later).videotakes the texture fromVideoPlayer.texture.shadowis the AR staple: a ground plane that shows only the shadows cast on it.
Color & map
material.color = '#ff8800' // any ColorInput
material.map = tex // Texture, or a Canvas (baked to a texture on assign)
material.map = null // remove the mapcolor and map are conveniences over the underlying uniforms (baseColor / baseColorMap on
lit/unlit, color on shadow). On a custom material they don't know the uniform names —
color is a silent no-op and map writes baseColorMap; set your own uniforms directly instead.
Custom shaders & uniforms
const material = await Material.load(asset('./hologram.mat'))
// or, from a response you already have:
const material = new Material(await fetch(asset('./hologram.mat'), { useOnce: true }))
material.set(key: string, value): this // chainable
material.uniforms[key] = value // same thing, property-style
material.uniforms.speed // reads return the last value YOU set (no native read)Each value type lowers to a specific uniform call:
| JS value | Lowered to |
|---|---|
'#rrggbb' string |
RGB color |
'#rrggbbaa' string |
RGBA color |
number |
float |
boolean |
bool |
Texture |
sampler (honors the texture's wrapS/wrapT) |
number[] / Float32Array |
float array / vector |
null |
clears the uniform |
color strings must be #-hex here — the wider ColorInput forms (rgba(…), packed
ints) are only understood by the color setter, not by set(). And a Vec3/Quat wrapper is
not lowered — spread it to a plain array first: material.set('dir', [...v]).
The scene occlusion material
scene.occlusionMaterial hands back the scene's occlusion material as a regular Material, so
you can set uniforms on it — see Scene (host-gated: not available on the web viewer).
Pitfalls
// ✗ expecting material.color to work on a custom shader
customMat.color = '#f00' // no-op: the SDK doesn't know your uniform's name
// ✓ address the uniform directly
customMat.set('baseColor', '#ff0000')
// ✗ passing a Vec3 as a uniform value
mat.set('lightDir', dir) // not an Array — lowers to NULL, not a vec3
// ✓
mat.set('lightDir', [...dir])