LeCodesdocs

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

TypeScript
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

TypeScript
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.particles(options?: { map?: Texture | null, sheet?: number | [cols, rows],
                               emissive?: number, blend?: 'alpha' | 'add', soft?: boolean,
                               render?: 'point' | 'quad' | 'stretch' | 'ribbon', stretch?: number }): Material
                                                     // sprites/quads/ribbons for Particles + Trail (their default)
Material.load(url: string): Promise<Material>        // custom compiled shader (.mat / filamat)
  • lit needs light to be visible — IBL and/or a Light.sun(). Its PBR scalars are 0–1: roughness 0 = mirror, 1 = matte; metallic 0 = dielectric, 1 = metal (both are plain uniforms — unset means the shader's default, and .set('roughness', v) changes them later).
  • video takes the texture from VideoPlayer.texture.
  • shadow is the AR staple: a ground plane that shows only the shadows cast on it.
  • particles is what a Particles system (and Trail) draws with when you don't pass a custom material — you rarely construct it yourself, since the Particles/Trail options forward to it. Per-particle tint/size/rotation/opacity/frame come from the particle curves; the uniforms (sheetSize, emissive, additive, softness, plus mode/stretch on the quad variant) set the per-system look, and the factory options map onto them. render: 'point' uses particles.filamat; the other modes share particles-quad.filamat.

Color & map

TypeScript
material.color = '#ff8800'            // any ColorInput
material.map = tex                    // Texture, or a Canvas (baked to a texture on assign)
material.map = null                   // remove the map

color and map are conveniences over the underlying uniforms (baseColor / baseColorMap on lit/unlit, color on shadow). On lit/unlit the shader multiplies them — baseColor × baseColorMap, both defaulting to white (an unset map is backed by a 1×1 white texture) — so a color alone or a map alone shows through unchanged, a color set alongside a map tints the map, and map = null resets the texture back to white. 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

TypeScript
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 (a texture resets to the 1×1 white)
Note

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

TypeScript
// ✗ 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])

See also

  • Mesh — assigning materials (mesh.material, setMaterial(index))
  • Texture — loading maps, wrap modes, video textures
  • Light — what lit materials respond to
  • SceneocclusionMaterial, setMaterialGlobalParameter for custom shaders