{T}

材质进阶

深入探讨 Three.js 的高级材质技术,包括 PBR 材质工作流、自定义着色器材质、材质动画等高级主题。

概述

在掌握了基础材质类型后,本章节将介绍更高级的材质应用,包括物理渲染材质的深度应用、自定义着色器编程、以及材质的高级特效。

PBR 材质工作流

理解 PBR(Physically Based Rendering)

PBR 是一种基于物理的渲染方法,能够产生更真实、更一致的光照效果。相比传统渲染,PBR 具有以下优势:

核心优势

  • 物理准确性:基于真实物理定律的光照计算
  • 能量守恒:反射光能量不会超过入射光能量
  • 材质一致性:在不同光照环境下保持相似的外观
  • 工作流标准化:便于团队协作和资产复用

PBR 核心原理

  1. 能量守恒定律

    javascript
    // 反射光 + 漫反射 ≤ 入射光
    // Three.js 自动处理能量守恒
    const material = new THREE.MeshStandardMaterial({
      metalness: 0.5,  // 金属度越高,漫反射越少
      roughness: 0.5   // 粗糙度影响反射分布
    });
  2. 微表面理论

    • 粗糙表面:光线向多个方向散射
    • 光滑表面:光线集中反射
  3. 菲涅尔效应

    • 视角越倾斜,反射越强
    • Three.js 在 MeshPhysicalMaterial 中自动计算
javascript
import * as THREE from 'three';

// PBR 材质核心属性
const pbrMaterial = new THREE.MeshStandardMaterial({
  // 基础属性
  color: 0xffffff,           // 漫反射颜色
  metalness: 0.0,            // 金属度(0-1)
  roughness: 0.5,            // 粗糙度(0-1)
  
  // 法线
  normalMap: null,           // 法线贴图
  normalScale: new THREE.Vector2(1, 1),
  
  // 自发光
  emissive: 0x000000,        // 自发光颜色
  emissiveIntensity: 1.0,
  emissiveMap: null,
  
  // 环境光遮蔽
  aoMap: null,
  aoMapIntensity: 1.0,
  
  // 环境贴图
  envMap: null,
  envMapIntensity: 1.0
});

金属度工作流(Metalness Workflow)

金属度工作流是 Three.js 使用的 PBR 标准工作流,通过金属度(Metalness)和粗糙度(Roughness)两个核心参数控制材质外观。

金属度(Metalness)

金属度定义了材质的金属特性,范围 0-1:

金属度值材质类型特性典型应用
0.0非金属(电介质)有漫反射,无金属反射塑料、木材、布料
0.0-0.2半金属混合混合特性特殊材质
0.8-1.0金属无漫反射,金属反射金、银、铜、铁
javascript
// 非金属(电介质)材质示例
const dielectricMaterial = new THREE.MeshStandardMaterial({
  color: 0xcccccc,      // 漫反射颜色
  metalness: 0.0,       // 非金属
  roughness: 0.5        // 中等粗糙度
});

// 金属材质示例
const metalMaterial = new THREE.MeshStandardMaterial({
  color: 0xffd700,      // 金色
  metalness: 1.0,       // 完全金属
  roughness: 0.2        // 光滑表面
});

// 不同金属材质配置表
const metalPresets = {
  gold: {
    color: 0xffd700,
    metalness: 1.0,
    roughness: 0.3,
    description: '黄金'
  },
  silver: {
    color: 0xc0c0c0,
    metalness: 1.0,
    roughness: 0.2,
    description: '白银'
  },
  copper: {
    color: 0xb87333,
    metalness: 1.0,
    roughness: 0.4,
    description: '铜'
  },
  aluminum: {
    color: 0xd4d4d4,
    metalness: 1.0,
    roughness: 0.5,
    description: '铝合金'
  },
  iron: {
    color: 0x8c8c8c,
    metalness: 1.0,
    roughness: 0.6,
    description: '铁'
  }
};

// 创建预设材质
const metals = {};
for (const [name, config] of Object.entries(metalPresets)) {
  metals[name] = new THREE.MeshStandardMaterial(config);
}

粗糙度(Roughness)

粗糙度定义了表面的光滑程度,范围 0-1:

