材质进阶
深入探讨 Three.js 的高级材质技术,包括 PBR 材质工作流、自定义着色器材质、材质动画等高级主题。
概述
在掌握了基础材质类型后,本章节将介绍更高级的材质应用,包括物理渲染材质的深度应用、自定义着色器编程、以及材质的高级特效。
PBR 材质工作流
理解 PBR(Physically Based Rendering)
PBR 是一种基于物理的渲染方法,能够产生更真实、更一致的光照效果。相比传统渲染,PBR 具有以下优势:
核心优势:
- 物理准确性:基于真实物理定律的光照计算
- 能量守恒:反射光能量不会超过入射光能量
- 材质一致性:在不同光照环境下保持相似的外观
- 工作流标准化:便于团队协作和资产复用
PBR 核心原理:
-
能量守恒定律
javascript// 反射光 + 漫反射 ≤ 入射光 // Three.js 自动处理能量守恒 const material = new THREE.MeshStandardMaterial({ metalness: 0.5, // 金属度越高,漫反射越少 roughness: 0.5 // 粗糙度影响反射分布 }); -
微表面理论
- 粗糙表面:光线向多个方向散射
- 光滑表面:光线集中反射
-
菲涅尔效应
- 视角越倾斜,反射越强
- 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.5ms | 中 | 200-500 | 中 |
| MeshStandardMaterial | ~1.0ms | 中-高 | 100-200 | 中 |
| MeshPhysicalMaterial | ~2.0ms | 高 | 50-100 | 高 |
| ShaderMaterial | 不定 | 不定 | 取决于复杂度 | 不定 |
优化建议
-
材质复杂度优化:
javascript// 根据距离切换材质 function updateMaterialLOD(distance, mesh) { if (distance > 100) { mesh.material = basicMaterial; // 远距离:简单材质 } else if (distance > 50) { mesh.material = lambertMaterial; // 中距离:Lambert } else { mesh.material = standardMaterial; // 近距离:PBR } } -
贴图优化:
javascript// 合理设置贴图尺寸 const textureSizes = { hero: 2048, // 主角物体 important: 1024, // 重要物体 regular: 512, // 普通物体 background: 256 // 背景物体 }; // 使用贴图压缩 // KTX2 格式可减少 50-70% 内存 -
渲染批处理:
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);最佳实践
-
选择合适的材质类型:
- 简单场景:MeshBasicMaterial 或 MeshLambertMaterial
- 需要高光:MeshPhongMaterial
- 真实渲染:MeshStandardMaterial
- 高级效果:MeshPhysicalMaterial
-
正确使用 PBR 工作流:
- 理解金属度和粗糙度的物理含义
- 使用环境贴图获得真实反射
- 合理配置贴图(法线、AO、粗糙度等)
-
优化贴图使用:
- 合并贴图(ORM 贴图)
- 使用合适的分辨率
- 启用各向异性过滤
-
共享材质实例:
- 避免重复创建相同材质
- 使用材质预设库
-
注意着色器性能:
- 避免复杂计算
- 使用低精度类型
- 优化 uniform 更新频率