Skip to content

Material & Texture System

The Material & Texture System defines surface appearance properties for rendered objects. Materials control how objects respond to lighting, transparency, blending, and depth testing. Textures provide image data that materials can sample. The system bridges high-level material definitions to low-level shader programs through a compilation process that generates GLSL code based on material features.

For information about how materials are used in the rendering pipeline, see WebGL Rendering Pipeline. For shader program compilation details, see Shader Programs & Compilation. For geometry data that materials are applied to, see Geometry System.


Material Base Class

The Material class src/materials/Material.js17-660 defines the abstract base for all material types. It provides properties common to all rendering: blending modes, culling, depth testing, stencil operations, and clipping planes.

Core Material Properties

Property CategoryPropertiesPurpose
Blendingblending, blendSrc, blendDst, blendEquationControls how object colors blend with framebuffer
Cullingside, shadowSideDetermines which face sides render
DepthdepthTest, depthWrite, depthFuncManages depth buffer operations
StencilstencilWrite, stencilFunc, stencilRefControls stencil buffer operations
Transparencytransparent, opacity, alphaTest, alphaHashManages transparency rendering
ClippingclippingPlanes, clipIntersection, clipShadowsDefines clip plane behavior

Material State Flags

Material.needsUpdate     // Triggers recompilation
Material.version         // Increments on disposal
Material.isMaterial      // Type testing flag

The base class also defines callback hooks:

Sources: src/materials/Material.js1-660


Texture System

Texture Class Architecture

SVG
100%

Texture Properties

The Texture class src/textures/Texture.js32-600 encapsulates image data and sampling parameters:

Core Texture Properties:

SVG
100%

Texture Update Mechanism:

Textures use a needsUpdate flag to trigger GPU upload:

texture.needsUpdate = true;  // Marks texture for re-upload

The texture matrix combines offset, repeat, rotation, and center into a single Matrix3 transform src/textures/Texture.js458-482

Built-in Material Types

Three.js provides material types in ShaderLib src/renderers/shaders/ShaderLib.js9-355 Each material type maps to a shader ID used during compilation.

Material Type Mapping

SVG
100%

Material Feature Support

Material TypeLightingPBRTexturesSpecial Features
MeshBasicMaterialNoNomap, alphaMap, envMap, aoMap, lightMap, specularMapSimple unlit rendering
MeshLambertMaterialYes (Lambertian)No+ emissiveMap, bumpMap, normalMap, displacementMapDiffuse lighting
MeshPhongMaterialYes (Blinn-Phong)No+ specularMapSpecular highlights, shininess
MeshStandardMaterialYesYes+ roughnessMap, metalnessMapMetallic-roughness PBR
MeshPhysicalMaterialYesYesAll standard + clearcoat, transmission, sheen, iridescence mapsExtended PBR features
MeshToonMaterialYesNo+ gradientMapCel-shaded rendering
MeshMatcapMaterialNoNomatcapMaterial capture lookup

Material-to-Shader Compilation Pipeline

The compilation pipeline transforms high-level material definitions into cached shader programs. The WebGLPrograms class orchestrates this process.

Compilation Flow Diagram

SVG
100%

Parameter Object Structure

The getParameters() function src/renderers/webgl/WebGLPrograms.js50-380 generates a comprehensive parameters object that drives shader generation:

Texture Feature Detection:

const HAS_MAP = !! material.map;
const HAS_MATCAP = !! material.matcap;
const HAS_ENVMAP = !! envMap;
const HAS_AOMAP = !! material.aoMap;
const HAS_LIGHTMAP = !! material.lightMap;
const HAS_BUMPMAP = !! material.bumpMap;
const HAS_NORMALMAP = !! material.normalMap;
const HAS_DISPLACEMENTMAP = !! material.displacementMap;
const HAS_EMISSIVEMAP = !! material.emissiveMap;
const HAS_METALNESSMAP = !! material.metalnessMap;
const HAS_ROUGHNESSMAP = !! material.roughnessMap;

UV Channel Assignment:

For each texture, the system determines which UV attribute to use via getChannel() src/renderers/webgl/WebGLPrograms.js40-48:

parameters.mapUv = HAS_MAP && getChannel( material.map.channel );
parameters.alphaMapUv = HAS_ALPHAMAP && getChannel( material.alphaMap.channel );
parameters.normalMapUv = HAS_NORMALMAP && getChannel( material.normalMap.channel );
// ... etc for all texture types

The channel value (0-3) maps to UV attributes (uv, uv1, uv2, uv3).

Material Property Flags:

parameters.flatShading = material.flatShading;
parameters.vertexColors = material.vertexColors;
parameters.vertexTangents = !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY );
parameters.fog = !! fog;
parameters.useFog = material.fog === true;

Shader Prefix Generation

The WebGLProgram constructor src/renderers/webgl/WebGLProgram.js412-1025 generates shader prefixes containing #define statements based on parameters:

Vertex Shader Prefix Example:

precision highp float;
#define SHADER_TYPE MeshStandardMaterial
#define SHADER_NAME MyMaterial
#define USE_MAP
#define MAP_UV uv
#define USE_NORMALMAP
#define NORMALMAP_UV uv
#define USE_TANGENT
#define USE_SKINNING
#define MORPHTARGETS_COUNT 4
// ... uniforms declarations

The generation occurs in src/renderers/webgl/WebGLProgram.js473-669 for vertex shaders and src/renderers/webgl/WebGLProgram.js671-782 for fragment shaders.

Include Resolution:

Shader code uses #include <chunk_name> directives src/renderers/webgl/WebGLProgram.js243-276 which are recursively resolved from ShaderChunk:

