一、凹凸贴图Bump Mapping
- 把物体的细节分为三种尺度
- 宏观尺度(覆盖很多像素)
- 由几何图元来表示
- 中观尺度(覆盖少量像素)
- 细节复杂,无法使用单个三角形渲染,并且足够大
- 微观尺度(可能覆盖小于一个像素)
- 在着色模型当中表现,模拟物体表面微观几何形状的相互作用
- 宏观尺度(覆盖很多像素)
凹凸映射是模拟中观尺度的常用方法之一,能够让观察者感知到比几何模型尺度更小的细节
基本思想:在纹理中把尺度细节相关的信息编码进去,在着色过程中用受到干扰的表面代替真实表面,就让表面看起来具有小尺度的细节。
总之,凹凸贴图是对物体表面贴图进行变化再进行光照计算的一种技术。(增加物体真实感,但不需要额外的几何复杂度)
- 分类
- 法线贴图
- 视差贴图
- 浮雕贴图
在这三种技术中都会用到法线(贴图)
二、法线贴图Normal Mapping
法线贴图是一张存有物体局部表面法线信息的贴图。
计算光照时,程序读取法线图,并获取当前着色点的法线信息,结合光照信息进行光照计算。
法线贴图一般由高模映射到对应的底模上来生成,但像金属、木头等细节丰富的物体,可借助程序化软件如:Photoshop,Substance Designer等生成对应法线贴图
切线空间
法线的储存一般放在模型的切线空间中
- 切线空间
- 物体表面切线、副切线、法线方向为基,组成的几何空间
- 读取切线空间法线,需要将法线从切线空间转换到世界空间

世界和切线空间转换
切线空间坐标系的正交基是世界空间下的顶点法线(N)、切线(T)、副切线(B),法线为z轴,切线为x轴,副切线为y轴
构建一个3x3的矩阵做空间向量的坐标系转换。
$$
TBN = \begin{bmatrix}T_x&B_x&N_x\
T_y&B_y&N_y\
T_z&B_z&N_z\
\end{bmatrix}\
\ \
TBN^{-1}=TBN^T=\begin{bmatrix}T_x&T_y&T_z\
B_x&B_y&B_z\
N_x&N_y&N_z\
\end{bmatrix}\
$$
想不清哪个是世界-切线,哪个是切线-世界,考虑一个单位阵,左乘矩阵,看看会变成什么就知道了。
- 切线空间的好处
- 切线空间记录的是相对的法线信息,对于一个物体表面记录的法线扰动,可以同样应用到球形物体上(植物的光照处理),但是模型空间记录法线就是绝对的,只能在该物体上用。
- 方便制作UV动画,贴图采样变化一致
- 法线纹理可重用
- 便于计算储存,0-1的储存映射范围,知道两个可以计算另一个
- Unity中法线贴图的压缩格式
- 非移动平台,unity会把法线贴图转换成DXRT5nm格式,这种格式只有两个有效通道AG通道,可以节省空间
- 在DXRT5nm格式中,AG通道分别储存对应法线的x,y分量,z分量需要通过一个简单的计算求得。
- 移动平台,unity使用传统RGB通道
- 非移动平台,unity会把法线贴图转换成DXRT5nm格式,这种格式只有两个有效通道AG通道,可以节省空间


三、视差贴图Parallax Mapping
法线贴图只能改变法线而改变光照,无法使模型表面产生遮挡效果
视差贴图Parallax Mapping是一种类似法线贴图的技术。它用于提高模型表面细节并赋予其遮挡关系,可以和法线贴图一起使用。
视差贴图需要引进一张新的贴图——高度图。高度图一般是用于顶点位移使用的(位移/置换贴图 Displacement mapping),但性能消耗高,需要大量三角形。视差贴图的核心是改变纹理坐标来改变遮挡关系,视差贴图就利用储存模型信息的高度图,利用模型表面高度信息来对纹理进行偏移。

