为什么需要法线贴图
物体模型由成百上千个平坦的三角形围成.
三角形上附加纹理的方式来增加额外细节. 提升真实感.
但是近看时,就有问题了.变成平面.缺乏细节.
原因:
- 物体表面并非平坦的. 而是表示出无数(凹凸不平的)细节.
如果以光的视角来看这个问题:
是什么使表面被视为完全平坦的表面来照亮?
- 答案会是 表面的法线向量。
从光照的角度考虑:
只有一件事决定物体的形状.那就是垂直与它的法向量!
- 如果一个表面只有一个法线向量.那么表面就按这个法向量的方式被一种方式照亮.
- 如果每个Fragment拥有不同的法线. 就能对细微的细节进行改变.
每个fragment使用了自己的法线,可以让光照相信一个表面由很多微小的(垂直于法线向量的)平面所组成,物体表面的细节将会得到极大提升。
这种每个fragment使用各自的法线替代一个面上所有fragment使用同一个法线的技术叫做 法线贴图(normal mapping)或凹凸贴图(bump mapping)
法线贴图
为使法线贴图工作,我们需要为每个fragment提供一个法线。
范围转换
法线向量的范围在-1到1之间
由于纹理的rgb的颜色在[0,1]之间.我们需要把其映射到 [-1,1]之间.
// 从法线贴图范围[0,1]获取法线
normal = texture(normalMap, fs_in.TexCoords).rgb;
// 将法线向量转换为范围[-1,1]
normal = normalize(normal * 2.0 - 1.0);
很多法线图都是蓝色的.
因为基本上法线都是指向正z轴.
方向问题
- 法线图的方向的固定的. 但是法线贴图可以贴到不同的部位上.
- 若不同,那么法线贴图可能就会错误.
有一个可行的方法就是 为每个表面制作一个单独的法线贴图。
但是如果模型有无数个朝向不同的表面. 那么还是不可行.
另一个稍微有点难的解决方案是,
在一个不同的坐标空间中进行光照
这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;
所有的光照向量都相对与这个正z方向进行变换。这样就能 始终使用同样的法线贴图,不管朝向问题。
这个坐标空间叫做 切线空间(tangent space)
切线空间
法线贴图中的法线向量在切线空间中, 法线永远指向正Z方向.
切线空间是位于三角形表面之上的空间
-
法线相对于单个三角形的本地参考
-
它们都被定义为指向正z方向,无论最终变换到什么方向
-
使用一个 特定的矩阵 能将 本地/切线空间中的法线向量转成世界或视图坐标,使它们转向到最终的贴图表面的方向
-
法线贴图被定义在切线空间中,解决问题的方式是计算出一种矩阵,把法线从切线空间变换到一个不同的空间,这样它们就能和表面法线方向对齐了
-
切线空间的一大好处是我们可以 为任何类型的表面计算出一个这样的矩阵,把切线空间的z方向和表面的法线方向对齐
这种矩阵叫做 TBN矩阵 这三个字母分别代表tangent、bitangent和normal向量。
这是建构这个矩阵所需的向量。需要三个相互垂直的向量,它们沿一个表面的法线贴图对齐于:上、右、前
已知上向量是表面的法线向量。
右和前向量是 切线(Tagent)和副切线(Bitangent)向量
计算出切线和副切线并不像法线向量那么容易。
切线和副切线与纹理坐标的两个方向对齐
通过法线贴图我们已经有了N.
需要计算B和T.计算需要什么信息:
- 用一个三角形的顶点和纹理坐标 (因为纹理坐标和切线向量在同一空间中)
- 这需要计算出 delta纹理坐标矩阵的逆矩阵
从技术上讲,顶点着色器中无需副切线。
所有的这三个TBN向量都是相互垂直的用T和N向量的叉乘计算出副切线:vec3 B = cross(T, N);
切线空间法线贴图
-
先将所有TBN向量变换到我们所操作的坐标系. (如世界空间. 那么就是乘以 Model矩阵). 但其实也可以不做变换. 我们只关心向量的方向.
-
创建实际的TBN矩阵. vs_out.TBN = mat3(T, B, N);
-
现在有了 TBN矩阵. 如何使用:
- 直接使用TBN矩阵. 这个矩阵可以把切线坐标空间的向量转换到世界坐标空间. 直接将采样得到的法线向量乘以TBN矩阵. 这样所有法线和其他光照变量就在同一个坐标系中了.
- 也可以使用TBN矩阵的逆矩阵. 这个矩阵可以把世界坐标空间的向量转换到切线坐标空间。
One More Thing
TBN向量可能会不能互相垂直,这意味着TBN矩阵不再是正交矩阵了。法线贴图可能会稍稍偏移
- 使用叫做格拉姆-施密特正交化过程(Gram-Schmidt process)的数学技巧,我们可以对TBN向量进行重正交化,这样每个向量就又会重新垂直了