粗糙度值表面特性反射效果典型应用
0.0完全光滑镜面反射镜子、抛光金属
0.2-0.4光滑清晰反射汽车漆面、光滑塑料
0.5中等柔和反射磨砂金属
0.7-0.9粗糙模糊反射粗糙石材
1.0完全粗糙无明显反射混凝土、粗糙布料

粗糙度效果

javascript
// 创建不同粗糙度的球体
for (let i = 0; i <= 10; i++) {
  const material = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    metalness: 0.5,
    roughness: i / 10  // 从光滑到粗糙
  });
  
  const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(0.5, 32, 32),
    material
  );
  sphere.position.x = (i - 5) * 1.2;
  scene.add(sphere);
}

环境贴图

环境贴图对 PBR 材质非常重要,提供反射和间接光照。

javascript
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

// 加载 HDR 环境贴图
const rgbeLoader = new RGBELoader();
rgbeLoader.load('environment.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;
  
  // 设置场景环境
  scene.environment = texture;
  scene.background = texture;
});

// 或者使用 CubeTexture
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMap = cubeTextureLoader.load([
  'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'
]);

scene.environment = envMap;

// 材质使用环境贴图
const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  metalness: 1.0,
  roughness: 0.0,
  envMap: envMap,
  envMapIntensity: 1.0
});

高级贴图技术

法线贴图(Normal Map)

法线贴图模拟表面细节,无需增加几何体复杂度。

javascript
const textureLoader = new THREE.TextureLoader();

const normalMap = textureLoader.load('normal.jpg');

const material = new THREE.MeshStandardMaterial({
  color: 0x888888,
  normalMap: normalMap,
  normalScale: new THREE.Vector2(1, 1),  // 法线强度
  metalness: 0.5,
  roughness: 0.5
});

// 动态调整法线强度
material.normalScale.set(2, 2);

位移贴图(Displacement Map)

位移贴图实际改变几何体顶点位置。

javascript
const displacementMap = textureLoader.load('displacement.jpg');

const material = new THREE.MeshStandardMaterial({
  color: 0x888888,
  displacementMap: displacementMap,
  displacementScale: 0.5,   // 位移强度
  displacementBias: 0,       // 位移偏移
  
  // 需要足够多的顶点才能看到效果
  // 使用带分段的几何体
});

const geometry = new THREE.PlaneGeometry(4, 4, 128, 128);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

环境光遮蔽贴图(AO Map)

模拟环境光在裂缝和角落的遮挡效果。

javascript
const aoMap = textureLoader.load('ao.jpg');

const material = new THREE.MeshStandardMaterial({
  color: 0x888888,
  aoMap: aoMap,
  aoMapIntensity: 1.0,  // AO 强度
  
  // AO 贴图需要第二组 UV 坐标
  // geometry.setAttribute('uv2', geometry.attributes.uv);
});

// 设置第二组 UV
geometry.setAttribute('uv2', geometry.attributes.uv);

自发光贴图(Emissive Map)

实现物体的自发光效果。

javascript
const emissiveMap = textureLoader.load('emissive.jpg');

const material = new THREE.MeshStandardMaterial({
  color: 0x444444,
  emissive: 0xffffff,
  emissiveMap: emissiveMap,
  emissiveIntensity: 2.0
});

// 动态调整自发光强度
material.emissiveIntensity = Math.sin(time) * 0.5 + 1.5;

金属度与粗糙度贴图

javascript
const metalnessMap = textureLoader.load('metalness.jpg');
const roughnessMap = textureLoader.load('roughness.jpg');

const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  metalness: 1.0,
  metalnessMap: metalnessMap,
  roughness: 1.0,
  roughnessMap: roughnessMap
});

// 或者使用 ORM 贴图(Ambient Occlusion + Roughness + Metalness)
const ormMap = textureLoader.load('orm.jpg');
const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  aoMap: ormMap,
  roughnessMap: ormMap,
  metalnessMap: ormMap,
  
  // 设置通道
  aoMapChannel: 'r',
  roughnessMapChannel: 'g',
  metalnessMapChannel: 'b'
});

MeshPhysicalMaterial 高级特性

清漆层(Clearcoat)

模拟车漆、钢琴漆等效果。

