常见反射方案总结
a). SSR
优点: 实现简单,效果较好;
缺点:
- 只能反射屏幕内的物体(屏幕空间算法的通病);
- 解决方式:SSR作为主方案,Reflection Probe作为备选项,在SSR失效的地方(如屏幕边缘)对两者做平滑过渡;
- 需要Ray Marching、频繁读取纹理,可能造成IO瓶颈;
- 被反射物只有漫反射的时候才是完全物理正确的;
- Ray Marching次数限制,容易出现物体断裂;
- 解决方法: 一般会引入Thickness来防止物体出现断裂,即ray marching的点除了深度需要大于深度图的深度值,还需要步进的步数小于Thickness;
优化:
- Hierarchical Z;
- Mip生成高效算法可参考AMD 的 Single Pass Downsampler、Unity中贴图Mip生成(Unity参考的是Nv的DirectX-Graphics-Samples/MiniEngine/Core/Shaders/GenerateMipsCS.hlsli at master · microsoft/DirectX-Graphics-Samples (github.com))。
- Dither;
- 下采样;
b). Stochastic SSR
针对情况: Gloosy物体(对于普通SSR来说,只可通过roughness对反射图进行blur)
整体思路为
将屏幕划分为Tile,进行一次低分辨率的光线步进,评估Tile的重要性,需要多少射线
根据材质粗糙度判断使用何种的RayMarching
- 昂贵的射线(用于smooth表面):借助Hi-Z的精确tracing,能得到准确的命中点
- 便宜的射线(用于粗糙表面):简单的线性步进,反正之后会粗滤波
使用重要性采样求RayMarching结果
时域空域光线复用(本质是计算 brdf/p(x) 的加权和),进行插值滤波
Tile评估
将屏幕划分为多个Tile,对于每一个Tile以1/8分辨率发射射线
判断射线(的反射光线)是否击中
- 若所有光线都没命中,则跳过这个Tile的步进
- 根据命中的比例和命中信息的差异,判断这个Tile中的像素需要多少个光线
- 根据着色点的粗糙度,决定使用何种RayMarching
c). 平面反射
缺点: 可能增加一倍的Draw Call;
优化手段:
- 剔除影响不明显的小物件;
- 如反射物为镜子之类的,可以结合Portal或者手动计算去剔除镜子外的物件;
d). IBL+Relection Probe
略过
e). Pixel Projected Reflection(PPR、SSPR)
PPR 将整个图像,按照平面的位置进行翻转,在屏幕中进行投影,即可直接得到反射的位置,写入数据并得到一张反射的贴图。
因为场景中的像素,并不能完全保证覆盖整个反射平面,因此往往会产生一些缝隙。UE4中采用一种简单的修复缝隙的方式,是在写入偏移时,将目标像素周围2x2像素直接全部写入偏移值。
- 根据深度图,还原场景深度,算出对应点世界坐标,将其投影到平面上,然后将其在屏幕中的偏移量,将偏移量进行编码,写入到 Intermediate Buffer当中;
- 从Intermediate Buffer中还原出屏幕空间的偏移,采样屏幕中相应坐标的像素点,将结果写入到反射图中;
- 使用反射图,绘制出反射面的效果。
【参考资料】
游戏中的全局光照(五) Reflection Probe、SSR和PPR - 张亚坤的文章 - 知乎