前言
之前之所以要重写一遍URP的lit,其实主要目的就是为了这里能实现真正的带深度偏移的陡峭视差映射。
URP自带的视差映射只是最最基本的用射线方向和高度去做偏移,只能在高度不高时有一点效果,要想更高一点的高度,只能像HDRP一样,分层用类似光线步进的方式去采样。
高度图
高度图可以有多种使用方式,最简单的就是用来变化顶点,但需要模型面数很高。
或者曲面细分后位移顶点,对GPU就有要求了。
还可以在像素级别实现。
通过偏移UV,来模拟高度。URP自身的高度图就是这样的,但URP没有支持像素深度位移。
即仅仅偏移了UV,但像素的深度没有改变,会导致看起来物体悬空。HDRP则实现了这一功能。
先来看看URP默认的高度图是怎么做的吧。
主要是这个函数
这里传入一个切线空间中的视线方向,以及UV,然后偏移这个UV,后续都采用偏移的UV来采样贴图。
这里的采样高度是最简单的一种方式,单纯用原点向视线方向偏移的方式,采样一次即可。
具体效果如下
在缩放比例较小时效果还可以,稍微增大一些就不如下面提到的方法了。
陡峭视差映射
主要参考自LearnOpenGL
具体的原理也不复杂,参看原文吧,下面给出Unity中的实现
这就最简单的仅分层的视差映射了,当层数足够时,效果还可以,但如果层数太低,就会有分层的感觉,也算是光线步进的通病了。
视差遮蔽映射
再然后就是对上述方式的改进,不是单纯用一个点来确定高度,而是用一个范围然后线性插值来得到,
改动如下
能看出比之前要好上不少,尤其是相同层数的情况下,分层感少了很多,但还是有。
再加上抖动
这个做法来自这里,采用屏幕空间的随机噪声,来抖动采样层数。
可能在有TAA的情况下效果会不错,但目前URP还没有(2023会有),这个就等下次手撸TAA的时候再来看吧。
HDRP中的做法
然后去翻了翻HDRP中对视差的做法,大体一致,但最后选择采样点时,不是单纯的用两个点来插值,
而是又加上了割线法,迭代三次,取一个更近似的解。
像素深度偏移
先看看没有深度偏移之前的样子
其他物体是浮在之前的平面上的,凹陷下去的部分也不会透显出其他物体。
这是因为写入的深度没有改变,还是之前平面的深度,实际应该根据视角和高度(深度),来像素级别的改变深度图。
所以这里需要SV_Depth
这个关键字。
改动下原来的Shader片元的函数声明
这里的逻辑很清晰,就是把片元的世界坐标沿着视线的反方向偏移,然后再手动计算一次深度,最后吧把深度写入。
这时深度信息就正确了。
阴影
同样的,在绘制阴影贴图时,也要添加对应的深度偏移
改动一下阴影pass的片元着色器
这里要注意的是,对于平行光来说,视线方向应该就是光线方向。
阴影也考虑高度的话其实挺吃性能的,一般这种用作地面的平面,不投射阴影仅接收阴影也可以,或者阴影少几层影响也不大。
VR
得益于URP的单通道渲染,几乎不需要什么改动就可以完美适配VR,VR下视差贴图的效果确实惊人,因为视差考虑到了双眼位置的不同,双眼的画面也就不同,这就比单纯的法线要生动真实的多。看来虚幻的VR开发指南中推荐用视差还是有道理的。
后续
如果说还有什么可以改进的,那就考虑软阴影,抗锯齿之类,以后有需要再说吧。