在着色时,模型在切线空间下所有点都在切平面内(0.0),核心就是对于要计算的片元A时,真正应该计算的点是视线与物体的“实际”交点B点。
要计算B点,就需要AB两点在平面上的UV偏差,为了简便,采取近似计算的方法,根据高(深)度图及切线空间下视角方向,近似求解偏移量,视角方向(v)与切平面的正切值与A点的高度值相乘来近似求解,并通过一个缩放值来控制。(有比较大的误差,必须要用这个scale来调整)
$$
d = \frac{v.xy}{v.z}\cdot ha\cdot scale
$$
陡峭视察映射Steep Parallax Mapping
陡峭视察映射也是近似,但更准确一些
陡峭视察映射将深度分为等距的若干层,从顶端开始采样,并且每次沿视角方向偏移一定值,若当前层深度大于采样出的深度,则停止检查并返回结果
(有点ray marching的感觉,那其实在优化上也可以借鉴一下分级采样?https://xzyw7.github.io/post/CbZTf-uM4/#real-time-global-illuminationscreen-space)
也可以根据v和n的角度来对采样层数进行控制
四、浮雕贴图Relief Mapping

视差贴图在使用较大的uv偏移时存在失真。
浮雕贴图更容易提供更多的深度,还可以做自阴影、AO效果
实现方法
浮雕映射一般采用射线步进、二分查找来决定uv偏移量
第一种使用射线步进来查找可能的交点(直接用二分查找可能漏掉较薄的区域导致结果不准确),确定交点位于哪一个步进内。之后在该步进内使用二分查找快速确定交点位置,最后返回结果,偏移贴图。
- 解决最后一步二分查找性能开销问题
- 视差闭塞贴图(Parallax Occlusion Mapping)
- 在步进后,分别对步进两端uv值采样,对结果插值,作为p点的结果(插值导致表面平滑效果更好)
作业
结合先行版基础渲染光照介绍(一)将本次课所讲的案例结合进先前的光照效果
这里就4个案例嘛
法线贴图
左一:standard shader
左二:custom shader,使用法线贴图
右一:custom shader,无法线贴图
1 | struct v2f { |
不想像入门精要那样传一整个矩阵,我们可以传递的变量也是有限的,甚至可以只传tangent,副切线用叉乘计算。甚至也可以用之前的ddx和ddy的trick来计算。
https://xzyw7.github.io/post/zezxM-QCJ/#ddxddy%E4%B8%8E%E6%B3%95%E7%BA%BF%E8%B4%B4%E5%9B%BE
(Tips:有注意到在learnopengl中有描述,在一些网格较大的时候,出现TBN不互相垂直的情况,可以用施密特正交化来解决。)
视差贴图
这个时候发现……狮子模型这个素材没有高度图……还得换个素材……
1 | fixed3 LightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));//normalize(_WorldSpaceLightPos0.xyz);// |
视差贴图在视线接近垂直的时候效果还是很好的,但是正如learnopengl中所说,当从一个角度看过去的时候,会有一些问题产生(和法线贴图相似),陡峭的地方会产生不正确的结果。并且它的效果非常依赖于_heightScale这一参数
陡峭视差贴图
1 | float2 steepParallaxMapping (float2 uv, float3 viewDir) { |
中间一直出现的报错“unable to unroll loop”,给tex2D改成tex2Dlod或tex2Dgrad就好了
参考
https://zhuanlan.zhihu.com/p/391443312
https://zhuanlan.zhihu.com/p/144434084
https://stackoverflow.com/questions/57994423/why-i-cant-use-tex2d-inside-a-loop-in-unity-shaderlab
tex2D只能从“均匀控制流”调用,因为它必须通过计算“导数”来计算LOD。tex2Dlod没有,因为您提供了LOD。
tex2Dlod和tex2Dgrad都能指定纹理层,所以能够在循环中调用。
浮雕贴图
1 | float2 steepParallaxMapping (float2 uv, float3 viewDir) { |
视察闭塞贴图
1 | float2 left = currentUV; |
POM是肉眼可见的效果不错(上:SPM,中:RPM,下:POM),RPM就不太能看得出变化了,但其实还是有的



参考资料
[1] https://www.bilibili.com/video/BV1Ub4y1Z765 【技术美术百人计划】图形 2.5 BUMP图改进
[2] Unity Shader入门精要 p146-155
[3]
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/05%20Parallax%20Mapping/