@pixi/3d
    Preparing search index...

    Class Material3D

    The standard physically based (PBR) material

    Base colour, metalness and roughness, with normal, occlusion and emissive maps, plus the glTF extensions for specular, iridescence, transmission and volume, clearcoat, sheen and anisotropy. glTF materials load into it, except unlit ones, which load as FlatMaterial.

    Each extension is an option group, and passing one gives the material a runtime object of the same name: new Material3D({ clearcoat: { roughness: 0.2 } }) gives material.clearcoat.roughness. Writing to it, or to a core value such as roughness, never recompiles the shader.

    The groups passed to the constructor fix the material's features for good. A group left out costs nothing, while a feature turned off at runtime, such as a coat at factor 0, keeps its cost. To add a feature to a material that exists, such as one the glTF loader built, material.clone({ clearcoat: { factor: 1 } }) (BaseMaterial3D#clone) makes a copy with it and everything else the material has.

    Custom shader code in BaseMaterial3DOptions#extraBits runs after the features in each slot, and in the material slot before the 'mask' cutoff, so it sees the final albedo and alpha, and alpha it writes there still reaches the cutoff. Like the groups, it's fixed when the material is built, and clone({ extraBits }) makes a copy with more.

    const paint = new Material3D({
    baseColor: 0x8c1c13,
    metallic: 0.2,
    roughness: 0.5,
    clearcoat: { roughness: 0.05 },
    });
    paint.clearcoat!.factor = 0.8;

    Hierarchy (View Summary)

    Index
    anisotropy?: AnisotropyProps

    Runtime anisotropy controls; present only when the material was built with an anisotropy group

    if (material.anisotropy) material.anisotropy.rotation = Math.PI / 2;
    
    clearcoat?: ClearcoatProps

    Runtime clearcoat controls; present only when the material was built with a clearcoat group

    if (material.clearcoat) material.clearcoat.roughness = 0.1;
    
    colorSpace: ColorSpace = 'linear'

    The colour space this material's shader works in; see ColorSpace

    'display' takes effect only on an unlit material, in a view with toneMapping: 'none' and no enabled 'beforeToneMapping' effect, on a mesh no fog or vertex colours reach. Anywhere else the shader works in linear light. To change it after the material has rendered, see BaseMaterial3D.

    Bits on a 'display' material should read colours through these helpers, which exist in both shader stages and return the colour in the shader's space:

    • decodeColor(c: vec3): a display colour, such as a texture sample.
    • inputTint(linearTint: vec4, displayTint: vec4): pass material.baseColor and material.displayBaseColor, which the material fills once a bit declares them in bindings.material.
    • decodeFrame(c: vec3): a colour from a frame this view drew, such as a render-texture feed.

    Multiplying colours read this way gives the same bytes in both spaces. Adding, blending or scaling them by a number doesn't, so the result changes when fog moves the shader to linear light. On the display path, a SceneOutput bit that reads albedo sees display colours.

    'linear'
    
    class MyStickerMaterial extends BaseMaterial3D {
    public override colorSpace: ColorSpace = 'display';
    public override lit = false;
    }

    The values the material's shader reads, one property per field its bits declare; see MaterialData for how they read and write

    const toon = new ToonMaterial({ bands: 3 });
    toon.data.bands = 5;
    destroyed: boolean = false

    Whether destroy() has run

    doubleSided: boolean

    Whether back faces are drawn too, lit with their normal flipped; set by the doubleSided option

    emissive?: EmissiveProps

    Runtime glow controls; present only when the material was built with an emissive group

    if (material.emissive) material.emissive.strength = 2;
    
    extraBits: readonly ShaderBit[]

    The shader bits this material was built with on top of its own, from the extraBits option

    Fixed for the material's lifetime. BaseMaterial3D#clone makes a copy with more.

    const dissolving = materials.filter((material) => material.extraBits.includes(dissolveBit));
    
    flatShading: boolean

    Whether triangles are shaded with their face normal; set by the flatShading option

    const rock = new Material3D({ baseColor: 0x8a7f72, flatShading: true });
    
    fog: boolean

    Whether the view's fog may reach this material; set by the fog option

    const sky = new Material3D({ baseColorTexture: skyTexture, fog: false });
    
    iridescence?: IridescenceProps

    Runtime iridescence controls; present only when the material was built with an iridescence group

    if (material.iridescence) material.iridescence.factor = 1;
    
    label?: string

    Name for debugging and for finding the material, such as the glTF material name the loader gives it; a Model3DOptions#materials record matches it

    const paint = source.materials.filter((material) => material.label === 'Paint');
    
    lit: boolean = true

    Whether the material's shader does lighting

    Set it to false for a shader that does none, as FlatMaterial does. That leaves out calculateShadow, so a shader that calls it fails to compile. To change it after the material has rendered, see BaseMaterial3D.

    true
    
    class MyUnlitMaterial extends BaseMaterial3D {
    public override lit = false;
    }
    sheen?: SheenProps

    Runtime sheen controls; present only when the material was built with a sheen group

    if (material.sheen) material.sheen.roughness = 0.4;
    
    specular?: SpecularProps

    Runtime specular controls; present only when the material was built with a specular group

    if (material.specular) material.specular.factor = 0.5;
    
    state: State

    Render state (blending, depth, culling) for every draw with this material

    alphaMode and doubleSided set parts of it. To change a field after the material has rendered, see BaseMaterial3D. polygonOffset works on WebGL only; WebGPU ignores it.

    // 'blend' draws it after the opaque meshes, so with no depth test it shows through them
    const overlay = new FlatMaterial({ baseColor: 0xffcc00, alphaMode: 'blend' });
    overlay.state.depthTest = false;

    The material's textures by slot name, the same names the textures option takes

    Assigning swaps the slot, and a name the material doesn't have throws. A swap reuses the compiled shader, except that a real map in the normal slot, or the default put back, switches normal mapping on or off.

    Wrap mode, filtering and anisotropy come from each texture's source.style, which clamps by default. To tile a texture, set texture.source.style.addressMode = 'repeat', then call style.update().

    material.textures.baseColor = crateTexture;
    
    topology?: Topology

    Primitive topology for every pass that doesn't set its own; set by the topology option, and PassDescriptor lists what overrides it

    transmission?: TransmissionProps

    Runtime transmission controls; present only when the material was built with a transmission group

    if (material.transmission) material.transmission.factor = 0.9;
    
    uid: number = ...

    Unique id for this material instance

    vertexColors: boolean = false

    Whether a geometry's vertex colours (its colors) tint this material

    Off by default, since exporters often fill the colour set with masks or weights; put data that isn't colour in colors1 when you can. The glTF loader turns it on, as glTF's COLOR_0 is a base colour multiplier in linear light; convert colours picked in sRGB with srgbToLinear. To change it after the material has rendered, see BaseMaterial3D.

    false
    
    const painted = new Material3D();
    painted.vertexColors = true;
    • get alpha(): number

      Base opacity, from 0 to 1; alphaMode: 'opaque' ignores it

      Returns number

      1
      
      material.alphaMode = 'blend';
      material.alpha = 0.5;
    • set alpha(value: number): void

      Sets the base opacity

      Parameters

      • value: number

        From 0 (transparent) to 1 (opaque)

      Returns void

    • get alphaCutoff(): number

      Alpha below which fragments are discarded while BaseMaterial3D#alphaMode is 'mask'

      Returns number

      0.5
      
      material.alphaMode = 'mask';
      material.alphaCutoff = 0.3;
    • set alphaCutoff(value: number): void

      Sets the alpha cutoff

      Parameters

      • value: number

        Alpha below which fragments are discarded

      Returns void

    • get alphaMode(): AlphaMode

      How the material treats alpha; see AlphaMode

      Setting it also sets the blending and depth writes of state. 'blend' turns depth writes off, and its meshes cast no shadow. On a material built from a raw descriptor, 'mask' discards only if the descriptor's bits include alphaMaskBit.

      Returns AlphaMode

      material.alphaMode = 'blend';
      material.alpha = 0.5;
    • set alphaMode(value: AlphaMode): void

      Sets the alpha mode

      Parameters

      Returns void

    • get baseColor(): number

      Base colour tint, read back as a hex number

      Takes any ColorSource in display (sRGB) space, like Container.tint. The source's alpha is ignored; BaseMaterial3D#alpha is the opacity.

      Returns number

      material.baseColor = 'tomato';
      material.baseColor; // 0xff6347
    • set baseColor(value: ColorSource): void

      Sets the base colour tint

      Parameters

      • value: ColorSource

        Display colour; its alpha is ignored

      Returns void

    • get ior(): number

      Index of refraction (KHR_materials_ior); sets how strongly non-metals reflect, and how far transmission bends light

      Returns number

      1.5
      
      material.ior = 1.33;
      
    • set ior(value: number): void

      Sets the index of refraction

      Parameters

      • value: number

        Refractive index; 1.5 suits most glass and plastic

      Returns void

    • get metallic(): number

      Metalness, from 0 (non-metal) to 1 (metal)

      Ambient lights don't light a metal, so with no environment a metal is dark except for its highlights.

      Returns number

      0
      
      material.metallic = 1;
      
    • set metallic(value: number): void

      Sets the metalness

      Parameters

      • value: number

        Metalness, 0 to 1

      Returns void

    • get normalScale(): number

      Strength of the normal map's bumps; 1 leaves the map as authored

      Returns number

      1
      
      material.normalScale = 2;
      
    • set normalScale(value: number): void

      Sets the strength of the normal map's bumps

      Parameters

      • value: number

        Bump strength

      Returns void

    • get occlusionStrength(): number

      Multiplier on the occlusion

      Ambient and environment light are scaled by the sampled occlusion times this value, so values below 1 darken that light even where the map is white, and values above 1 brighten it.

      Returns number

      1
      
      material.occlusionStrength = 0.8;
      
    • set occlusionStrength(value: number): void

      Sets the multiplier on the occlusion

      Parameters

      • value: number

        Multiplier on the occlusion

      Returns void

    • get roughness(): number

      Perceptual roughness, from 0 (mirror-like) to 1 (fully rough)

      Returns number

      1
      
      material.roughness = 0.2;
      
    • set roughness(value: number): void

      Sets the perceptual roughness

      Parameters

      • value: number

        Roughness, 0 to 1

      Returns void

    • get uvTransform(): Matrix | undefined

      The UV transform you set, or undefined while the base colour texture's frame sets it

      One transform, as glTF's KHR_texture_transform defines it, applies to every texture of the material. Setting a matrix takes over from a framed (spritesheet) base colour texture, and a framed texture assigned while it's set warns that its frame is ignored; setting undefined hands it back. Either is a data write, never a shader rebuild. On a material whose bits lack uvTransformBit, setting a matrix warns and does nothing.

      A UV (u, v) becomes (a * u + b * v + tx, c * u + d * v + ty), transposed from pixi's Matrix.apply, so new Matrix().rotate(rotation).scale(sx, sy).translate(x, y) gives the KHR_texture_transform result for those values. The getter returns the material's own matrix; changing it in place does nothing until you assign it again.

      Returns Matrix | undefined

      material.uvTransform = new Matrix().scale(4, 4);
      material.uvTransform = undefined;
    • set uvTransform(m: Matrix | undefined): void

      Sets the UV transform

      Parameters

      • m: Matrix | undefined

        The transform, or undefined to follow the base colour texture's frame

      Returns void

    • get EMPTY(): BaseMaterial3D
      Advanced

      A shared material with no shader, which a Mesh3D holds until it's given a material

      A mesh on it draws nothing and compiles no shader, though its skeleton and morph targets still upload. Assign it to a mesh's material to stop the mesh drawing; to hide a mesh for a while, visible = false is cheaper. Don't give it to a BatchedMesh3D slot that's in a pass, even a hidden one: such a slot needs a real material.

      Every waiting mesh shares it, so don't edit it. Its destroy does nothing, as with pixi's Texture.EMPTY.

      Returns BaseMaterial3D

      if (mesh.material === BaseMaterial3D.EMPTY) mesh.material = new Material3D();
      
    • Makes a material of the same class that draws as this one does now, with the options you pass changed

      options are the class's constructor options. clone reads this material's current values back as those options, merges yours over them, and passes the result to the class's constructor. An option left out, or set to undefined, keeps this material's value. One that takes an object, such as a feature group or passes, is merged one level deep: emissive: { strength: 2 } on a Material3D keeps its emissive colour. descriptor is replaced whole. A group the material doesn't have, such as clearcoat, adds the feature.

      extraBits adds to this material's BaseMaterial3D#extraBits, skipping a bit it already has. Fields a new bit declares start at 0 and its texture slots hold Texture.WHITE, so set them on the copy. The fields of the extra bits it already had keep their values.

      A raw-descriptor material, or any class that inherits BaseMaterial3D's _toOptions unchanged, keeps every field value and texture it has, except those an option you pass sets.

      What isn't an option comes across too: colorSpace, lit, the passes joined with BaseMaterial3D#enablePass, and state with any change made through it, except the flags an option you pass sets, such as alphaMode's blending. Colour options read back as 8-bit hex, as their getters return them.

      Textures are shared, not copied. Everything else belongs to the copy: assign it to meshes and destroy() it when you're done, which leaves the shared textures alive. Materials with the same bits share compiled shaders, so a copy compiles nothing new unless its bits are new.

      Parameters

      • Optionaloptions: Material3DOptions

        Constructor options to change on the copy; left out, it's a plain copy.

      Returns this

      The copy, of the same class.

      const dissolving = mesh.material.clone({ extraBits: [dissolveBit] });
      dissolving.data.dissolve = 0.5;
      mesh.material = dissolving;

      const ghost = mesh.material.clone({ alphaMode: 'blend', alpha: 0.4 });
    • Removes this material from a custom render pass

      Meshes using the material leave the pass on the next render, except one that joined it through Mesh3D#enablePass. An override given for the pass in the passes option is kept, so BaseMaterial3D#enablePass brings it back.

      Parameters

      • name: string

        The pass's name, such as 'outline'.

      Returns void

      material.disablePass('outline');
      
    • Adds this material to a custom render pass, such as an OutlinePass, as naming it true in the passes option does

      Meshes using the material draw in the pass from the next render, except one that left it through Mesh3D#disablePass. BatchedMesh3D slots take their passes from BatchedMesh3D#setPassesAt instead. Overrides for the pass can only be given at construction, and the passes option has the limit on custom pass names.

      Parameters

      • name: string

        The pass's name, such as 'outline'.

      Returns void

      view.passes = [new OutlinePass()];
      material.enablePass('outline');
    • Frees the GPU memory of this material's textures until a frame draws them again

      The next frame that draws the material re-uploads them from their CPU copies, so it keeps drawing the same. Texture sources are shared, so every other material using one re-uploads it too, as with texture.source.unload(). The engine's default textures are skipped, and so are render textures and ContainerTextures, whose pixels exist only on the GPU.

      Call BatchedMesh3D#invalidate for each batch slot that draws the material. Does nothing once the material is destroyed.

      Returns void

      view.root.removeChild(model);
      material.unload(); // hand the texture memory back until the model is shown again

      Model3D.unload For giving back a whole model's geometry and textures at once.

    • Queues this material's data for upload

      Property setters and data field assignments call it for you. Call it yourself after writing into a data vector in place. Calls in one frame upload once.

      Returns void

      const scroll = material.data.scrollOffset;
      scroll[0] += 0.01;
      material.update();