Parallax Mapping

首先,我们要知道视差贴图Parallax Mapping的作用,为什么要引入它。

对于一张原本2D的图片,我们通过使用法线贴图,使其表面表现出了一定的凹凸感,细节感。但是还是可能会出现一些问题,法线贴图只能通过明暗来表现凹凸,同时它忽视了现实中的一些效果。比如说遮挡的效果,只应用了法线贴图的纹理无法塑造出正确的遮挡效果(这里的遮挡效果要和深度测试区分开,你也可以说这种效果为“更加强烈”的凹凸感,并且这种凹凸感还能调整),顶多明暗变化一下就完了。

图形学是基于现实的模拟,如此问题当然不能容忍,于是就有了后来的视差贴图。


我们给出一张图片

Parallax Mapping

 如上所示,在应用了法线贴图之后,我们能看到的平面就是红线,但实际上的平面还是平的,红线所代表的平面实际上是不存在的。

由于这个特性,当我们的视线看向点a的时候,实际上看到的却是点b的纹理。想要获得更加真实的效果的话我们就需要将b点纠正到a点。当然,这里的纠正到a点并不是指真的纠正到a点,实际上因为红线(即表面的凹凸)是不存在的,所以“纠正”的意思其实是纠正到a点对应的纹理像素(texel),也就是经过a点作一条垂直于平面的直线,交点为纹理像素的位置。然后再通过应用法线贴图,创造出看到红线表面。

为了方便计算,我们将以下的计算都放到切线空间中。

那么问题来了,我们如何将b点纠正到a点(所对应的纹理像素)呢?这个时候就引入了高度贴图height Map,它用颜色通道的方式记录了平面上各个点的凹凸程度,黑色(0)不凸起,白色(1)完全凸起。具体怎么应用,请看下图:

Parallax Mapping

 首先我们在高度贴图中获取b点的高度(当然是我们赋予它的高度,而不是这东西就有这么高,请永远记住,实际的平面一直是2D的,没有任何凸起),然后沿着视线作相同长度的向量,再过此向量的终点,作垂直于实际平面的垂线,垂线与平面的交点up就是我们要采样的纹理像素(这个纹理像素会被应用于顶点位置的b点)。

缺点也是有的,看上图就知道up和ua有距离,并且这个距离还会随着视线的改变而改变,我们基本上不能得到真正的ua纹理像素。当物体的表面高度变化极快的时候,这个距离还会进一步增大。

于是在这个基础上,提出了改进的方法:陡峭视差映射(Steep Parallax Mapping)。

我们不再只通过一个值来接近a(ua),毕竟那样误差太大。取而代之,我们使用“逼近”的方式来靠近a(ua),从而减少误差。具体方案如下:

Parallax Mapping

如上所示,我们将高度分层,然后将视线与高度线的交点标记起来,不断地逼近点a(ua),直到找到第一个高度比a点要低的交点,然后过这点作垂线,得到的坐标up既是我们要采样的纹理像素坐标。我们可以通过增加高度线的数量来达到更加精确的效果,虽然通过不断的增加高度线的数量,可以使得结果越来越精确,但是这无疑需要消耗更多的性能。

于是我们考虑改进陡峭视差贴图:不是找到第一个高度比a点要低的点,而是在两个接近的深度层之间进行插值。这种方法就好比数学中的零点,从高度比a点要高的点,到高度比a点低的点,我们必然能取到最接近a点的点(当然也有可能直接找到a点,只要精确度够)。

 

以上。

参考资料:

[1]视差贴图(Parallax Mapping)

P.S. viewDir.xy是将视线投影到xy(uv)平面;而除以viewDir.z是考虑到当视线角度改变的行为,(viewDir进行过标准化)viewDir.z的值在[0.0, 1.0]之间,当视线无限接近水平的时候,P的长度应该更大(假设同一高度的话,当然,高度不统一(更低)的话就更需要)。因此我们用viewDir.xy / viewDir.z,当然这是非必须的。

最后ParallaxMapping函数的返回值用 TexCoords - P,是因为我们需要将新的纹理坐标往左挪,让它去对应更左边的顶点坐标。

Parallax Mapping

 

Parallax Mapping

上一篇:Python时间序列--股票预测(七)


下一篇:封装axios,带请求头和响应头