计算机图形学八:纹理映射的应用(Normal Maps, Bump Maps, Displacement Maps and Environment Maps)
在上一节内容中,我们详细介绍了纹理映射的概念,以及纹理贴图过大过小带来的种种问题与解决方案,但纹理映射的应用远不止单单作为diffuse的反射系数来表现出不同颜色。本文会详细介绍一些主要的纹理映射的应用及其原理,首先从法线映射和切线空间开始说起。
1 Normal Maps及切线空间
在Blinn-Phong光照模型中,法线向量扮演着重要的一环,不同的法线向量对光照的计算结果有着很大的影响,打个比方,倘若将一个高精度模型法线信息套用在低精度模型之上,会使低精度模型的渲染效果有着巨大的提升。
那么如何做到呢?我们知道Texture上可以存储3维的颜色信息作为漫反射系数,那么自然也就可以存储法线向量的信息!同样利用(u,v)坐标去查询每个点的法线向量,而不使用原来模型法线信息,达到各种不同的效果,这就是Normal Maps。
明白了Normal Maps的原理之后,有一点重要的是,如何在存储这些法线信息呢?一种可选的方法是,存储object space下的法线向量坐标(这会使得法线贴图看起来五颜六色的),好处是取出来转换到世界坐标就可以直接使用,坏处是一但该法线向量的三角形面发生了变形,那么该法线向量就不再正确。这也就引出了第二种方法,存储切线空间之的法线向量坐标(这会使得法线贴图大部分呈蓝色,原因下文会提)。任何空间坐标系都要由3个互相正交的基底向量构成,切线空间也不例外,如下图所示:
其z轴由原来该面上的几何法线n构成,指向物体表面的外侧。x,y轴分别由该面所对应的贴图上U,V增加的两个方向构成,称之为tangant轴和bitangent轴。这样我们称tangant轴(t)、bitangent轴(b)及法线轴(N)所组成的坐标系,即切线空间(tbn)。
法线向量N可以根据原来的模型信息得到,如何去计算T,B呢?
如上图所示,记一个三角形的面的三个顶点分别为p0,p1,p2,并且使用(ui,vi)来表示对应顶点的texture坐标,那么根据顶点坐标的差值,纹理坐标的差值,以及t,b两轴,可以得到如下关系
为了进一步简化公式,设:
那么便可以把第一个公式简化为如下形式:
利用线代知识,将其写为矩阵形式:
相信熟悉线性方程组的同学,已经知道如何解出t,b向量了,两边同乘系数矩阵的逆即可得到:
其中右边式子中的变量全部已知,自然就已经成功求出t,b两轴向量,再加上原几何法线向量n,至此便已经得出了切线空间(tbn)。法线贴图的数据就存储在空间之下,对于没有变动的法线向量就是(0,0,1)而这恰巧也就解释了为什么法线贴图大部分都是蓝色的(因为大部分法线一般不变动)。
具体实施的时候只需利用[t b n]向量组成的矩阵乘以法线贴图上的存储3维信息,即可得到正确的法线向量了。
tips: 1.求出来的t,b两轴并不一定保证垂直,有时候还需再加一步施密特正交化如下:
tips: 2.真正存储的时候只需要t 和 n即可,第三轴可以直接叉乘得到。并且将t和n作为顶点的属性进行存储,正如顶点n是共享该点的面法线均值,t同样是所有共享该点的三角面做计算出来t的均值
2 Bump Maps
Bump Maps其实与Normal Maps十分类似,Normal Maps直接存储了法线信息,而Bump Maps存储的是该点逻辑上的相对高度(可为负值),该高度的变化实际上表现了物体表面凹凸不平的特质,利用该高度信息,再计算出该点法线向量,最后再利用该法线计算光照,这就是Bump Maps的过程,只不过比直接的Normal Maps多了一步从height到normal向量。
那么所需要关心的问题就是,如何从相对高度计算出法线向量呢?
该过程也很容易理解,这里就直接用闫老师课上的Slides作为解答了,2维情况如下:
3维情况可以类推得到:
正如最后一点所标注的,所有计算出来的法线都是局部坐标即切线空间之下,因此还需要左乘[t b n]矩阵转到(世界)相机坐标系之下得到正确法向!
3 Displacement Maps
Displacement Maps其实又与Bump Maps十分类似了,在第二章作者提到了,Bump Maps是逻辑上的高度改变,而Displacement Maps则是物理上的高度改变,二者的区别就在此处,可以通过物体阴影的边缘发现这点:
4 Environment Maps
终于到了最后一点,环境光映射了,顾名思义就是将环境光存储在一个贴图之上。想象这样一个情形,光照离我们的物体的距离十分遥远,因此对于物体上的各个点光照方向几乎没有区别,那么唯一的变量就是人眼所观察的方向了,因此各个方向的光源就可以用一个球体进行存储,即任意一个3D方向,都标志着一个texel:
进一步就像地球仪一样,利用墨卡托投影或是其它类似的方法将球上的信息转换成一个平面上,就得到了环境Texture了:
以下给出分别在光线追踪以及Blinn-Phong模型利用环境映射的伪代码:(不熟悉光线追踪可以掠过该部分伪代码)
可以看到在光线未能撞击物体的时候,会利用光线方向求得展开之后贴图上的(u,v)坐标,再去查询颜色返回。
对于Blinn-Phong模型来说只需增加一项反射的颜色即可,如下:
利用观察方向相对于法线的反射方向去查询环境映射的颜色值。
但是用一个球体来存储环境光有一个比较明显的缺点,仔细观察上文当中展开的那副Texture图可以观察看到,上方和下方均有较为严重的扭曲,因此另外一种存储的方法就是Cube Map,也就是天空盒:
一个天空盒有6幅Texture来表示,明显相对球体少了很多扭曲的情况,但是中间多了一步从方向到面上的计算:
简单来说就是利用方向计算出与对应平面上的交点坐标,剔除平面所对应的一维,剩下来的两维坐标转换到(0,1)范围之内即为(u,v)坐标。
举个例子: 一个方向为(1,2,3)则其与z = 1平面的交点为(1/3,2/3,1) (找最近的平面交点),剔除z轴之后剩下为(1/3,2/3), 在进行(1+x)/2的转换(因为方向存在负值,而uv坐标不存在),则得到z = 1的那一幅Texture上的 uv坐标为(2/3,5/6)。
Reference
[1] Fundamentals of Computer Graphics 4th
[2] GAMES101-现代计算机图形学入门-闫令琪
[3]【D3D11游戏编程】学习笔记二十四:切线空间(Tangent Space)
[4]Foundations of Game Engine Development, Volume 2