javascript
const material = new THREE.MeshPhysicalMaterial({
  color: 0xff0000,
  metalness: 0.0,
  roughness: 0.1,
  
  // 清漆层
  clearcoat: 1.0,              // 清漆强度
  clearcoatRoughness: 0.1,     // 清漆粗糙度
  clearcoatMap: null,          // 清漆贴图
  clearcoatRoughnessMap: null,
  clearcoatNormalMap: null,    // 清漆法线贴图
  clearcoatNormalScale: new THREE.Vector2(1, 1)
});

// 示例:车漆材质
const carPaintMaterial = new THREE.MeshPhysicalMaterial({
  color: 0x1a1a1a,  // 深黑色
  metalness: 0.9,
  roughness: 0.1,
  clearcoat: 1.0,
  clearcoatRoughness: 0.05
});

玻璃与透射

javascript
const glassMaterial = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  metalness: 0,
  roughness: 0,
  
  // 透射
  transmission: 0.9,           // 透光度(0-1)
  thickness: 0.5,              // 厚度
  attenuationDistance: 0.5,    // 光线衰减距离
  attenuationColor: new THREE.Color(0xffffff),  // 衰减颜色
  
  // 折射
  ior: 1.5,                    // 折射率(玻璃约 1.5)
  
  // 需要设置透明
  transparent: true,
  
  // 其他设置
  envMapIntensity: 1.0
});

// 不同材质的折射率
const iorValues = {
  air: 1.0,
  water: 1.33,
  glass: 1.5,
  diamond: 2.42
};

光泽(Sheen)

模拟织物、天鹅绒等材质。

javascript
const velvetMaterial = new THREE.MeshPhysicalMaterial({
  color: 0x800020,  // 酒红色
  metalness: 0,
  roughness: 0.8,
  
  // 光泽
  sheen: 1.0,                   // 光泽强度
  sheenColor: new THREE.Color(0xffffff),  // 光泽颜色
  sheenRoughness: 0.5           // 光泽粗糙度
});

// 天鹅绒材质
const velvetFabric = new THREE.MeshPhysicalMaterial({
  color: 0x4a0080,
  sheen: 1.0,
  sheenColor: new THREE.Color(0xff80ff),
  sheenRoughness: 0.3
});

镜面反射(Specular Intensity)

javascript
const material = new THREE.MeshPhysicalMaterial({
  color: 0x00ff00,
  metalness: 0,
  roughness: 0.2,
  
  // 镜面反射
  specularIntensity: 1.0,       // 镜面强度
  specularColor: new THREE.Color(0xffffff),  // 镜面颜色
  specularColorMap: null,
  specularIntensityMap: null
});

自定义着色器材质

基础着色器材质

javascript
const vertexShader = `
  varying vec2 vUv;
  varying vec3 vNormal;
  varying vec3 vPosition;
  
  void main() {
    vUv = uv;
    vNormal = normalize(normalMatrix * normal);
    vPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const fragmentShader = `
  uniform float time;
  uniform vec3 color;
  
  varying vec2 vUv;
  varying vec3 vNormal;
  varying vec3 vPosition;
  
  void main() {
    // 简单的渐变效果
    float gradient = sin(vUv.x * 10.0 + time) * 0.5 + 0.5;
    vec3 finalColor = color * gradient;
    
    // 添加简单的漫反射光照
    vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
    float diffuse = max(dot(vNormal, lightDir), 0.0);
    
    gl_FragColor = vec4(finalColor * (0.3 + 0.7 * diffuse), 1.0);
  }
