1. UV 映射基础概念
1.1 什么是 UV 坐标?
在三维计算机图形学中,UV 坐标是将二维纹理映射到三维模型表面的坐标系统。UV 中的 U 和 V 分别代表2D纹理空间的水平(X)和垂直(Y)坐标轴,与三维空间中的 XYZ 坐标形成区分。
- UV 坐标系范围:[0,0] 到 [1,1]
- 每个顶点对应一个UV坐标
- 纹理采样器通过UV值获取纹理颜色
1.2 坐标系差异
// Three.js 中的坐标系对比
三维模型坐标:(x, y, z) ∈ [-∞, +∞]
纹理UV坐标:(u, v) ∈ [0, 1]
屏幕像素坐标:(x, y) ∈ [0, viewportSize]
2. Three.js 中的 UV 实现
2.1 Geometry 与 BufferGeometry
// 经典 Geometry 的UV设置
const geometry = new THREE.Geometry();
geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 0),
new THREE.Vector2(1, 0),
new THREE.Vector2(0.5, 1)
]);
// BufferGeometry 的UV设置
// 自定义平面几何体
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([...]); // 顶点坐标
const uv = new Float32Array([
0, 0, // 顶点0的UV
1, 0, // 顶点1的UV
1, 1, // 顶点2的UV
0, 1 // 顶点3的UV
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setAttribute('uv', new THREE.BufferAttribute(uv, 2)); // 关键UV设置[1,5](@ref)
2.2 内置几何体的UV映射
映射类型 | 特点 | 适用场景 | Three.js实现 |
---|---|---|---|
平面映射 | 正交投影,无透视变形 | 墙面、地面 | PlaneGeometry 默认UV2 |
立方体映射 | 6面独立展开 | 盒子、建筑 | BoxGeometry 自动生成4 |
球形映射 | 极坐标展开 | 行星、球体 | SphereGeometry 经线展开6 |
圆柱映射 | 环形展开 | 管道、柱体 | 需手动设置UV3 |
2.2.1 BoxGeometry
- 6个立方体面共享同一张纹理
- 每个面UV范围:[ (0,0), (1,1) ]
- 默认沿表面均匀展开,,但可能存在接缝
2.2.2 SphereGeometry
- 经线方向:U坐标(0到1)
- 纬线方向:V坐标(0到1)
- 极点处UV密度较高
- 采用球形投影,UV 在两极可能出现拉伸。
2.2.3 CylinderGeometry
- 侧面:U环绕圆柱,V沿高度
- 顶部/底面:圆形UV展开
3. UV 操作实战技巧
3.1 动态修改UV坐标
// 获取UV属性数组
const uvAttribute = geometry.getAttribute('uv');
const uvs = uvAttribute.array;
// 修改第三个顶点的U坐标
uvs[2 * 2] = 0.75; // 索引计算:顶点索引 * 分量数 + 偏移
// 必须标记更新
uvAttribute.needsUpdate = true;
3.2 纹理重复与偏移
const texture = new THREE.TextureLoader().load('tile.jpg');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.MirroredRepeatWrapping;
texture.repeat.set(2, 3);
texture.offset.set(0.5, 0.25);
4. 高级 UV 应用
4.1 多重纹理混合
// 着色器代码示例
varying vec2 vUv;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main() {
vec4 texColor1 = texture2D(texture1, vUv);
vec4 texColor2 = texture2D(texture2, vUv * 2.0);
gl_FragColor = mix(texColor1, texColor2, 0.5);
}
4.2 UV 动画
function animate() {
requestAnimationFrame(animate);
// 水平滚动纹理
texture.offset.x += 0.01;
// 动态修改UV坐标
const uvs = geometry.attributes.uv.array;
for(let i=0; i<uvs.length; i+=2){
uvs[i] += Math.sin(Date.now() * 0.001) * 0.01;
}
geometry.attributes.uv.needsUpdate = true;
}
4.3 顶点着色器中的 UV 控制
通过自定义着色器实现复杂效果:
// 顶点着色器
varying vec2 vUv;
uniform float time;
void main() {
vUv = uv + vec2(time * 0.1, 0.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
4.4 法线映射与 UV 的关系
法线贴图依赖 UV 坐标确定法线方向:
const material = new THREE.MeshStandardMaterial({
normalMap: normalTexture,
normalScale: new THREE.Vector2(0.5, 0.5) // 控制法线强度
});
4.5 光照烘焙与 UV
在 Blender 中烘焙光照后导出带有uv2
的模型:
// 在Three.js中启用第二组UV
geometry.setAttribute('uv2', new THREE.BufferAttribute(uv2Array, 2));
material.aoMap = aoTexture;
material.aoMap.uvTransform = new THREE.Matrix3().fromArray(uvTransform);
5. 常见问题解决方案
5.1 纹理拉伸问题
成因:UV分布不均匀
解决方案:
- 使用更密集的几何细分
- 制作UDIM纹理集
- 应用三平面投影
5.2 接缝处理技巧
原因:UV 坐标不连续或导出时顶点 UV 重复。
解决方案:
在 Blender 中使用
Smart UV Project
重新展开。导出时勾选
Merge by Distance
选项。在 Three.js 中调整 UV 坐标:
const uvArray = geometry.attributes.uv.array; for (let i = 0; i < uvArray.length; i += 2) { uvArray[i] = uvArray[i] % 1; uvArray[i + 1] = uvArray[i + 1] % 1; } geometry.attributes.uv.needsUpdate = true;
6. 性能优化建议
- 合并UV集:将多个材质的UV合并到同一纹理图集
- 使用压缩纹理格式:推荐 KTX2或者Basis Universal 格式
- 避免动态UV更新:静态几何体应冻结UV缓冲区
- LOD策略:根据距离切换UV细节层级
7. 调试工具推荐
- UV检查材质:
const debugMaterial = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('uv-grid.png')
});
- Three.js编辑器:可视化UV展开
- Blender + Three.js导出:专业级UV编辑流程
结语
UV映射是连接3D模型与2D纹理的核心桥梁。通过深入理解Three.js的UV实现机制,开发者可以创建更复杂的材质效果、优化渲染性能,并解决实际项目中的各种纹理映射难题。实际开发中需注意:纹理尺寸需为2的幂、不同材质类型对UV的支持差异、移动端性能优化等。建议结合WebGL着色器编程,进一步挖掘UV系统的潜力。