function resolveIncludes( string ) {
    return string.replace( includePattern, includeReplacer );
}

function includeReplacer( match, include ) {
    let string = ShaderChunk[ include ];
    return resolveIncludes( string );  // Recursive
}

Material Uniforms and Updates

Uniform Structure

Materials define uniforms through UniformsLib src/renderers/shaders/UniformsLib.js1-256 collections merged per material type:

SVG
100%

Uniform Update Process

The WebGLMaterials.refreshUniforms() function src/renderers/webgl/WebGLMaterials.js9-458 updates material uniforms before rendering:

Texture Uniform Updates:

function refreshTransformUniform( map, uniform ) {
    if ( map.matrixAutoUpdate === true ) {
        map.updateMatrix();
    }
    uniform.value.copy( map.matrix );
}

Each texture type has an update path src/renderers/webgl/WebGLMaterials.js45-328:

if ( material.map ) {
    uniforms.map.value = material.map;
    refreshTransformUniform( material.map, uniforms.mapTransform );
}
if ( material.normalMap ) {
    uniforms.normalMap.value = material.normalMap;
    uniforms.normalScale.value.copy( material.normalScale );
    refreshTransformUniform( material.normalMap, uniforms.normalMapTransform );
}

Material Property Updates:

Standard material properties src/renderers/webgl/WebGLMaterials.js134-284:

uniforms.opacity.value = material.opacity;

if ( material.color ) {
    uniforms.diffuse.value.copy( material.color );
}

if ( material.emissive ) {
    uniforms.emissive.value.copy( material.emissive )
        .multiplyScalar( material.emissiveIntensity );
}

if ( material.roughness !== undefined ) {
    uniforms.roughness.value = material.roughness;
}
if ( material.metalness !== undefined ) {
    uniforms.metalness.value = material.metalness;
}

Physical Material Extensions:

MeshPhysicalMaterial adds additional uniforms src/renderers/webgl/WebGLMaterials.js286-414:

uniforms.clearcoat.value = material.clearcoat;
uniforms.clearcoatRoughness.value = material.clearcoatRoughness;

if ( material.iridescence > 0 ) {
    uniforms.iridescence.value = material.iridescence;
    uniforms.iridescenceIOR.value = material.iridescenceIOR;
    // ... thickness min/max
}

if ( material.sheen > 0 ) {
    uniforms.sheenColor.value.copy( material.sheenColor )
        .multiplyScalar( material.sheen );
    uniforms.sheenRoughness.value = material.sheenRoughness;
}

if ( material.transmission > 0 ) {
    uniforms.transmission.value = material.transmission;
    uniforms.thickness.value = material.thickness;
    uniforms.attenuationDistance.value = material.attenuationDistance;
    uniforms.attenuationColor.value.copy( material.attenuationColor );
}

Material Serialization

JSON Export Format

The Material.toJSON() method src/materials/Material.js627-658 serializes materials to JSON, storing UUIDs for textures:

{
    "uuid": "F3D1E2A4-B5C6-7D8E-9F0A-1B2C3D4E5F6A",
    "type": "MeshStandardMaterial",
    "name": "MyMaterial",
    "color": 16777215,          // 0xffffff
    "roughness": 0.5,
    "metalness": 0.8,
    "map": "texture-uuid-1",
    "normalMap": "texture-uuid-2",
    "normalScale": [1, 1],
    "emissive": 0,
    "emissiveIntensity": 1.0,
    "envMapIntensity": 1.0,
    "side": 0,                  // FrontSide
    "transparent": false,
    "opacity": 1.0,
    "depthTest": true,
    "depthWrite": true
}

MaterialLoader Parsing

The MaterialLoader class src/loaders/MaterialLoader.js43-380 reconstructs materials from JSON:

Type Instantiation:

createMaterialFromType( type ) {
    const materialClass = {
        'MeshBasicMaterial': MeshBasicMaterial,
        'MeshLambertMaterial': MeshLambertMaterial,
        'MeshPhongMaterial': MeshPhongMaterial,
        'MeshStandardMaterial': MeshStandardMaterial,
        'MeshPhysicalMaterial': MeshPhysicalMaterial,
        // ... etc
    }[ type ];
    
    return new materialClass();
}

Property Assignment:

The loader src/loaders/MaterialLoader.js111-380 assigns properties with type-specific handling:

if ( json.color !== undefined ) {
    material.color.setHex( json.color );
}
if ( json.roughness !== undefined ) {
    material.roughness = json.roughness;
}
if ( json.map !== undefined ) {
    material.map = getTexture( json.map );  // Looks up by UUID
}
if ( json.normalScale !== undefined ) {
    material.normalScale.fromArray( json.normalScale );
}

Texture Reference Resolution:

Textures are resolved from a pre-loaded texture dictionary src/loaders/MaterialLoader.js113-125:

const textures = this.textures;

function getTexture( name ) {
    if ( textures[ name ] === undefined ) {
        warn( 'MaterialLoader: Undefined texture', name );
    }
    return textures[ name ];
}

The ObjectLoader coordinates this by parsing textures first src/loaders/ObjectLoader.js207-208 then materials src/loaders/ObjectLoader.js208

Material Update Lifecycle

Trigger Conditions

Materials trigger recompilation when needsUpdate is set:

material.needsUpdate = true;  // Forces shader recompilation

This occurs automatically when:

Version Tracking

Materials use version numbers for cache invalidation src/materials/Material.js69-75:

this.version = 0;

dispose() {
    this.dispatchEvent( { type: 'dispose' } );
    this.version++;  // Invalidates cached programs
}

Renderers track material versions to detect when uniform updates are needed.