1. 法线贴图(Normal Mapping)
法线贴图是一种在3D图形渲染中广泛使用的表面细节增强技术。它通过存储每个像素的法线信息来模拟表面的细微凹凸细节,而无需增加实际的几何复杂度。
1.1. 工作原理
纹理存储
- 使用RGB通道存储法线向量的XYZ分量
- 通常呈现出偏蓝色的外观(因为默认法线指向Z轴)
- 每个像素都包含表面法线方向信息
切线空间
- 法线贴图通常在切线空间(TBN空间)中定义
- 包含切线(Tangent)、副切线(Bitangent)和法线(Normal)
- 便于将法线信息应用到任意曲面
1.2. 优势
高效率
- 不增加模型的多边形数量
- 能够呈现出丰富的表面细节
- 性能开销相对较小
灵活性
- 可以与其他贴图技术结合使用
- 适用于静态和动态物体
- 便于美术人员制作和修改
1.3. 应用场景
- 游戏角色的皮肤细节
- 建筑物表面的砖块纹理
- 布料的褶皱效果
- 地形的细节增强
1.4. TBN空间
TBN空间(Tangent Space)是法线贴图中的一个关键概念,它由三个相互垂直的向量构成:切线(Tangent)、副切线(Bitangent)和法线(Normal)。
T - 切线(Tangent)
- 沿着纹理坐标U方向的向量
- 通常与物体表面相切
- 在顶点着色器中计算
B - 副切线(Bitangent)
- 沿着纹理坐标V方向的向量
- 与切线和法线都垂直
- 可以通过叉积计算:B = N × T
N - 法线(Normal)
- 垂直于物体表面的向量
- 决定了表面的朝向
- 由模型数据提供
1.5. TBN矩阵的作用
// TBN矩阵的构建
mat3 TBN = mat3(T, B, N);
主要用途:
- 将法线贴图中的法线向量从切线空间转换到世界空间
- 保证法线方向在任意曲面上都正确
- 使法线贴图可以在不同的表面上重复使用
1.6. 优势
- 空间一致性:保证法线方向在任何表面都能正确表现
- 可重用性:同一个法线贴图可以应用到不同的模型上
- 精确性:提供了准确的法线方向计算
1.7. 实现流程
可以在顶点着色器中计算TBN矩阵,并在片段着色器中应用它,也可以在片段着色器中计算TBN矩阵。
以下代码展示了如何在片段着色器中计算TBN矩阵:
layout (binding =0) uniform sampler2D normalMap; // 法线贴图
//uniform sampler2D heightMap; // 高度贴图
// 计算法线的函数
// normal: 顶点的法线向量
// tangent: 顶点的切线向量
// texCoord: 纹理坐标
vec3 calcNormal(vec3 normal, vec3 tangent, vec2 texCoord)
{
// 使用Gram-Schmidt正交化计算切线T
// 确保切线与法线垂直
vec3 T = normalize(tangent - dot(tangent, normal) * normal);
// 通过叉积计算副切线B
// T和N叉积得到B,确保三个向量相互垂直
vec3 B = normalize(cross(T, normal));
// 从法线贴图中获取法线数据并转换到[-1,1]范围
vec3 N = normalize(texture(normalMap, texCoord).xyz * 2.0 - 1.0);
// 构建TBN矩阵
// 用于将切线空间的法线转换到世界空间
mat3 TBN = mat3(T, B, N);
// 从法线贴图中获取法线数据
vec3 retrivedNormal = texture(normalMap, texCoord).xyz;
// 将法线从[0,1]范围转换到[-1,1]范围
retrivedNormal = normalize(retrivedNormal * 2.0 - 1.0);
// 使用TBN矩阵将法线从切线空间转换到世界空间
vec3 newNormal = normalize(TBN * retrivedNormal);
return newNormal;
}
1.8. 进一步说明切线向量
实际中,切线向量通常由模型数据提供,例如顶点法线、顶点切线等。这些数据通常存储在模型文件中,例如OBJ文件。
如果模型中没有提供切线向量,就需要通过计算得到。对于表面可导的曲面,可以通过计算得到切线向量。而如果 曲面不可导,就需要使用其他方法来计算切线向量。一种折中的方式是将每个顶点指向下一个顶点的向量作为切线向量。但这样会造成顶点法线与切线向量不垂直,从而影响法线贴图的效果。因此,在着色器中计算切线向量时,需要使用一些技巧来保证切线向量与顶点法线垂直。
// 确保切线与法线垂直
vec3 T = normalize(tangent - dot(tangent, normal) * normal);
1.9. 法线贴图内容说明
法线贴图通常由RGB三个通道组成,分别表示法线向量的X、Y、Z分量。每个分量取值范围为[-1,1],但实际文件存贮为[0,1],所以需要经过转换。
// 法线贴图中的法线向量从[0,1]范围转换到[-1,1]范围
retrivedNormal = normalize(retrivedNormal * 2.0 - 1.0);
下图为法线贴图
法线贴图呈现蓝色的原因
- 法线向量的存储方式
- RGB通道分别对应XYZ轴向
- 值范围从[-1,1]映射到[0,1]存储
- 转换公式:color = normal * 0.5 + 0.5
- 默认法线方向
- 在切线空间中,默认法线指向Z轴正方向(0,0,1)
- 转换到[0,1]范围后变为(0.5,0.5,1.0)
- 这导致蓝色通道(Z轴)的值最大
- 颜色表现
- R通道(X)≈0.5:中等红色
- G通道(Y)≈0.5:中等绿色
- B通道(Z)≈1.0:最大蓝色
- 三个通道叠加后呈现出偏蓝色的外观
2. 高度贴图(Height Mapping)
高度贴图是一种使用灰度图来存储表面高度信息的技术,用于在渲染时动态改变表面几何形状。
2.1. 工作原理
纹理存储
- 使用单通道灰度图存储高度信息
- 黑色(0.0)表示最低点
- 白色(1.0)表示最高点
- 灰度值表示中间高度
置换映射
- 根据高度值沿法线方向移动顶点
- 可以实际改变几何形状
- 需要较高的网格分辨率
视差映射
- 不改变实际几何形状
- 通过调整纹理坐标创造深度错觉
- 性能消耗较小
2.2. 优势
真实的凹凸效果
- 可以产生实际的几何变化
- 在边缘处有正确的轮廓
- 可以产生自遮挡效果
易于制作和编辑
- 使用常规图像编辑工具即可创建
- 直观的黑白高度表示
- 便于美术人员使用
2.3. 应用示例
// 顶点着色器 中得到的原始顶点位置为vertPos,经过处理后的顶点位置为p
vec3 p=vertPos+ vertNormal*texture(heightMap,vertTexCoord).r*0.2;
2.4. 与法线贴图的区别
几何影响
- 高度贴图可以实际改变几何形状
- 法线贴图只改变光照计算
资源消耗
- 高度贴图需要更多的顶点数据
- 法线贴图主要影响像素着色
效果表现
- 高度贴图可以产生真实的凹凸轮廓
- 法线贴图在边缘处效果有限
以下是高度图 (灰度图)
以下是示例中采用的纹理图,为简便起见,只是将高度图加了绿色