法线贴图可以改变物体视觉上的凹凸,但是不能改变凹凸带来的视觉遮挡。
视差需要一张高度图
i.tangentViewDir = normalize(i.tangentViewDir);
i.uv.xy += i.tangentViewDir.xy * _ParallaxStrength;
这样带来的效果是贴图整体上移。
这样做有效地将视角方向投影到纹理表面上。当以90°的角度看直线的时候,切线空间中的视角方向等于表面法线(0,0,1),这导致不需要位移。视角越浅,投影越大,位移效应越大。
因为摄像机向量是在切空间下,而切空间是沿着纹理坐标方向建立的,所以向量V的X和Y分量就可以直接不加换算的用作纹理坐标的偏移量。
这个浮动的效果,其实和单纯的uv+=float2(0.2,0.2)这种是不一样的,这样只是平移,
视差由相对于观察者的透视投影引起,这意味着我们必须根据视角方向来偏移坐标,这对于每个片段程序是不同的。
关于切线空间,可以这样理解,想象自己是切线空间原点,眼睛的前方是切线空间的Z轴,左边是切线tangent方向,头顶是binormal副切线方向。那么观察到的世界就是一个平面了,和二维贴图正好对应。
//视差贴图和法线贴图一致,都是存在于切线空间中的
float4 Parallax (float4 texcoords, half3 viewDir)
{
//这里贴图采样用的g通道,一般采样贴图是纯灰度图的话,哪个通道都是一样的
half h = tex2D (_ParallaxMap, texcoords.xy).g;
//(h-0.5)*_ParallaxScale; 这个h是从贴图中得到的0-1的值,减去0.5就是把区间控制在-0.5到0.5之间,这样就可以让原本
//0.5的位置变成0,就作为基准高度,小于0的就代表低洼的地方,大于0的就代表隆起的部分,
//h = (h-0.5)*_ParallaxScale;
h = h *_ParallaxScale - _ParallaxScale/2;
half3 v = normalize(viewDir);
//v.xy / v.z;这是由相似三角形得出的。0.42这个偏移值也可以作为编辑器变量,
//它主要的目的是为了避免z值太小,产生不正确的结果
float2 offset = h * (v.xy / v.z+0.42);
return float4(texcoords.xy + offset, texcoords.zw + offset);
#endif
}
Steep Parallax Mapping:陡峭视差映射
陡峭视差映射的工作方式在下面的图片上举例。深度被分割成8个层,每层的高度值是0.125。每层的纹理坐标偏移是V.xy/V.z * scale/numLayers。从顶层黄色方块的位置开始检查,下面是手动计算步骤:
1.层的深度为0,高度图深度H(T0)大约为0.75。采样到的深度大于层的深度,所以开始下一次迭代。
2.沿着V方向偏移纹理坐标,选定下一层。层深度为0.125,高度图深度H(T1)大约为0.625。采样到的深度大于层的深度,所以开始下一次迭代。
3.沿着V方向偏移纹理坐标,选定下一层。层深度为0.25,高度图深度H(T2)大约为0.4。采样到的深度大于层的深度,所以开始下一次迭代。
4.沿着V方向偏移纹理坐标,选定下一层
。层深度为0.375,高度图深度H(T3)大约为0.2。采样到的深度小于层的深度,所以向量V上的当前点在表面之下。我们找到了纹理坐标Tp=T3是实际交点的近似点。