一:Linear Blending 代码实现:
1 2float3 normal_1 = tex2D(normal_tex1, uv).xyz * 2.0f - 1.0f; 3 4float3 normal_2 = tex2D(normal_tex2, uv).xyz * 2.0f - 1.0f; 5 6float3 n = normalize(normal_1 + normal_2); 这是一种非常暴力的方法,实现起来也是最简单的(相加后再归一化即可);虽然最终可以实现混合法线贴图的效果,但是会导致混合后的法线变平,细节也就会更加模糊
产生这种结果的原因有两个,可以分别从几何维度与物理维度的方向来分析:
1.几何维度:
法线本质上是定义在球面上的向量,当我们使用线性的公式对两个法线向量进行混合时,计算的路径实际上是沿着“弦”而非“弧”进行的;这会导致两个法线向量相加得到的中间向量的模始终小于 1,虽然可以通过normalize 将模长拉回至 1,但这又会使得“** 角速度非均匀** ”,表现在混合后的法线上就是:混合结果在中间部分的变化比两端更慢,并且高频的法线会在这种非线性的映射中被抹平,产生“细节模糊”;细节模糊的另一个原因是——当两个法线的方向差异过大(接近180度,也就是反向)时,这样的线性混合得到的结果会趋近于垂直向上,丢失了原有的细节
2.物理维度:
法线贴图存储的并不是颜色,而是物体表面的偏导数(斜率),如果使用处理颜色的办法去混合两个法线,只会导致数据的平均,而不是细节的叠加
e.g. 有两张法线贴图,一张代表“砖块的粗糙纹理”,另一张代表“划痕”,线性混合的行为逻辑是:“我有 50% 像砖块,50% 像划痕”, 但真实的逻辑应该是:“在砖块的斜率基础上,叠加划痕的斜率”
代码实现:
1 2float3 normal_1 = tex2D(normal_tex1, uv).xyz * 2.0f - 1.0f; 3 4float3 normal_2 = tex2D(normal_tex2, uv).xyz * 2.0f - 1.0f; 5 6float3 n = normal_1 < 0.5 ? 2 * normal_1 * normal_2 : 1 – 2 * (1 - normal_1) * (1 - normal_2); 7 8n = normalize(n * 2 - 1); 相比于线性混合,Overlay Blending 是对两张法线贴图进行了类似 PS 中“正片叠底”的处理,虽然 overlay 对于增强二维图像的对比度又很好的效果,但是法线贴图本身并不应该被当作颜色贴图进行处理;overlay 并不理解法线贴图存储的是斜率信息,因此他会对x、y、z分量无差别地进行非线性缩放,这会导致严重的数学错误与视觉伪影
三:Partical Derivative Blending 代码实现:
1 2float3 normal_1 = tex2D(texBase, uv).xyz*2 - 1; 3 4float3 normal_2 = tex2D(texDetail, uv).xyz*2 - 1; 5 6float2 pd = normal_1.xy/normal_1.z + normal_2.xy/normal_2.z; //Add the PDs 7 8float3 n = normalize(float3(pd, 1)); 根据导数的线性性质:
$$
\frac{\partial F_{total}}{\partial x} = \frac{\partial f_1}{\partial x} + \frac{\partial f_2}{\partial x}
$$可以得知要想混合两个法线,需要对它们在切空间中对应的表面梯度进行线性累加,然后将合成后的梯度重新投影回法线空间
...