`;

const material = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 },
    color: { value: new THREE.Color(0x00ff00) }
  },
  vertexShader: vertexShader,
  fragmentShader: fragmentShader
});

使用纹理的着色器

javascript
const material = new THREE.ShaderMaterial({
  uniforms: {
    map: { value: texture },
    time: { value: 0 },
    distortion: { value: 0.1 }
  },
  vertexShader: `
    varying vec2 vUv;
    uniform float time;
    uniform float distortion;
    
    void main() {
      vUv = uv;
      
      // 顶点扭曲
      vec3 pos = position;
      pos.x += sin(pos.y * 5.0 + time) * distortion;
      pos.z += cos(pos.x * 5.0 + time) * distortion;
      
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D map;
    uniform float time;
    
    varying vec2 vUv;
    
    void main() {
      // UV 动画
      vec2 uv = vUv;
      uv.x += sin(uv.y * 10.0 + time) * 0.05;
      
      vec4 texColor = texture2D(map, uv);
      gl_FragColor = texColor;
    }
  `
});

高级着色器效果

javascript
// 菲涅尔效果
const fresnelMaterial = new THREE.ShaderMaterial({
  uniforms: {
    fresnelColor: { value: new THREE.Color(0x00ff00) },
    fresnelBias: { value: 0.1 },
    fresnelScale: { value: 1.0 },
    fresnelPower: { value: 2.0 }
  },
  vertexShader: `
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    
    void main() {
      vNormal = normalize(normalMatrix * normal);
      vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
      vViewPosition = -mvPosition.xyz;
      gl_Position = projectionMatrix * mvPosition;
    }
  `,
  fragmentShader: `
    uniform vec3 fresnelColor;
    uniform float fresnelBias;
    uniform float fresnelScale;
    uniform float fresnelPower;
    
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    
    void main() {
      vec3 viewDir = normalize(vViewPosition);
      float fresnel = fresnelBias + fresnelScale * pow(1.0 - dot(viewDir, vNormal), fresnelPower);
      gl_FragColor = vec4(fresnelColor * fresnel, 1.0);
    }
  `,
  transparent: true
});

材质动画

动态颜色变化

javascript
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });

function animate() {
  requestAnimationFrame(animate);
  
  // 颜色随时间变化
  const time = Date.now() * 0.001;
  const hue = (time * 0.1) % 1;
  material.color.setHSL(hue, 1, 0.5);
  
  renderer.render(scene, camera);
}
animate();

动态金属度与粗糙度

javascript
const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  metalness: 0.5,
  roughness: 0.5
});

function animate() {
  requestAnimationFrame(animate);
  
  const time = Date.now() * 0.001;
  
  // 动态金属度
  material.metalness = (Math.sin(time) + 1) / 2;
  
  // 动态粗糙度
  material.roughness = (Math.cos(time * 0.7) + 1) / 2;
  
  renderer.render(scene, camera);
}

纹理动画

javascript
const texture = new THREE.TextureLoader().load('texture.jpg');
const material = new THREE.MeshBasicMaterial({ map: texture });

function animate() {
  requestAnimationFrame(animate);
  
  const time = Date.now() * 0.001;
  
  // UV 偏移
  texture.offset.x = time * 0.1;
  texture.offset.y = time * 0.05;
  
  // UV 旋转
  texture.rotation = time * 0.5;
  
  renderer.render(scene, camera);
}

着色器动画

javascript
const material = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 },
    amplitude: { value: 1.0 }
  },
  vertexShader: `
    uniform float time;
    uniform float amplitude;
    
    varying vec2 vUv;
    
    void main() {
      vUv = uv;
      
      vec3 pos = position;
      pos.z += sin(pos.x * 5.0 + time) * amplitude * 0.1;
      pos.z += cos(pos.y * 5.0 + time) * amplitude * 0.1;
      
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  fragmentShader: `
    uniform float time;
    varying vec2 vUv;
    
    void main() {
      float wave = sin(vUv.x * 10.0 + time) * 0.5 + 0.5;
      vec3 color = vec3(wave, 1.0 - wave, 0.5);
      gl_FragColor = vec4(color, 1.0);
    }
  `
});

function animate() {
  requestAnimationFrame(animate);
  
  material.uniforms.time.value = performance.now() * 0.001;
  material.uniforms.amplitude.value = Math.sin(performance.now() * 0.0005) + 1;
  
  renderer.render(scene, camera);
}

材质特效

线框与实体混合

javascript
// 创建实体材质
const solidMaterial = new THREE.MeshStandardMaterial({
  color: 0x00ff00,
  side: THREE.DoubleSide
});

// 创建线框材质
const wireframeMaterial = new THREE.MeshBasicMaterial({
  color: 0x000000,
  wireframe: true
});

// 创建两个网格
const solidMesh = new THREE.Mesh(geometry, solidMaterial);
const wireframeMesh = new THREE.Mesh(geometry.clone(), wireframeMaterial);

// 线框稍微大一点,避免 z-fighting
wireframeMesh.scale.multiplyScalar(1.001);

scene.add(solidMesh);
scene.add(wireframeMesh);

双面材质

