[中英]Screen Space Planar Reflections in Ghost Recon Wildlands
CGGraph渲染图形学图形图像技术 2210 4
实名

通过了实名认证的内容创造者

发布于 2021-10-2 10:48:35

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 雨の日曜日 于 2021-10-2 11:15 编辑

Reflections done in screen-space
The screen space reflections algorithm (SSR) is a technology now used in numerous video games to provide the reflection visuals. They are expected to be less expensive than actual reflection rendering and more accurate than cubemap-based reflections, as long as the reflection source is present on the screen. Still, this technology isn’t that affordable in a realtime application and it suffers from a lot of graphic caveats: SSR isn’t the final solution to the reflection inputs. However, on specific – controlled – situations, these flaws can be alleviated and SSR can deliver astonishing results.

在屏幕空间进行的反射
屏幕空间反射算法(SSR)是一种技术,目前用于许多视频游戏中,以提供反射视觉效果。只要反射源出现在屏幕上,它们预计比实际反射渲染成本更低,比基于立方体贴图的反射更精确。尽管如此,这种技术在实时应用程序中并不是那么便宜,而且它还存在许多图形警告:SSR并不是反射输入的最终解决方案。然而,在特定的受控情况下,这些缺陷可以得到缓解,SSR可以产生惊人的结果。
1.jpg

2.jpg

Motivation
Here we’re going to talk about another form of SSR that has been developed to support the water rendering ambitions of Ghost Recon Wildlands. It aims to  provide multiple high-quality reflection surfaces from close to far distances with little performance impact, for the specific case of planar reflectors.

动机
在这里,我们将讨论另一种形式的SSR,它是为支持幽灵侦察荒地的水渲染野心而开发的。针对平面反射器的具体情况,其目的是在几乎不影响性能的情况下,从近距离提供多个高质量反射面。

Projection
Hash resolve
Filling the gaps
Optimizations
Multiple water planes

预测
散列解析
填补空白
优化
多水平面

Projection
The concept on which the Screen Space Planar Reflections (SSPR) is based on is the Projection Hash Buffer. This screen-space texture will hold information about the location where the pixels from the main view should be projected in the reflection view.

预测
屏幕空间平面反射(SSPR)所基于的概念是投影哈希缓冲区。此屏幕空间纹理将保存有关主视图中的像素应投影到反射视图中的位置的信息。

To this end, for every pixel in the main depth buffer:
          The pixel is reprojected in world space
          This world position is reflected with a known water plane and projected in screen space
          We write the screen location of the source pixel at the reflected screen position in the projection hash buffer via an UAV write.

为此,对于主深度缓冲区中的每个像素:

  像素在世界空间中重新投影

  此世界位置通过已知的水平面反映,并投影到屏幕空间

  我们通过UAV写入将源像素在反射屏幕位置的屏幕位置写入投影哈希缓冲区。

In practice, we encode the projected pixel location on a single R32_UINT following the simple hash formula

在实践中,我们按照简单的散列公式对单个R32单元上的投影像素位置进行编码

uint ProjectionHash = PixelY << 16 | PixelX

float WaterHeight = …

float4 PS_ProjectHash(float2 ScreenUV) : SV_Target0
{
    float3 PosWS = Unproject(ScreenUV, MainDepthBuffer);
    float3 ReflPosWS = float3(PosWS.xy, 2 * WaterHeight – PosWS.z);
    float2 ReflPosUV = Project(ReflPosWS);

    uint2 SrcPosPixel = ScreenUV * FrameSize;
    uint2 ReflPosPixel = ReflPosUV * FrameSize;

    ProjectionHashUAV[ReflPosPixel] = SrcPosPixel.y << 16 | SrcPosPixel.x;

    return 0; // Dummy output
}

Here, “ReflPosPixel” is the pixel-space location of the projected pixels in the reflection. We use it to store in the projection hash buffer where the source pixel “SrcPosPixel” lies in the main view. Once it’s encoded in the hash texture it’s ready to be used for the second pass.

这里,“ReflPosPixel”是反射中投影像素的像素空间位置。我们使用它存储在投影哈希缓冲区中,源像素“SrcPosPixel”位于主视图中。一旦它在散列纹理中编码,就可以用于第二遍了。

