理解法线贴图、切线空间、凹凸贴图
法线能用来做什么?#
考虑这样一面墙,砖块的表面非常粗糙,显然不是完全平坦的:它包含着接缝处水泥凹痕,以及非常多细小的空洞。下图中我们可以看到砖块纹理应用到了平坦的表面,并被一个点光源照亮。
可以看到渲染效果非常不真实,因为光照没有呈现出任何裂痕和孔洞,完全忽略了砖块之前的线条。
从物理上分析,想呈现出真实的效果,需要建立正确的网格,把接缝、孔洞和线条都用三角面建模出来。但这样会需要大量的三角形面,制作工艺和性能不可接受。
那我们可以用一些trick的方法,去模拟真实的情况。真实的墙面,相对于一个建模的平面,差别是
面上的点有高度差。在渲染中,面上的点有高度差,近似于法线不同。
每个点用不同的法线光照后,就可以得到近似真实的接缝、孔洞和线条效果。
从下图可以看出,用法线贴图控制法线,可以模拟出非常精细的模型渲染出来的光影效果。
法线如何存储?#
从上一节知道法线对提升效果有很大的帮助,那么法线效果应该存在哪里呢?
存在模型上?肯定不行,因为模型顶点很少,像素很多,顶点法线无法表示像素级别的法线变化。
那就只能存在贴图上了。
法线的数值范围是[-1, 1],而贴图里不方便存储小于0的值,所以把法线的范围映射到[0, 1],在shader中采样贴图的时候再映射到[-1, 1]:
storage = normal * 0.5 + 0.5
normal = texture2D(storage) * 2.0 - 1.0
法线的范围我们知道怎么映射了,我们应该存储什么值呢?
考虑到光照计算一般发生在世界空间中,如果我们将在世界空间中的法线值存到贴图中,当模型出现位移/旋转时,法线就会失效。
可以将模型空间的法线存储到贴图中,那么模型发生位移、旋转、缩放时法线也能正常工作。但是当模型发生形变时(骨骼动画) ,这种法线与模型的表面也不再一致,将会出现错误的渲染效果。
考虑以上的旋转、位移、缩放、形变的各种出问题的情况,我们可以根据三角面建立一个空间,把这个空间下的法线存储到贴图中。这个如何理解呢?
模型的三角形面能确定一个法线,根据这个法线再构建出另外两个基向量,就可以组成一个坐标空间,法线贴图中存储各个点的法线在这个空间下的向量,这样的话法线就只跟表面有关系了。在使用贴图的法线前,构建出这个表面空间相对于模型或世界中的变换矩阵,然后把法线变换到模型或世界空间中,就可以进行光照计算了。这个空间称为纹理空间(texture-space) 或切线空间(tangent-space) 。
那么如何构建这个矩阵呢?
考虑一个模型的一个三角面
通过向量叉乘很容易求出来模型空间下的法线
现在我们只需要再求出另外两个基向量就可以构造切线空间。
为了方便计算,我们定义模型的切线(tangent)和副切线(bitangent),分别与纹理空间的u和v的方向相同,用向量
三个顶点的纹理坐标为分别是
,将三角形两条边
求
所以,
右边的值都是已知的,即可求得
最后组成正交的TBN矩阵,
这就是法线所需要转换将其到模型空间的矩阵,如果需要转换到世界空间,再乘以模型到世界的矩阵即可。
由于这三个向量相互正交,可以通过其中的两个向量求得另外一个向量,所以一般在顶点中存储将
法线贴图(normal mapping)#
法线贴图用来存储表面的法线方向,由上面讲述的知识可知,存储在切线空间中的法线比较实用。如果法线方向没有变动,它的值是(0, 0, 1)。由于法线方向一般不会大浮动变动,只是在z轴附近扰动,我们看到的法线贴图一般都呈蓝色。
在shader里采样法线贴图转换为法线后,再乘以TBN矩阵,就可以将法线变换到模型空间,再变换到世界空间就可以进行光照计算了。
凹凸贴图(bump mapping)#
凹凸贴图也是作用在法线上,只不过它存储的不是法线信息,而是存储面上某个点的相对三角面的高度。高度不同,法线自然也不同,也能渲染出模型的凹凸不平的视觉效果。
所以只需要根据相对高度计算出法线信息,之后就可以用TBN矩阵进行后续的光照计算了。
那么如何根据高度信息计算法线呢?
先考虑一维的情况,黄色的线是相对于表面的高度,原表面的法线为垂直于uv方向,在一维中就是(0, 1),所以已知p的高度,求点p的法线。
我们先求点p的切线,看上面的图,竖直方向是高度h,水平方向是uv中的u,二者单位不一样,没法直接求切线。所以再引入一个常量s,它表示凹凸贴图的最大高度相对于像素宽度的倍数,有
所以,切线方向为
法线为
同理在3维情况下可以得到
所以,在u方向的切线为
在v方向的切线为
所求法线为二者的叉乘
之后按照切线空间的法线流程计算光照就可以了。
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)