javascript
const frontMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const backMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });

const geometry = new THREE.PlaneGeometry(2, 2);

// 正面网格
const frontMesh = new THREE.Mesh(geometry, frontMaterial);
scene.add(frontMesh);

// 背面网格(反转法线)
const backMesh = new THREE.Mesh(geometry.clone(), backMaterial);
backMesh.material.side = THREE.BackSide;
scene.add(backMesh);

多材质几何体

javascript
// 为立方体的每个面设置不同材质
const materials = [
  new THREE.MeshStandardMaterial({ color: 0xff0000 }),  // 右
  new THREE.MeshStandardMaterial({ color: 0x00ff00 }),  // 左
  new THREE.MeshStandardMaterial({ color: 0x0000ff }),  // 上
  new THREE.MeshStandardMaterial({ color: 0xffff00 }),  // 下
  new THREE.MeshStandardMaterial({ color: 0xff00ff }),  // 前
  new THREE.MeshStandardMaterial({ color: 0x00ffff })   // 后
];

const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);

性能优化

材质共享

javascript
// 不推荐
const meshes = [];
for (let i = 0; i < 1000; i++) {
  const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
  const mesh = new THREE.Mesh(geometry, material);
  meshes.push(mesh);
}

// 推荐
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const meshes = [];
for (let i = 0; i < 1000; i++) {
  const mesh = new THREE.Mesh(geometry, sharedMaterial);
  meshes.push(mesh);
}

贴图尺寸优化

javascript
// 使用合适的贴图尺寸(2 的幂次方)
const sizes = [64, 128, 256, 512, 1024, 2048];

// 根据距离使用不同分辨率
function loadTextureWithLOD(distance) {
  if (distance < 10) return textureLoader.load('high_res.jpg');
  if (distance < 50) return textureLoader.load('medium_res.jpg');
  return textureLoader.load('low_res.jpg');
}

PBR 材质预设库

常见材质配置

javascript
// 材质预设库
const MaterialPresets = {
  // 金属类
  metals: {
    gold: {
      color: 0xffd700,
      metalness: 1.0,
      roughness: 0.3
    },
    silver: {
      color: 0xc0c0c0,
      metalness: 1.0,
      roughness: 0.2
    },
    copper: {
      color: 0xb87333,
      metalness: 1.0,
      roughness: 0.4
    },
    chrome: {
      color: 0xffffff,
      metalness: 1.0,
      roughness: 0.1
    }
  },
  
  // 非金属类
  dielectrics: {
    plastic: {
      color: 0x0066ff,
      metalness: 0.0,
      roughness: 0.4
    },
    rubber: {
      color: 0x333333,
      metalness: 0.0,
      roughness: 0.9
    },
    wood: {
      color: 0x8b4513,
      metalness: 0.0,
      roughness: 0.7
    },
    fabric: {
      color: 0x8b0000,
      metalness: 0.0,
      roughness: 0.8
    }
  },
  
  // 玻璃与透射
  transparent: {
    glass: {
      color: 0xffffff,
      metalness: 0.0,
      roughness: 0.0,
      transmission: 0.95,
      transparent: true,
      ior: 1.5
    },
    water: {
      color: 0x4488ff,
      metalness: 0.0,
      roughness: 0.0,
      transmission: 0.8,
      transparent: true,
      ior: 1.33
    }
  },
  
  // 高级效果
  advanced: {
    carPaint: {
      color: 0xff0000,
      metalness: 0.9,
      roughness: 0.1,
      clearcoat: 1.0,
      clearcoatRoughness: 0.05
    },
    velvet: {
      color: 0x800020,
      metalness: 0.0,
      roughness: 0.8,
      sheen: 1.0,
      sheenColor: 0xffffff,
      sheenRoughness: 0.3
    }
  }
};

// 创建材质工厂函数
function createMaterialFromPreset(category, name) {
  const preset = MaterialPresets[category]?.[name];
  if (!preset) {
    console.warn(`Material preset not found: ${category}.${name}`);
    return new THREE.MeshStandardMaterial();
  }
  
  // 根据材质类型选择合适的类
  if (preset.transmission !== undefined) {
    return new THREE.MeshPhysicalMaterial(preset);
  }
  if (preset.clearcoat !== undefined || preset.sheen !== undefined) {
    return new THREE.MeshPhysicalMaterial(preset);
  }
  return new THREE.MeshStandardMaterial(preset);
}