Hash resolve
The idea is quite simple: a fullscreen quad is going to:
          Fetch the projection hash texture
          Decode the hashes to retrieve the location of the source color pixels
          Fetch the source pixels and output them in the final reflection target.

散列解析
          想法很简单:一个全屏四元屏幕将:
          获取投影哈希纹理
          解码散列以检索源颜色像素的位置
          获取源像素并将其输出到最终反射目标中。

float4 PS_ResolveHash(float2 ScreenUV) : SV_Target0
{
    uint Hash = ProjectionHashTex[ScreenUV * FrameSize].x;
    uint x = Hash & 0xFFFF; uint y = Hash >> 16;

    if(Hash != 0)
    {
        float4 SrcColor = MainColorTex[uint2(x, y)];
        return SrcColor;
    }
    else
        return 0;
}

That was pretty straightforward, but at this point the results can be disappointing:

这非常简单,但在这一点上,结果可能令人失望:

Concurrent reflective candidates resulting in blinking pixels(导致闪烁像素的并发反射候选)
There are two flagrant issues here:
           The projection of the source positions into the reflection is an injective transformation, which means that two different pixels from the main view can be merged in the reflection and become “reflective concurrents” without knowing which one of them should prevail. Hence the blinking pixels.
           There are gaps in the reflection caused by the occlusion in the main view that prevents valid pixels in the reflection to be projected.

这里有两个明显的问题:
&#8226;源位置投影到反射中是里射变换,这意味着来自主视图的两个不同像素可以合并到反射中,并成为“反射同线”,而不知道其中哪一个应该占优势。因此出现了闪烁的像素。
&#8226;主视图中的遮挡导致反射中存在间隙,从而阻止反射中的有效像素被投影。

That’s where the Projection hash texture and the hashing function we chose eventually make sense. By using the intrinsic InterlockedMax when writing on the UAV, two hashes are going to be sorted first by their high bytes and so by their PixelY value.

这就是我们最终选择的投影散列纹理和散列函数的意义所在。通过在UAV上写入时使用内部InterlockedMax,两个哈希将首先按其高字节排序,然后按其像素值排序。

// Read-write max when accessing the projection hash UAV
uint projectionHash = SrcPosPixel.y << 16 | SrcPosPixel.x;
InterlockedMax(ProjectionHashUAV[ReflPosPixel], projectionHash, dontCare);

The concurrent projection is now sorted “from-bottom-to-top” and the source pixel locations stored in the projection hash are now the ones closest to the water plane thus the closest to the camera in the reflection view. The projection is now stable.

现在,并发投影被“从下到上”排序,并且存储在投影散列中的源像素位置现在是最接近水平面的位置,因此在反射视图中是最接近相机的位置。预测现在是稳定的。

Filling the gaps
The missing geometry is the #1 issue with the SSR approach and we need to find a way to fill it or else the reflection effect would be disastrously broken.
First we’ll deal with the missing reflection on the screen borders. This is due to geometry absent from the main view but needed in the reflection. There’s no actual solution besides rendering a bigger out-of-screen main frame so we’d be able to fetch it to fill the borders. But in a real world game where every microsecond counts, this is not a option.
Instead we’ll add some stretch on the projected location based on the distance between the source pixel and the water plane.

填补空白
缺少的几何体是SSR方法的#1问题,我们需要找到一种方法来填充它,否则反射效果将被破坏。
首先,我们将处理屏幕边框上缺少的反射。这是由于主视图中缺少几何体,但反射中需要几何体。除了渲染一个更大的屏幕外主框架,我们没有实际的解决方案,所以我们可以获取它来填充边框。但在一个每微秒都很重要的真实游戏中,这不是一个选项。
相反,我们将根据源像素和水平面之间的距离在投影位置上添加一些拉伸。

float HeightStretch = (PosWS.z – WaterHeight);
float AngleStretch = saturate(- CameraDirection.z);
float ScreenStretch = saturate(abs(ReflPosUV.x * 2 - 1) – Threshold);

ReflPosUV.x *= 1 + HeightStretch * AngleStretch * ScreenStretch * Intensity;
  Reflection stretching to fill the missing pixels on the borders(反射拉伸以填充边界上缺失的像素)
