Chenyj Space
返回博客
·26 分钟

UV 映射完全指南:从基础到高级技术

图形学UV映射纹理3D建模tech

什么是 UV 映射?

UV 映射(UV Mapping)是 3D 计算机图形学中将 2D 纹理图像映射到 3D 模型表面的过程。UV 坐标系统使用 U 和 V 两个轴来表示纹理空间,其中:

  • U 轴:对应纹理的水平方向(类似 X 轴)
  • V 轴:对应纹理的垂直方向(类似 Y 轴)
  • 范围:通常归一化到 [0, 1] 区间

UV 映射的本质是建立 3D 模型表面上的点与 2D 纹理图像上的点之间的对应关系。

为什么需要 UV 映射?

纹理映射的基本原理

3D 模型是由顶点和三角形组成的几何结构,而纹理是 2D 图像。要将纹理"贴"到模型上,需要解决以下问题:

  1. 表面参数化:将 3D 表面展开到 2D 平面
  2. 坐标对应:确定每个顶点在纹理空间中的位置
  3. 插值计算:在三角形内部进行平滑插值

UV 坐标与顶点属性

在图形管线中,UV 坐标通常作为顶点属性存储:

hlsl
struct Vertex {
    float3 position : POSITION;
    float3 normal : NORMAL;
    float2 uv : TEXCOORD; // UV 坐标
};

UV 映射的基本类型

1. 平面映射(Planar Mapping)

将纹理沿一个方向投影到模型表面:

hlsl
// 平面映射 - 从 Y 轴方向投影
float2 planarMapping(float3 position) {
    return float2(position.x, position.z);
}

适用场景:地形、墙壁、地板等平面物体

优点:简单快速 缺点:会产生拉伸和接缝

2. 圆柱映射(Cylindrical Mapping)

将纹理包裹在圆柱体周围:

hlsl
// 圆柱映射
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)

将纹理映射到球体表面:

hlsl
// 球面映射
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)

从六个方向投影纹理:

hlsl
// 立方体映射 - 根据法线选择投影面
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 布局:

  1. 标记缝合边:选择需要切开的边
  2. 展开算法:使用 ABF++、LSCM 等算法自动展开
  3. 手动调整:优化布局,减少拉伸

自动 UV 展开算法

1. ABF++(Angle Based Flattening)

基于角度的展开方法,保持角度比例:

python
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)

最小二乘保角映射,保持局部形状:

python
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 投影

基于角度阈值自动分割和展开:

python
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 映射中最常见的问题,可以通过以下方法优化:

python
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 映射中不可避免的问题,可以通过以下策略减少:

  1. 隐藏接缝:将接缝放在不显眼的位置(如底部、内侧)
  2. 接缝对齐:让接缝沿着模型的自然边界
  3. 重叠 UV:对称部分共享 UV 空间

3. 纹理空间利用率

优化 UV 岛的排列,最大化纹理空间使用:

python
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 空间:

code
UDIM 纹理命名规则:
- 1001: UV (0,0) 到 (1,1)
- 1002: UV (1,0) 到 (2,1)
- 1003: UV (2,0) 到 (3,1)
- ...
hlsl
// 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 坐标的纹理映射技术,通过三个方向的投影混合:

hlsl
// 三平面映射
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,适用于大型环境:

hlsl
// 世界空间映射
float2 worldSpaceUV(float3 worldPosition, float scale) {
    return worldPosition.xz / scale; // 从上方投影
}

UV 映射中的常见问题

1. 接缝可见性

问题:UV 接缝处出现明显的纹理断裂

解决方案

  • 使用纹理包裹模式(Wrap/Repeat)
  • 在接缝处添加重叠区域
  • 使用接缝隐藏工具

2. 纹理拉伸

问题:某些区域的纹理被过度拉伸

解决方案

  • 重新展开 UV,减少拉伸
  • 使用多个 UV 通道
  • 应用纹理缩放补偿

3. 纹理空间浪费

问题:UV 岛之间有大量空白区域

解决方案

  • 使用自动打包工具
  • 手动调整 UV 岛位置
  • 使用 UDIM 工作流程

实际工作流程

Blender 中的 UV 映射工作流程

  1. 准备模型

    • 清理拓扑结构
    • 标记缝合边(Ctrl+E → Mark Seam)
  2. 展开 UV

    • 选择所有面(A)
    • 打开 UV 编辑器
    • 执行展开(U → Unwrap)
  3. 优化布局

    • 调整 UV 岛位置
    • 缩放和旋转以减少拉伸
    • 使用 Pack Islands 工具
  4. 导出和测试

    • 导出 UV 布局
    • 在纹理绘制软件中测试

游戏开发中的 UV 最佳实践

  1. 纹理分辨率分配

    • 重要区域使用更高分辨率
    • 不可见区域使用更低分辨率
  2. UV 通道复用

    • 第一通道:漫反射纹理
    • 第二通道:光照贴图
    • 第三通道:细节纹理
  3. 对称性利用

    • 对称模型共享 UV 空间
    • 减少纹理内存占用

程序化 UV 生成

实时 UV 生成

在着色器中动态生成 UV 坐标:

hlsl
// 程序化 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 坐标实现纹理动画:

hlsl
// 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 工具

  1. Blender:免费开源,功能强大
  2. Maya:行业标准,UV 工具丰富
  3. 3ds Max:游戏开发常用
  4. RizomUV:专业 UV 展开工具

自动化工具

  1. UV-Packer:自动 UV 打包
  2. Unfold3D:自动 UV 展开
  3. TexTools:Blender UV 插件

总结

UV 映射是 3D 图形学中的核心技能,掌握它需要理解:

  1. 基本概念:UV 坐标系统、纹理空间、映射类型
  2. 展开技术:手动展开、自动算法、优化方法
  3. 高级技巧:UDIM、三平面映射、程序化生成
  4. 问题解决:接缝处理、拉伸优化、空间利用

通过实践和经验积累,可以创建出高质量的 UV 布局,为后续的纹理绘制和渲染打下坚实基础。

参考资源