Skip to content

材质与纹理系统

材质与纹理系统定义了渲染对象的表面外观属性。材质控制对象如何响应光照、透明度、混合和深度测试。纹理提供材质可以采样的图像数据。该系统通过编译过程将高级材质定义桥接到低级着色器程序,该编译过程根据材质特性生成 GLSL 代码。

关于材质如何在渲染管线中使用的详细信息,请参阅 WebGL 渲染管线。关于着色器程序编译的详细信息,请参阅 着色器程序与编译。关于应用材质的几何体数据,请参阅 几何体系统


材质基类

Materialsrc/materials/Material.js17-660 定义了所有材质类型的抽象基类。它提供了所有渲染通用的属性:混合模式、剔除、深度测试、模板操作和裁剪平面。

核心材质属性

属性类别属性用途
混合blending, blendSrc, blendDst, blendEquation控制对象颜色如何与帧缓冲区混合
剔除side, shadowSide确定渲染哪些面
深度depthTest, depthWrite, depthFunc管理深度缓冲区操作
模板stencilWrite, stencilFunc, stencilRef控制模板缓冲区操作
透明度transparent, opacity, alphaTest, alphaHash管理透明度渲染
裁剪clippingPlanes, clipIntersection, clipShadows定义裁剪平面行为

材质状态标志

Material.needsUpdate     // 触发重新编译
Material.version         // 处置时递增
Material.isMaterial      // 类型测试标志

基类还定义了回调挂钩:

来源: src/materials/Material.js1-660


纹理系统

Texture 类架构

SVG
100%

纹理属性

Texturesrc/textures/Texture.js32-600 封装了图像数据和采样参数:

核心纹理属性:

SVG
100%

纹理更新机制:

纹理使用 needsUpdate 标志来触发 GPU 上传:

texture.needsUpdate = true;  // 标记纹理以重新上传

纹理矩阵将 offset、repeat、rotation 和 center 组合成单个 Matrix3 变换 src/textures/Texture.js458-482

内置材质类型

Three.js 在 ShaderLib src/renderers/shaders/ShaderLib.js9-355 中提供材质类型。每种材质类型映射到编译期间使用的着色器 ID。

材质类型映射

SVG
100%

材质特性支持

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

材质到着色器的编译管线

编译管线将高级材质定义转换为缓存的着色器程序。WebGLPrograms 类编排此过程。

编译流程图

SVG
100%

参数对象结构

getParameters() 函数 src/renderers/webgl/WebGLPrograms.js50-380 生成一个驱动着色器生成的全面参数对象:

纹理特性检测:

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 通道分配:

对于每个纹理,系统通过 getChannel() src/renderers/webgl/WebGLPrograms.js40-48 确定使用哪个 UV 属性:

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

通道值 (0-3) 映射到 UV 属性(uvuv1uv2uv3)。

材质属性标志:

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;

着色器前缀生成

WebGLProgram 构造函数 src/renderers/webgl/WebGLProgram.js412-1025 根据参数生成包含 #define 语句的着色器前缀:

顶点着色器前缀示例:

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

生成过程在顶点着色器的 src/renderers/webgl/WebGLProgram.js473-669 和片元着色器的 src/renderers/webgl/WebGLProgram.js671-782 中发生。

Include 解析:

着色器代码使用 #include <chunk_name> 指令 src/renderers/webgl/WebGLProgram.js243-276,这些指令从 ShaderChunk 递归解析:

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

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

材质 Uniform 和更新

Uniform 结构

材质通过 UniformsLib src/renderers/shaders/UniformsLib.js1-256 集合定义 uniform,每种材质类型单独合并:

SVG
100%

Uniform 更新过程

WebGLMaterials.refreshUniforms() 函数 src/renderers/webgl/WebGLMaterials.js9-458 在渲染之前更新材质 uniform:

纹理 Uniform 更新:

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

每种纹理类型都有更新路径 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 );
}

材质属性更新:

标准材质属性 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;
}

物理材质扩展:

MeshPhysicalMaterial 添加额外的 uniform 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 );
}

材质序列化

JSON 导出格式

Material.toJSON() 方法 src/materials/Material.js627-658 将材质序列化为 JSON,存储纹理的 UUID:

{
    "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 解析

MaterialLoadersrc/loaders/MaterialLoader.js43-380 从 JSON 重建材质:

类型实例化:

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

属性分配:

加载器 src/loaders/MaterialLoader.js111-380 分配属性并进行类型特定处理:

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 );
}

纹理引用解析:

纹理从预加载的纹理字典 src/loaders/MaterialLoader.js113-125 解析:

const textures = this.textures;

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

ObjectLoader 通过首先解析纹理 src/loaders/ObjectLoader.js207-208 然后解析材质 src/loaders/ObjectLoader.js208 来协调此过程。

材质更新生命周期

触发条件

当设置 needsUpdate 时,材质触发重新编译:

material.needsUpdate = true;  // Forces shader recompilation

这会在以下情况自动发生:

版本跟踪

材质使用版本号进行缓存失效 src/materials/Material.js69-75:

this.version = 0;

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

渲染器跟踪材质版本以检测何时需要 uniform 更新。