在Three.js的世界里,纹理贴图就像给3D模型穿上衣服的过程。想象你刚用几何体建好一个白模,就像服装设计师用白坯布做出的样衣,纹理贴图就是最后赋予它色彩、质感和细节的关键步骤。
纹理本质上就是2D图像,通过UV映射技术"包裹"在3D模型表面。UV坐标系统(U代表水平,V代表垂直)将2D图像的每个像素对应到3D模型的特定位置。这个过程就像地球仪展开成平面地图,虽然会有一定变形,但通过合理的UV展开可以最大限度保持纹理的准确性。
Three.js支持多种纹理类型:
实际项目中,90%的性能问题都出在纹理处理不当。我曾遇到一个案例:设计师提供的4K纹理导致移动端直接崩溃,最终通过压缩和mipmap优化才解决。
Three.js通过TextureLoader加载纹理,以下是实际项目中最常用的三种加载方式:
javascript复制// 基础加载方式
const loader = new THREE.TextureLoader();
const texture = loader.load('textures/brick_diffuse.jpg');
// 带回调的加载方式(推荐)
const texture = loader.load(
'textures/metal_plate.jpg',
(texture) => { console.log('纹理加载完成'); },
undefined,
(err) => { console.error('加载失败', err); }
);
// 使用LoadingManager统一管理
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {
console.log(`加载进度: ${loaded}/${total}`);
};
const loader = new THREE.TextureLoader(manager);
加载后的纹理需要合理配置参数才能达到最佳效果:
javascript复制texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.MirroredRepeatWrapping; // 垂直镜像重复
texture.repeat.set(2, 2); // 重复次数
texture.offset.set(0.5, 0.5); // 偏移量
texture.rotation = Math.PI/4; // 旋转45度
texture.center.set(0.5, 0.5); // 旋转中心
texture.magFilter = THREE.LinearFilter; // 放大过滤
texture.minFilter = THREE.LinearMipmapLinearFilter; // 缩小过滤
我曾在一个建筑可视化项目中,通过巧妙设置repeat和offset参数,用一张512x512的砖墙贴图完美覆盖了整个足球场模型,节省了80%的纹理内存。
将纹理应用到不同材质的方法有所差异:
javascript复制// MeshBasicMaterial(不受光照影响)
const material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
alphaMap: alphaTexture
});
// MeshStandardMaterial(PBR材质)
const pbrMaterial = new THREE.MeshStandardMaterial({
map: diffuseTexture,
normalMap: normalTexture,
roughnessMap: roughTexture,
metalnessMap: metalTexture
});
法线贴图通过RGB通道存储表面法线信息,在不增加几何体复杂度的情况下增加表面细节。实际操作中需要注意:
javascript复制material.normalMap = normalTexture;
material.normalScale.set(1, 1); // 可调节法线强度
// 常见问题:法线贴图"反向"的解决方案
material.normalMap.flipY = false; // 根据贴图制作软件调整
环境贴图(EnvMap)用于模拟环境反射,实现金属、玻璃等材质效果:
javascript复制// 立方体贴图加载
const envLoader = new THREE.CubeTextureLoader();
const envMap = envLoader.load([
'px.jpg', 'nx.jpg',
'py.jpg', 'ny.jpg',
'pz.jpg', 'nz.jpg'
]);
material.envMap = envMap;
material.envMapIntensity = 1.5; // 反射强度
// 实时环境贴图生成
const rt = new THREE.WebGLCubeRenderTarget(512);
scene.environment = rt.texture;
Three.js支持将视频作为动态纹理使用:
javascript复制const video = document.createElement('video');
video.src = 'textures/fire.mp4';
video.loop = true;
video.muted = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
material.map = videoTexture;
// 性能优化:设置正确的更新策略
function animate() {
if(video.readyState === video.HAVE_ENOUGH_DATA) {
videoTexture.needsUpdate = true;
}
requestAnimationFrame(animate);
}
尺寸优化:
内存管理:
javascript复制// 主动释放纹理内存
texture.dispose();
// 检查纹理内存占用
console.log(renderer.info.memory.textures);
加载策略:
问题1:纹理闪烁(Moiré Pattern)
javascript复制texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.generateMipmaps = true;
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
问题2:透明纹理边缘白边
javascript复制material.alphaTest = 0.5; // 设置合适的阈值
// 或
material.premultipliedAlpha = true;
问题3:纹理加载延迟导致模型白模
javascript复制// 预加载关键纹理
const preloadTextures = async () => {
const loader = new THREE.TextureLoader();
const textures = await Promise.all([
loader.loadAsync('diffuse.jpg'),
loader.loadAsync('normal.jpg')
]);
return textures;
};
以下是一个完整的PBR材质纹理配置示例,包含所有贴图类型:
javascript复制const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
map: diffuseTexture, // 基础颜色
normalMap: normalTexture, // 法线
roughnessMap: roughTexture, // 粗糙度
metalnessMap: metalTexture, // 金属度
aoMap: aoTexture, // 环境光遮蔽
emissiveMap: emitTexture, // 自发光
envMap: envTexture, // 环境反射
// 各贴图强度调节
normalScale: new THREE.Vector2(1, 1),
roughness: 1.0,
metalness: 0.5,
aoMapIntensity: 1.0,
emissiveIntensity: 0.8,
envMapIntensity: 1.2
});
在配置这类复杂材质时,我通常会创建一个调试面板来实时调节各参数:
javascript复制const gui = new GUI();
gui.add(material, 'roughness', 0, 1);
gui.add(material, 'metalness', 0, 1);
gui.add(material.normalScale, 'x', 0, 2).name('法线强度X');
gui.add(material.normalScale, 'y', 0, 2).name('法线强度Y');
经过多个项目实践,我总结出以下高效工具组合:
图像处理:
格式转换:
在线工具:
Three.js配套:
特别提醒:设计师提供的法线贴图经常需要翻转Y轴,这是不同软件间的常见差异点。我通常在加载后立即添加 texture.flipY = false 这行代码,可以避免80%的法线贴图方向问题。