解决半透明实时渲染挑战,Oculus用掩模渲染作替代方案
查看引用和消息源请点击:映维网
半透明渲染与掩模渲染
(映维网 2019年05月17日)渲染半透明材质是实时应用面临的一大难题,尤其是因为现有的大多方法对于虚拟现实而言都成本过高。用于Rift和Rift S的Oculus Avatars采用半透明材质,而传统的半透明方法存在渲染方面的挑战。Oculus的软件工程师瑞奇·拉贾尼(Ricky Rajani)日前分享了针对所述问题的解决方案,并展示了如何利用掩模渲染作为替代方案。下面是映维网的具体整理:
对于材质和几何,Home 2.0是采用集群正向渲染而非延迟渲染,支持MSAA(多重采样抗锯齿),以及用更低带宽成本实现更高分辨率。原来的Avatars渲染成透明,使用双通道渲染技术。当Home中的Avatars没有照明影响时,这可以实现优秀质量的解决方案。为了获得更高的视觉质量,Home现有有了照明影响,所以在采用半透明渲染时会出现性能和质量方面的挑战。Oculus于是切换成渲染掩模Avatars(Home),允许你以二进制方式控制对象的可见性。这种方法适用于MSAA,但如果没有MSAA,掩模渲染中的伪影会相当明显。
从左到右:4倍MSAA;掩模渲染无MSAA;半透明无MSAA;透明4倍MSAA
1. 透明 VS 半透明
在本文中,“半透明”指定透明对象的渲染方法与Unreal引擎术语一致。Unreal引擎将“半透明”用于描述透明对象的材质。例如,当为透明对象的材质选择“混合模式”和“照明模式”时,我们必须分别选择“半透明”和“表面半透明体积”选项。
由于两个术语之间可能存在混淆,所以有必要定义“半透明”,并详细阐述为什么我们决定选择这个而不是另一个。透明材质和半透明材质之间的主要区别在于,其影响通过光量的物理属性。透明材质允许光线完全通过,同时透明对象另一侧的对象清晰可见,没有变形。半透明材质允许光线进入,然后根据材质的物理属性进行散射。它允许一定的光线通过,同时扭曲和漫射光路,以及位于它们后面的对象。
2. 半透明渲染
当为Avatars(Home)实现半透明渲染时,Oculus优先考虑性能和最小化视觉伪影。下面介绍了与深度,照明和MSAA相关的挑战。
2.1 深度问题
采用半透明渲染的Avatars(Home)是通过Alpha Blending实现。上图是关于这一实现的深度问题示例:左侧没有以正确的深度排序;右侧则是正确的深度排序。为了实现正确的结果,对象必须从后到前进行混合,不可交换。因此,必须对三角形进行分类,而这对特定的边缘情况来说成本又高又不足够。表面混合的顺序会影响每个表面的总体可见度。我们可以使用与顺序无关的透明度,允许按像素几何排序,但这是一种成本相当高的技术,不适合VR。
Oculus通过Unreal引擎的Custom Depth来解决排序问题。这项功能可以提供类似于Z-Buffer中的遮挡,同时能够实现正确的半透明排序顺序。Custom Depth主要利用了第二个深度缓冲区,而后者可用于自定义剔除等半透明效果。对于Avatars,Oculus利用Custom Depth进行每像素深度遮挡。
在将网格渲染到这个深度缓冲区时,可以剔除网格外壳后面的像素。运行两个通道,如下:
- 使用DepthTest渲染Custom Depth以获得最接近深度。
- 在半透明通道中读取Custom Depth。渲染根据像素深度和Custom Depth之间的深度比较结果进行掩模的半透明度。
通过将半透明材质中的复选框设置为“Write to Custom Depth”,并为静态网格和骨架网格启用“Render Custom Depth”复选框,你即可在Unreal引擎中设置这种双通道方法。然后,像素着色器向Custom Depth缓冲区读取,而所有这一切都不会增加像素着色器的成本。
上图左侧描绘了渲染至Custom Depth缓冲区的渲染效果,右侧则是完成半透明通道后的结果场景。Avatar是左侧唯一的对象,因为它是场景渲染到Custom Depth中唯一的网格。Avatar(Home)采用了与Custom Depth缓冲区和结果纹理相同的分辨率和MSAA采样。
除了在场景深度和Custom Depth之间进行比较之外,为了防止过多的像素被剔除,我们同时应该添加一定的深度偏差,包括外壳材质。下图是有否添加深度偏差的效果。
2.2 照明
当前的Avatars(Home)允许光线影响其外观,所以解决阴影接收问题非常重要,亦即环境遮蔽材质。出于质量原因,Oculus希望Avatars(Home)具备这一属性。然而,Unreal引擎中的正向渲染不支持半透明材质的阴影接收。由于性能原因,Unreal引擎将近似计算半透明材质的阴影,而这不适用于Oculus的延迟阴影函数。
- 对于Unreal引擎的半透明照明体积功能,其不支持透明材质从静止光源接收动态阴影。另外,这项功能会因额外的通道而增加GPU性能成本。
- 另一种接收阴影的方法是使用体积光照贴图。当与固定光源一起使用时,它们为动态对象提供了更高质量的烘培阴影。Oculus设法利用这项功能来为每个对象添加阴影。因此,烘焙信息会随着时间的推移而逐渐消失,但它只适用于静态几何并需要固定光源。
为了给半透明材质添加动态阴影,我们可以采用下面其中一种方法(但会增加额外的性能成本):
- 将单个光线投射到光源,从而估计单个阴影内值。对于柔和阴影,可以使用多次光线投射来进一步改善。
- 添加更多CPU光线投射以创建详细的阴影纹理掩模。这仍然只会产生低分辨率阴影。
- 启用半透明照明体积以配合正向渲染,从而提供更高质量的阴影。
2.3 MSAA伪影
由于Oculus Home使用集群正向渲染,我们可以利用MSAA来改善边缘的视觉质量。但在使用半透明渲染时,部分对象边缘出现了问题。这会导致附近和背景内容的伪影出现溢出至场景之中。由于深度误差,半透明材料陡峭角度周围的不透明场景通常在边缘周围溢出。
Oculus的解决方案可以减少“溢出”伪影,包括添加多个深度偏差,确保最小化深度误差,尤其是MSAA。这是当确定像素的不透明度时在深度拒识期间完成,没有添加深度偏差,许多片段被剔除。
下图是深度拒识期间添加的不同深度偏差水平。第一张图描绘了较小恒定偏差的深度比较,没有考虑边缘周围的变化陡度水平。第二张图描绘了与较大恒定深度偏差的深度比较。第三张图则描绘了与恒定深度偏差和梯度深度偏差的深度比较。
下面概述了场景深度和Custom Depth的比较过程。
- 利用场景深度(图像中的像素深度),我们通过计算ddx和ddy并选择两者中的最大值来计算最接近的深度。
- 将数值乘以预定的“DepthBias_Gradient”值,这考虑了边缘周围角度的变化陡度。
- 钳制乘法值并与预定的“DepthBias_Constant”值进行比较。两者的最大值将添加到“场景深度”中。
- 将新的场景深度与“Custom Depth”缓冲区中的最小值进行比较。如果没有MSAA,则最小值是存储在缓冲区中的值。但如果有MSAA,我们比较所有MSAA采样的Custom Depth并取其最小值。
- 如果新的场景深度更大,则不透明度设置为0。
遗憾的是,这种方法并没有修复因每个对象排序所引起的“溢出”伪影,有一些糟糕的情形会令所述伪影更加明显。一个例子是当半透明Avatar位于高动态范围(HDR)光源的前面时。HDR光源具有高饱和度值,它在运行半透明通道之前未被钳制,所以会出现“溢出”伪影。因此,在重新映射颜色之前,高颜色值会“溢出”到半透明材质,导致伪影变得更加明显。
修复“溢出”伪影的一种方法是,增加上述的两种深度偏差。但是,这可能会导致其他与深度相关的问题。深度偏差与其行为中的阴影偏差相似。阴影偏差可以修复阴影“粉刺”(类似于“溢出”伪影),但阴影偏差太大同样会令阴影断连。类似地,太大的深度偏差会导致半透明材质出现交叉点相关伪影。
下图描述了交叉点相关伪影,其深度偏差增加,从而导致更多伪影。
注:左上图是掩模渲染,没有任何交叉点相关伪影。右上图是半透明渲染,恒定深度偏差为-0.2。左下图是半透明渲染,恒定深度偏差为-0.5;右下图是半透明渲染,恒定深度偏差为-1.0。随着恒定深度偏差的增加,交叉点相关的深度问题更加突出。
为了获得最佳结果,有必要在最小化“溢出”伪影与交叉相关伪影之间寻找一个合理的平衡,而我们发现添加深度偏差是一种优秀的方法。
3. 掩模渲染
Home目前为Avatars使用掩模渲染。掩模材质的视觉质量取决于MSAA水平和抖动模式的结合。Oculus采用蓝色早点抖动模式来避免有序的抖动外观。
尽管Home为材质和几何采用集群正向渲染,但延迟阴影仍用于不透明渲染和掩模渲染。延迟阴影是通过运行Early Z通道来计算深度,而深度稍后将用于创建Base通道中所使用的阴影掩模。换句话说,延迟阴影在屏幕空间中计算,并由Base通道中的所有非半透明对象读取。
下图描绘了Eatly Z通道中渲染的内容,其中包含Avarars渲染,以及稍后在Base通道中渲染的内容。
最终,阴影接收可以保证掩模渲染,因为掩模材质的不透明部分尊重照明。对象的排序同样正确,所以我们不需要使用第二个深度缓冲区。
掩模渲染不会出现与半透明渲染相同的MSAA伪影,它采用alpha-to-coverage,亦即“将像素着色器输出的alpha值映射到MSAA的覆盖掩模。”通过在掩模渲染中结合MSAA和alpha-to-coverage,会有大量的子采样需要平均,而这会带来更高的视觉质量。在掩模渲染的深度比较期间同样不需要深度偏差。下图比较了掩模渲染和半透明渲染。左边是掩模渲染,右边是半透明渲染。你可以看到,右图出现了来自MSAA的“溢出”伪影。
掩模渲染解决了半透明材质带来的众多问题,包括在Unreal引擎中允许确定性遮挡和延迟阴影的能力。
4. 性能
下面的性能数字来自于Avatar看着Avatar Editor Mirror的情景。场景是手动设置,这增加了一定的人为错误。例如,在设置期间不计算Avatar与镜子的距离。因此,每个场景可能存在或多或少的半透明/掩模像素。这是使用Medium Graphics设置进行分析,这对应于Unreal引擎的High Scalability设置,同时使用了4倍MSAA。
两种渲染通道的CPU性能相似,因为半透明渲染每帧需要大约750次绘制调用,而掩模渲染每帧需要720次绘制调用。通过测量渲染掩模Avatar和半透明Avatar的相关通道,我们可以评估GPU性能。数据表明,与掩模渲染相比,半透明渲染每帧的GPU时间增加了近80%。
掩模渲染运行Early Z通道,然后在Base通道中渲染。半透明渲染会跳过Early Z通道,而是在Base通道之后运行Custom Depth通道。然后它在半透明通道中渲染。半透明渲染需要Resolve Scene Depth通道,从而在提交至OVR时将Custom Depth解析为最终Z。
5. 总结
上表是半透明渲染和掩模渲染之间的关键差异。由于MSAA的视觉效果,增加的性能成本,以及半透明渲染缺少阴影接收,Home on Rift仅对Oculus Avatars采用掩模渲染。