Advancedcolor'display': the portal only multiplies its feed by its tint, so it can skip the decode and encode
The values the material's shader reads, one property per field its bits declare; see MaterialData for how they read and write
Whether destroy() has run
ReadonlydoubleWhether back faces are drawn too, lit with their normal flipped; set by the doubleSided option
Readonly AdvancedextraThe 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.
ReadonlyflatWhether triangles are shaded with their face normal; set by the flatShading option
ReadonlyfogOptionallabelName for debugging and for finding the material, such as the glTF material name the loader gives it; a Model3DOptions#materials record matches it
Advancedlitfalse: the feed is a frame the view has already lit
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.
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().
Optional ReadonlytopologyPrimitive topology for every pass that doesn't set its own; set by the topology option, and PassDescriptor lists what overrides it
AdvanceduidUnique id for this material instance
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.
Sets the base opacity
From 0 (transparent) to 1 (opaque)
Alpha below which fragments are discarded while BaseMaterial3D#alphaMode is 'mask'
Sets the alpha cutoff
Alpha below which fragments are discarded
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.
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.
Sets the base colour tint
Display colour; its alpha is ignored
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.
Sets the UV transform
The transform, or undefined to follow the base colour texture's frame
StaticEMPTYAdvanced
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.
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.
Optionaloptions: PortalMaterialOptions
Constructor options to change on the copy; left out, it's a plain copy.
The copy, of the same class.
Frees the material for good; a second call does nothing
Its textures stay alive unless options asks for them. The engine's default textures are never
destroyed, and compiled shaders stay cached in the renderer.
Optionaloptions: TypeOrBool<TextureDestroyOptions>
{ texture: true } destroys the material's textures; add textureSource: true,
or pass true, to destroy their sources too
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.
The pass's name, such as '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.
The pass's name, such as 'outline'.
Advanced
Returns the shader descriptor for the material's main pass
The base returns the descriptor the material was built with. Override it when the bits depend
on the material's settings or on the view, and call _invalidateDescriptor when a setting
changes the answer. Bits that declare material fields or textures must also go through
_deriveLayoutFromBits in the constructor.
The view about to render this material.
class MyWaterMaterial extends BaseMaterial3D {
private readonly _plain = new ShaderDescriptor({ bitsGpu: [waterBit], bitsGl: [waterBit] });
private readonly _reflective = new ShaderDescriptor({
bitsGpu: [waterBit, skyReflectionBit],
bitsGl: [waterBit, skyReflectionBit],
});
constructor() {
super();
this._deriveLayoutFromBits([waterBit]);
}
public override getPrimaryDescriptor(view: View3D): ShaderDescriptor {
return view.environment ? this._reflective : this._plain;
}
}
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.
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.
Unlit surface that shows a render-texture feed at the viewer's own screen position
Each fragment samples the feed where it sits on screen, so the far side holds still as the viewer moves and the quad reads as an opening rather than a picture. Pair it with a PortalPass, which draws the feed from behind the exit portal. The material needs nothing from that pass but the texture, so a planar mirror's feed works too.
The feed shows as the view drew it: unlit, and not tone mapped a second time as long as its format suits the view (see PortalMaterialOptions#texture). The view's
exposurestill scales it, on top of the exposure in the feed. A portal is an opening only from the side it faces, so leavedoubleSidedoff.On WebGL, a View3D drawn into a render texture, inside a pixi filter or by
renderer.extract, shows the far side upside down.Example