Bump Mapping介绍
物体细节的表达
-
宏观尺度
- 特征可能覆盖很多个像素
- 由顶点、三角形、其他几何图元表示
- 例如:角色的四肢、头部
-
中观尺度
- 特征可能覆盖几个像素
- 描述了宏观和微观尺度之间的特征
- 包含的细节比较复杂,无法用单个三角形进行渲染
- 细节相对较大,可以被观察者看到几个像素以上的变化
- 例如:人脸上的皱纹、肌肉的褶皱、砖头的缝隙
-
微观尺度
- 特征可能是一个像素
- 通常在着色模型,写在像素着色器中,并且使用纹理贴图作为参数
- 模拟了物体表面微观几何的相互作用
- 例如:
- 有光泽的物体表面是光滑的、漫反射的物体,在微观下表面是粗糙的
- 角色的皮肤和衣服看起来也是不同的,因为使用了不同的着色模型/不同的参数
Bump Mapping 定义
-
对物体表面贴图进行变化然后再进行光照计算
-
在几何细节不够的情况下,像素着色时不再从原始模型读取法线等信息,而是通过额外的纹理读取并替代进行光照运算,从而达到以假乱真的效果。(对每个待渲染的像素在计算照明之前都要加上一个从高度图中找到的扰动,来模拟凹凸不平的视觉特征)
-
例如可以:
- 给法线分量添加噪音
- 在一个保存扰动值的纹理图中进行查找
-
是一种提升物体真实感的有效方法,但却不需要额外的提升物体的几何复杂度
-
在提升物体的表面细节或者表面的不规则性方面有显著效果
Bump Mapping(凹凸映射)的分类
- 上述方法都是广泛的被使用,来增加模型的细节效果,或者用来做特殊的画面表现效果。
- 最常用的是法线映射,一般的增加法线贴图后,会对局部的物体表面进行法线扰动,进而改变明暗关系,从而达到增加表面细节的效果。
Normal Mapping 法线映射
原理
-
法线贴图是一张存有物体局部表面法线信息的一张贴图
-
计算光照时使用:
- 读取法线图
- 获取到当前像素点的法线信息
- 结合光线信息进行光照计算(一般是替代几何法线,但也有混合插值的做法)
-
好处:
- 让物体表现出更加丰富的细节
- 随着光照方向的变换实时变化
- 是普通纹理贴图不能表现出的
-
生成法线贴图:
- 一般由高模映射到对应的底模上来生成
- 但像金属,木头等这些细节丰富的物体,可借助程序化的软件如:Photo Shop,Substance Designer等生成对应法线贴图
法线纹理的存储
可储存模型空间的法线信息或切线空间下的法线信息,一般用后者
切线空间
- 以物体表面的切线,副切线和法线组成的几何空间
- 每个顶点都有属于自己的切线空间,这个空间的原点是顶点本身,z轴是顶点的法线方向(n),x轴是顶点的切线方向(t),y轴有前边两个轴叉乘而来,被称为副切线(b)或者副法线。
- 一般存储顶点法线和切线,副切线可以通过叉乘计算,详见learn-opengl法线贴图部分
世界空间与切线空间的转换
-
在计算光照时,需要把相关的向量放在统一的坐标系下进行运算。此时就需要不同空间坐标的转换矩阵(世界空间转切线空间/切线空间转世界空间)
-
将世界坐标系下顶点的法线(Normal)、切线(Tangent)、副切线(Bitangent)作为切线空间坐标系的正交基。用这三个向量的标准正交基构建转换矩阵。对应关系为:法线方向作为z轴,切线方向作为x轴,副切线方向作为y轴
-
转换矩阵(可回顾下之前的线代部分):
- 切线空间到世界空间的转换矩阵为一个3×3的旋转矩阵,一般称为TBN矩阵
- 世界空间到切线空间的转换矩阵为上述TBN矩阵的逆矩阵,因为是正交矩阵,所以逆矩阵就是它的转置矩阵
切线空间的优点
- 自由度高。
- 模型空间下是绝对法线信息(仅可以用在创建它时的那个模型)
- 而切线空间下的是相对法线信息,是对当前物体法线的扰动。(可以复用)
- 可进行uv动画。
- 比如:移动uv坐标来实现凹凸移动效果
- 可以重用法线纹理。
- 比如:一个立方体,6个面可以用一张法线贴图
- 可压缩。
- 由于切线空间下贴图中法线的Z方向总是正方向(模型空间下可以是负的),那么我们只存XY(切线和副切线)就能推出Z(法线)了,可以少存一个。
法线贴图在Unity中的压缩格式
- 在非移动平台上,会把法线贴图转化为DXRT5nm格式
- 这个格式只有两个有效GA通道,分别对应法线的y、x分量
- 在移动平台上,使用传统RGB通道
- 法线纹理中存的就是表面法线,由于法线分量范围为[-1,1],像素的分量范围为[0,1] ,因此我们通常需要做一个映射:pixel =(normal + 1)/ 2,解码时就要做一个反向的操作
Parallax Mapping 视差映射
原理
法线贴图只能改变法线从而改变光照,无法使模型表面产生遮挡效果
视差贴图Parallax Mapping是一种类似法线贴图的技术。它用于提高模型表面细节并赋予其遮挡关系,可以和法线贴图一起使用。
视差贴图需要引进一张新的贴图——高度图。高度图一般是用于顶点位移使用的(位移/置换贴图 Displacement mapping),但性能消耗高,需要大量三角形。视差贴图的核心是改变纹理坐标来改变遮挡关系,视差贴图就利用储存模型信息的高度图,利用模型表面高度信息来对纹理进行偏移。
实现
-
原理上和法线贴图类似,是欺骗眼睛的做法,在着色阶段做手脚而不增加几何细节
-
我们的模型在切线空间下,所有的点都位于切线和副切线组成的平面内(图中0.0点),但我们期待物体要有更丰富的细节(又不想增加几何负担)
- 如果不使用视差贴图,正常着色会采样片元A点(黄色)的信息,就是图中的Ha
- 实际使用视差贴图时,真实的情况应该是视线和A点延长线和物体的交点,也就是B点,相应的就是Hb
-
视差映射的具体算法:如何在知道A的uv值的情况下,算出B的uv值
-
求图中d偏移量的长度
-
偏移量的获得:用近似的方法去求解
- 首先拿A的高度信息进行采样,得到物体表面距离水平面(0.0)的深度值Ha
- 用深度值Ha和视线的三角关系算出物体上等比的偏移方向,算出近似的B点(可以看到图中近似点B和实际点B还是有挺大差距的,所以模拟度比较低)
-
得到偏移之后B点的uv,再去对法线贴图进行采样、计算时,就不会采样A点了,而是B点
-
1 | float height = 1 - tex2D(_HeightMap, i.uv2.xy).r; |
- 理解:视差贴图是如何产生遮挡效果的
- 当视线看到的是A点这样深度比较大的,那么视差贴图计算出的偏移值也是非常大的,这样A点最终被渲染出来的机会就比较小(偏移后就被采样到其他点上了)
- 当视线看到B点这样深度比较小的点,计算出来的偏移就比较下,甚至原来点的附近,所以被采样的机会就比较大
- 深度大的点很容易被深度小的点覆盖掉,这样就会表现出遮挡的效果
改进:陡视差映射 Steep Parallax Mapping
-
原理:
- 将物体表面分为若干层,从最顶端开始采样,每次沿着视角方向偏移一定的值
- 如果当前采样的层数,大于实际采样的层数,就停止采样。
- 例如图中D点,采样到0.75层,实际是0.5层,就停止采样,返回偏移坐标
-
实现(learn-opengl视差贴图)
- 首先对A点采样,得到深度大约为0.8的位置,而其对应视线深度为0.0,不符合我们的基本思想,继续采样
- 采样B点,深度为1,视线深度为0.25,不符合,继续采样
- 采样C点,深度大约为0.8,视线深度为0.5,不符合,继续采样
- 采样D点,采样深度为0.5,视线深度约为0.75,符合上述的条件,认为是比较合理的一个偏移点,就返回结果(return)。
1 | float2 SteepParallaxMapping(float2 uv, real3 viewDirTS) |
-
问题:
- 在于分层机制,如果
- 分层多,性能开销就会大
- 分层小,渲染锯齿就比较明显
- 一种做法:可以根据视角v和法线n的角度限定采样层数
- 锯齿问题会在浮雕贴图上做改善
- 在于分层机制,如果
Relief Mapping 浮雕映射
原理
- 视差贴图在使用较大的uv偏移时存在失真。
- 浮雕贴图更容易提供更多的深度,还可以做自阴影、AO效果
- 可以看到浮雕的凹凸深度明显更大,且凹凸有自阴影效果
实现
-
浮雕映射一般用射线步进和二分查找来决定uv偏移量
- 第一步:射线步进部分,和视差贴图一样
- 二分查找部分:通过射线步进找到合适的步进后,在此步进内使用二分查找来找到精确的偏移值
-
为什么不直接使用二分查找?
- 会产生比较大的误差
- 如果直接使用二分查找,在深度0和1的中间的1点,进一步为2点 -> 3点 ->Q点。但我们要的结果是P点,可以看到结果很明显是错误的
1 | float2 ReliefMapping(float2 uv, real3 viewDirTS) |
改进:视差闭塞贴图(POM = Parallax Occlusion Mapping)
-
相对于浮雕贴图,不同之处在于最后一步
- 浮雕贴图是在确认最后步进之后进行二分查找(在迭代次数比较多的情况下,还是挺耗的)
- 视差闭塞贴图是在最后步进的两端uv值进行采样(下图红色箭头),采样之后再对这两个结果进行插值,插值的结果作为P点最终的偏移值
-
优点:
- 相对于浮雕映射,性能更好(最后只做插值,而浮雕要做二分查找)
- 相对于陡视差贴图,精确性更好
-
要求:
- 因为最后要做插值,所以要求表面是相对比较平滑/连续的,如果有莫名的凸起结果可能会出错
参考链接
- learnopengl法线贴图
- learnopengl视差贴图
- Real-Time Rendering 3rd》 提炼总结 (五) 第六章 · 纹理贴图及相关技术
- UE4 材质视差算法
- Unity Shader入门精要
- 大佬笔记
作业
结合之前先行版基础渲染光照介绍,尝试实现法线、视差、浮雕映射
课程中给出的实例代码,一些内置URP函数的使用值得学习!
1 | Shader "Bump Mapping" |
还可以实现: