高度贴图
视差贴图要用到一副利用R通道储存片元高度的高度贴图,模拟在看到凹凸面的视差。
如图所示,如果这是一个3D的模型,我们沿黄线方向看到的应当是B点,如果是平面则会看到A点。按着这个逻辑,我们只要在贴图的A点显示B的内容,就可以假乱真,用平面贴图达到3D的效果。
所以,我们要做的工作,就是计算出B点,用它替换A点。
顺便一提,视差贴图一般与法线贴图一同使用,相辅相成。
视差计算
深度比高度更容易模拟,所以我们通常使用深度贴图取反来代替高度图。
寻找B点的过程如图所示,从T0点出发,将深度分成N份逐层向下遍历,直到找到一点的深度小于等于该层(如图上的T3),则要找的点就在T2,T3之间,在利用相似三角形近似计算出该点的坐标。
以下是利用这个原理写的函数:
vec2 paracoor(vec2 texcoor,vec3 viewdir)
{
vec2 p=viewdir.xy/viewdir.z*material.heightscale;//视线向量
float layers=mix(20.0,10.0,abs(dot(vec3(0,0,1.0),viewdir)));//层数
vec2 deltacoor=p/layers;
float depth=0.0;
vec2 currentcoor=texcoor;
float currentdepth=1.0-texture(material.parawall,currentcoor).r;
float predepth;
while(depth<currentdepth)
{
depth+=1.0/layers;
currentcoor-=deltacoor;
predepth=currentdepth;
currentdepth=1.0-texture(material.parawall,currentcoor).r;
}
float ratio=(depth-currentdepth)/(depth-currentdepth+predepth-(depth-1.0/layers));
return (currentcoor+ratio*deltacoor);
}
p是视线的向量,viewdir.xy / viewdir.z 是为了使z分量=1,实际上是viewdir.xyz / viewdir.z,但是P是二维向量,所以只取xy。heightscale是一个系数,用来调整视差的强烈程度。
为了提高算法效率,当视线方向比较正的时候取样层数可以少一些,比较斜的时候应该多一些,所以用了一个mix函数来改变层数。
其他部分与法线贴图基本相同。
局限
一个3D模型的表面积显然是要大于一个平面的,所以3D上的信息一定大于平面。视差贴图实际上是反复使用临近的信息来补足丢失的信息,所以只有当模拟的3D物体颜色信息本身就重复性比较大时才显得比较真实。所以一般还是用来模拟墙壁等细节要求较低的地方。