UV 映射完全指南:从基础到高级技术
什么是 UV 映射?
UV 映射(UV Mapping)是 3D 计算机图形学中将 2D 纹理图像映射到 3D 模型表面的过程。UV 坐标系统使用 U 和 V 两个轴来表示纹理空间,其中:
- U 轴:对应纹理的水平方向(类似 X 轴)
- V 轴:对应纹理的垂直方向(类似 Y 轴)
- 范围:通常归一化到 [0, 1] 区间
UV 映射的本质是建立 3D 模型表面上的点与 2D 纹理图像上的点之间的对应关系。
为什么需要 UV 映射?
纹理映射的基本原理
3D 模型是由顶点和三角形组成的几何结构,而纹理是 2D 图像。要将纹理"贴"到模型上,需要解决以下问题:
- 表面参数化:将 3D 表面展开到 2D 平面
- 坐标对应:确定每个顶点在纹理空间中的位置
- 插值计算:在三角形内部进行平滑插值
UV 坐标与顶点属性
在图形管线中,UV 坐标通常作为顶点属性存储:
struct Vertex {
float3 position : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD; // UV 坐标
};
UV 映射的基本类型
1. 平面映射(Planar Mapping)
将纹理沿一个方向投影到模型表面:
// 平面映射 - 从 Y 轴方向投影
float2 planarMapping(float3 position) {
return float2(position.x, position.z);
}
适用场景:地形、墙壁、地板等平面物体
优点:简单快速 缺点:会产生拉伸和接缝
2. 圆柱映射(Cylindrical Mapping)
将纹理包裹在圆柱体周围:
// 圆柱映射
float2 cylindricalMapping(float3 position) {
float u = atan2(position.x, position.z) / (2.0 * 3.14159) + 0.5;
float v = (position.y - minY) / (maxY - minY);
return float2(u, v);
}
适用场景:瓶子、柱子、手臂等圆柱形物体
优点:减少侧面拉伸 缺点:顶部和底部仍需特殊处理
3. 球面映射(Spherical Mapping)
将纹理映射到球体表面:
// 球面映射
float2 sphericalMapping(float3 position) {
float3 dir = normalize(position);
float u = atan2(dir.x, dir.z) / (2.0 * 3.14159) + 0.5;
float v = asin(dir.y) / 3.14159 + 0.5;
return float2(u, v);
}
适用场景:球体、眼球、行星等球形物体
优点:均匀映射 缺点:极点处会产生奇点
4. 立方体映射(Box Mapping)
从六个方向投影纹理:
// 立方体映射 - 根据法线选择投影面
float2 boxMapping(float3 position, float3 normal) {
float3 absNormal = abs(normal);
float2 uv;
if (absNormal.x > absNormal.y && absNormal.x > absNormal.z) {
// X 轴方向
uv = float2(position.z, position.y);
} else if (absNormal.y > absNormal.z) {
// Y 轴方向
uv = float2(position.x, position.z);
} else {
// Z 轴方向
uv = float2(position.x, position.y);
}
return uv;
}
适用场景:建筑物、盒子、房间等方形物体
优点:多方向投影,减少拉伸 缺点:接缝处需要特殊处理
UV 展开技术
手动 UV 展开
在 3D 建模软件(如 Blender、Maya、3ds Max)中手动调整 UV 布局:
- 标记缝合边:选择需要切开的边
- 展开算法:使用 ABF++、LSCM 等算法自动展开
- 手动调整:优化布局,减少拉伸
自动 UV 展开算法
1. ABF++(Angle Based Flattening)
基于角度的展开方法,保持角度比例:
def abf_plus_plus(mesh):
# 1. 计算初始角度
angles = compute_angles(mesh)
# 2. 优化角度
optimized_angles = optimize_angles(angles)
# 3. 根据优化后的角度计算 UV 坐标
uv_coords = compute_uv_from_angles(optimized_angles)
return uv_coords
2. LSCM(Least Squares Conformal Mapping)
最小二乘保角映射,保持局部形状:
def lscm(mesh, pinned_vertices):
# 1. 构建线性系统
A = build_matrix(mesh)
b = build_rhs(mesh, pinned_vertices)
# 2. 求解最小二乘问题
uv_coords = solve_least_squares(A, b)
return uv_coords
3. 智能 UV 投影
基于角度阈值自动分割和展开:
def smart_uv_project(mesh, angle_limit=66):
# 1. 根据角度阈值分割面
islands = split_by_angle(mesh, angle_limit)
# 2. 对每个岛进行展开
for island in islands:
uv_island = simple_project(island)
pack_island(uv_island)
return combined_uv
UV 布局优化
1. 减少纹理拉伸
纹理拉伸是 UV 映射中最常见的问题,可以通过以下方法优化:
def compute_stretch(mesh, uv_coords):
"""计算纹理拉伸程度"""
stretch = []
for face in mesh.faces:
# 计算 3D 面积
area_3d = compute_face_area_3d(face)
# 计算 UV 面积
area_2d = compute_face_area_uv(face, uv_coords)
# 拉伸比
if area_2d > 0:
stretch_ratio = sqrt(area_3d / area_2d)
else:
stretch_ratio = float('inf')
stretch.append(stretch_ratio)
return stretch
2. 最小化接缝
接缝是 UV 映射中不可避免的问题,可以通过以下策略减少:
- 隐藏接缝:将接缝放在不显眼的位置(如底部、内侧)
- 接缝对齐:让接缝沿着模型的自然边界
- 重叠 UV:对称部分共享 UV 空间
3. 纹理空间利用率
优化 UV 岛的排列,最大化纹理空间使用:
def pack_uv_islands(islands, texture_size):
"""将 UV 岛打包到纹理空间"""
# 1. 按面积排序
sorted_islands = sort_by_area(islands, descending=True)
# 2. 贪心打包
packed = []
for island in sorted_islands:
# 尝试不同的位置和旋转
best_position = find_best_position(island, packed, texture_size)
place_island(island, best_position)
packed.append(island)
return packed
高级 UV 技术
1. 多纹理集(UDIM)
UDIM 是一种多纹理工作流程,允许使用多个纹理贴图覆盖更大的 UV 空间:
UDIM 纹理命名规则:
- 1001: UV (0,0) 到 (1,1)
- 1002: UV (1,0) 到 (2,1)
- 1003: UV (2,0) 到 (3,1)
- ...
// UDIM 纹理采样
float4 sampleUDIM(float2 uv, Texture2DArray textures) {
// 确定 UDIM 瓦片
int tileU = floor(uv.x);
int tileV = floor(uv.y);
int tileIndex = tileV * 10 + tileU;
// 计算瓦片内坐标
float2 tileUV = frac(uv);
return textures.Sample(sampler, float3(tileUV, tileIndex));
}
2. 三平面映射(Triplanar Mapping)
一种无需 UV 坐标的纹理映射技术,通过三个方向的投影混合:
// 三平面映射
float4 triplanarMapping(float3 position, float3 normal, Texture2D texX, Texture2D texY, Texture2D texZ) {
// 计算混合权重
float3 blend = abs(normal);
blend = pow(blend, 4); // 锐化混合
blend /= dot(blend, 1); // 归一化
// 三个方向的投影
float4 xProjection = texX.Sample(sampler, position.yz);
float4 yProjection = texY.Sample(sampler, position.xz);
float4 zProjection = texZ.Sample(sampler, position.xy);
// 混合结果
return xProjection * blend.x + yProjection * blend.y + zProjection * blend.z;
}
3. 世界空间坐标映射
使用世界空间坐标作为 UV,适用于大型环境:
// 世界空间映射
float2 worldSpaceUV(float3 worldPosition, float scale) {
return worldPosition.xz / scale; // 从上方投影
}
UV 映射中的常见问题
1. 接缝可见性
问题:UV 接缝处出现明显的纹理断裂
解决方案:
- 使用纹理包裹模式(Wrap/Repeat)
- 在接缝处添加重叠区域
- 使用接缝隐藏工具
2. 纹理拉伸
问题:某些区域的纹理被过度拉伸
解决方案:
- 重新展开 UV,减少拉伸
- 使用多个 UV 通道
- 应用纹理缩放补偿
3. 纹理空间浪费
问题:UV 岛之间有大量空白区域
解决方案:
- 使用自动打包工具
- 手动调整 UV 岛位置
- 使用 UDIM 工作流程
实际工作流程
Blender 中的 UV 映射工作流程
-
准备模型
- 清理拓扑结构
- 标记缝合边(Ctrl+E → Mark Seam)
-
展开 UV
- 选择所有面(A)
- 打开 UV 编辑器
- 执行展开(U → Unwrap)
-
优化布局
- 调整 UV 岛位置
- 缩放和旋转以减少拉伸
- 使用 Pack Islands 工具
-
导出和测试
- 导出 UV 布局
- 在纹理绘制软件中测试
游戏开发中的 UV 最佳实践
-
纹理分辨率分配
- 重要区域使用更高分辨率
- 不可见区域使用更低分辨率
-
UV 通道复用
- 第一通道:漫反射纹理
- 第二通道:光照贴图
- 第三通道:细节纹理
-
对称性利用
- 对称模型共享 UV 空间
- 减少纹理内存占用
程序化 UV 生成
实时 UV 生成
在着色器中动态生成 UV 坐标:
// 程序化 UV 生成
float2 proceduralUV(float3 position, float3 normal, float2 scale) {
// 基于世界空间坐标
float2 worldUV = position.xz * scale;
// 基于法线方向混合
float3 blend = abs(normal);
blend /= dot(blend, 1);
float2 uv = worldUV * blend.y + position.yz * scale * blend.x + position.xy * scale * blend.z;
return uv;
}
UV 动画
通过修改 UV 坐标实现纹理动画:
// UV 滚动动画
float2 uvAnimation(float2 uv, float2 scrollSpeed, float time) {
return uv + scrollSpeed * time;
}
// UV 旋转动画
float2 uvRotation(float2 uv, float angle, float2 center) {
float s = sin(angle);
float c = cos(angle);
uv -= center;
float2 rotated = float2(
uv.x * c - uv.y * s,
uv.x * s + uv.y * c
);
return rotated + center;
}
工具推荐
专业 UV 工具
- Blender:免费开源,功能强大
- Maya:行业标准,UV 工具丰富
- 3ds Max:游戏开发常用
- RizomUV:专业 UV 展开工具
自动化工具
- UV-Packer:自动 UV 打包
- Unfold3D:自动 UV 展开
- TexTools:Blender UV 插件
总结
UV 映射是 3D 图形学中的核心技能,掌握它需要理解:
- 基本概念:UV 坐标系统、纹理空间、映射类型
- 展开技术:手动展开、自动算法、优化方法
- 高级技巧:UDIM、三平面映射、程序化生成
- 问题解决:接缝处理、拉伸优化、空间利用
通过实践和经验积累,可以创建出高质量的 UV 布局,为后续的纹理绘制和渲染打下坚实基础。