// 使用示例
const goldMaterial = createMaterialFromPreset('metals', 'gold');
const glassMaterial = createMaterialFromPreset('transparent', 'glass');

性能优化策略

性能指标对比

材质类型帧时间影响GPU 负载适用物体数量内存占用
MeshBasicMaterial~0.1ms>1000
MeshLambertMaterial~0.3ms低-中500-1000
MeshPhongMaterial~0.5ms200-500
MeshStandardMaterial~1.0ms中-高100-200
MeshPhysicalMaterial~2.0ms50-100
ShaderMaterial不定不定取决于复杂度不定

优化建议

  1. 材质复杂度优化

    javascript
    // 根据距离切换材质
    function updateMaterialLOD(distance, mesh) {
      if (distance > 100) {
        mesh.material = basicMaterial;  // 远距离:简单材质
      } else if (distance > 50) {
        mesh.material = lambertMaterial; // 中距离:Lambert
      } else {
        mesh.material = standardMaterial; // 近距离:PBR
      }
    }
  2. 贴图优化

    javascript
    // 合理设置贴图尺寸
    const textureSizes = {
      hero: 2048,      // 主角物体
      important: 1024, // 重要物体
      regular: 512,    // 普通物体
      background: 256  // 背景物体
    };
    
    // 使用贴图压缩
    // KTX2 格式可减少 50-70% 内存
  3. 渲染批处理

    javascript
    // 合并相同材质的几何体
    const mergedGeometry = new THREE.BufferGeometry();
    // ... 合并几何体
    
    const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
    const mergedMesh = new THREE.Mesh(mergedGeometry, material);
    scene.add(mergedMesh);

常见问题

1. PBR 材质过暗或过亮

问题:材质在场景中显示效果不理想。

解决方案

javascript
// 检查环境贴图
scene.environment = envMap;  // PBR 材质必需

// 检查光照强度
const light = new THREE.DirectionalLight(0xffffff, 1);
scene.add(light);

// 调整材质参数
material.envMapIntensity = 1.0;  // 环境贴图强度
material.aoMapIntensity = 1.0;   // AO 强度

2. 玻璃材质效果不真实

问题:透射材质看起来像塑料而不是玻璃。

解决方案

javascript
const glassMaterial = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  metalness: 0,
  roughness: 0,
  transmission: 0.95,
  transparent: true,
  ior: 1.5,
  thickness: 0.5,
  
  // 关键设置
  envMapIntensity: 1.0,
  clearcoat: 0,
  
  // 需要环境贴图
  // scene.environment = envMap;
});

3. 自定义着色器与光照

问题:ShaderMaterial 不响应场景光照。

解决方案

javascript
const material = new THREE.ShaderMaterial({
  uniforms: {
    // Three.js 自动注入的光照 uniform
    ...THREE.UniformsLib.lights
  },
  vertexShader: `...`,
  fragmentShader: `...`,
  lights: true  // 启用光照支持
});

4. 材质贴图偏移和重复

问题:贴图显示不正确或需要调整。

解决方案

javascript
const texture = textureLoader.load('texture.jpg');

// 设置重复
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);  // 重复 2x2

// 设置偏移
texture.offset.set(0.5, 0);

// 设置旋转
texture.rotation = Math.PI / 4;
texture.center.set(0.5, 0.5);

最佳实践

  1. 选择合适的材质类型

    • 简单场景:MeshBasicMaterial 或 MeshLambertMaterial
    • 需要高光:MeshPhongMaterial
    • 真实渲染:MeshStandardMaterial
    • 高级效果:MeshPhysicalMaterial
  2. 正确使用 PBR 工作流

    • 理解金属度和粗糙度的物理含义
    • 使用环境贴图获得真实反射
    • 合理配置贴图(法线、AO、粗糙度等)
  3. 优化贴图使用

    • 合并贴图(ORM 贴图)
    • 使用合适的分辨率
    • 启用各向异性过滤
  4. 共享材质实例

    • 避免重复创建相同材质
    • 使用材质预设库
  5. 注意着色器性能

    • 避免复杂计算
    • 使用低精度类型
    • 优化 uniform 更新频率

相关链接