Then, it is time to deal with the holes in the projection, which were created by the geometry occluded by closer pixels, and which couldn’t have been projected.
          A classic temporal reprojection helps a lot and very little movement actually suffices to almost completely fill the cracks.
         As a fallback for pixels that still couldn’t be filled with relevant information but which could still be valid (i.e not in the sky), we’ll just make the reflection surface we generated the frame before “bleed” on the current one. It surely isn’t correct but it gracefully avoids any discontinuity and fills the remnant gaps with a coherent color/luminosity.

          然后,是时候处理投影中的孔了,这些孔是由被更接近的像素遮挡的几何体创建的,并且不可能被投影。
          一个经典的时间重投影有很大的帮助,实际上很少的移动就足以几乎完全填满裂缝。
          对于仍然无法填充相关信息但仍然有效(即不在天空中)的像素,作为回退,我们将在当前帧上“出血”之前生成反射面。这肯定是不正确的,但它优雅地避免了任何不连续性,并用连贯的颜色/亮度填充了剩余的间隙。


  The reprojection and bleeding fill the gaps with coherent values(重新投影和出血用一致的值填补了空白)

Optimizations
We have a nice real-time solution at this point but let’s try to get some extra bits of performance. We are going to use an empty additional stencil and see how it can drastically change the cost of the SSPR.
          We discard the pixels whose height is below the water plane as they have no chance to participate in the reflection. The successful pixels are marked in the additional stencil.
          The hash resolve pass uses this mask to only resolve the reflection on the pixels which have been previously discarded (thus whose stencil value has not been marked), as they are the only ones able to be reflective.
With these optimizations, the SSPR cost is linearly dependant on the percentage of the screen where pixels aren’t the sky and are located below the water plane.
          On Ghost Recon Wildlands, we were then able to generate any water reflection surface for a cost of 0.3~0.4 ms on consoles at 1/4 resolution.

优化

在这一点上,我们有一个很好的实时解决方案,但让我们尝试获得一些额外的性能。我们将使用一个空的额外模具,看看它如何能极大地改变SSPR的成本。
  我们丢弃高度低于水平面的像素,因为它们没有机会参与反射。成功的像素将标记在附加模具中。
  散列解析过程使用此掩码仅解析先前丢弃的像素上的反射(因此其模具值未标记),因为它们是唯一能够反射的像素。
通过这些优化,SSPR成本与屏幕百分比成线性关系,其中像素不是天空,而是位于水平面下方。
在幽灵侦察荒地上,我们能够以1/4分辨率在控制台上以0.3~0.4毫秒的成本生成任何水反射表面。

Multiple water planes
As we achieved our goal of an affordable reflection technology, we’re able to compute it several times in a single frame, allowing us to handle multiple water surfaces.
To achieve this, we’ll have to know which planes need the SSPR rendered.
           When rendering the water, each water pixel increments a counter with its plane ID
           These counters are then processed to know which planes are actually visible, and we keep the N planes which are the most present on the screen.
           We generate N reflection layers in a texture array using SSPR.
           In the next frame, the water will use its ID to fetch the array and retrieve the reflection.

多水平面
  当我们实现了一种价格合理的反射技术的目标时,我们能够在一个帧中多次计算它,使我们能够处理多个水面。
  为了实现这一点,我们必须知道哪些飞机需要渲染SSPR。
          渲染水时,每个水像素使用其平面ID增加一个计数器
          然后对这些计数器进行处理,以了解哪些平面实际可见,并保留屏幕上显示最多的N个平面。
          我们使用SSPR在纹理阵列中生成N个反射层。
          在下一帧中,水将使用其ID获取阵列并检索反射。

Multiplanar reflection on the pool and the lake surfaces(水池和湖面上的多平面反射)

A complete example
一个完整的例子

Lit color buffer




SSPR stencil + Projection Hash
Resolved hash
Water rendering


评分

参与人数 1元素币 +99 展开 理由
元素界王神... + 99 中英技术文奖励!

查看全部评分

自然选择,前进四!
使用道具 <
水木神川  发表于 2021-10-3 00:57:37  
2#
谢谢楼主分享
回复 收起回复
使用道具
好人好事  发表于 2021-10-3 07:35:59  
3#
回复 收起回复
使用道具
deocat  发表于 2021-10-8 19:35:57  
4#
感谢分享
回复 收起回复
使用道具
凌一一  发表于 2021-10-9 11:21:27  
5#
很丰富的教程
回复 收起回复
使用道具
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表