前言
之前的中篇中,我们粗读并分析了作者集中讲述并较为关键的三个部分:(数学与制成)
建模、密度采样、光照计算。
相对于之前旧方案中的2.5D体积云,作者集中介绍了为使飞入云层成为可能而改进的体素云系统的取舍与优化。其中存储空间和指令调用(或算法复杂度)的平衡是作者最核心的一项实践智慧。
如果只是纯理论研究,那可能了解到光照这一步也足够了。这次的下篇剩余的部分可以看作是工业化方面的一些收尾,以及整合与总结。
文章还是以翻译原文的讲稿为主。这次分为了上中下三篇——这是其中的下篇;打星号的部分则是我个人的补充说明。整篇分享中视频非常多,很多动态效果是完全无法用文字描述的;一方面我也转录了很多GIF段,另外原始精度的视频可以从文末链接去源地址下载。
1 光线步进追踪——Ray-Marching
径直向前,我们也需要关注光线步进(ray-march)方面的性能节省,以支撑飞跃云层的性能需要。
*Marching right along翻译过来就是径直向前,作者这里又双押了march这个双关语。
回顾一下我们的困境(dilemma)——由于没有很好的方法能获知云层(体素范围)中每个采样点的相关位置,我们无法高效地沿射线进行采样。
之前的封包法(Envelope method)云层方案中,我们整合了距离步进映射(distance step mapping)和锥体步进映射(cone step mapping)两种方式,来改进我们从2D数据生成的体积云中进行采样位置选取的过程。(*上篇中介绍了)
现在我们面临的是真正3维的问题了(基于体素)——幸运的是,有一项我们准备许久的技术此时可以派上用场了。
Sphere tracing is a technique that uses the 3-dimensional distance to the closest implicit surface to determine step size. A signed distance field stores measurements of these distances for every point in space. Positive values are outside of the object and negative values are inside.
球体追踪(Sphere tracing)是一项使用三维距离来确定最近的特定表面的技术(以确定步进长度);而一个有向距离场(Signed Distance Fields,缩写为SDF)存储了空间中每一点的最小追踪距离——正值表示在物体(云)外,而负值表示在物体内。
当沿着体积云层进行ray-marching时,就可以使用(射线上某一点)在距离场中的距离值来作为最大的步长,以减少射线上(无效)采样的数量。
有两篇很好的介绍SDF使用的资料:
- 第一篇是Inigo Quilez的网页,你可以了解如何为一些原始(primitive)形体计算距离场。
- 第二篇是GDC上Sebastien Antolnen的讲座,从中你可以了解如何在高性能要求的场合高效使用这项技术。
*不仅限于云层这个案例,在能使用SDF的场合,往往都会用来作为ray-march的重要补充。
*这个案例中,通过任意一点坐标查找体积云SDF,得出的值就是“从这一点为圆心刚好与云体相交的最小半径”;虽然查找无关方向,但是这个预计算的值是准确的,可以作为下一次递进的步长。
当艺术家完成构建他们的“人造云层形体”时(之前一篇介绍了,是用了生造词frankencloudscapes):
我们生成了一个3D的SDF,并把它写入了一个单独的NVDF(*NVDF是之前介绍的一种体素云数据结构的缩写)。数据的原始范围是从-256到4096(米),但我们将其缩放至一个0到1的范围内。在图中(中间一张)你可以看到在远离云的位置,值逐渐增加(越来越白)。
我们将其与云模型数据的NVDF分离开,因为它们会被频繁(每一步进过程)进行查找,因此我们不希望无缘由地引入内存瓶颈。提到内存瓶颈,距离场数据的压缩方式也很重要。
这里展示了Nathan Reed整理的(*原文还用了world-famous一词)BC格式表,以解释这一问题:
由于SDF会被频繁采样,因此对它压缩能降低每个纹素需要的字节数,以减少内存带宽占用——最终提升性能。
一个1通道的BC4似乎就很理想。而其中的tradeoff是,更少的字节数意味着较低的精度,而这对于一个基于SDF的yaymarch来说是一个大问题。
如果解压缩值过低,则会导致需要消耗额外的raymarch步数;如果过高,则会导致渲染故障。(*这里对应起来不太直观,但原文确实是decompressed value)
如何解决这一问题?我们采用了被称为“渲染工程师的黑魔法”的方案。(*原文就是Rendering engineer black magic,之前一篇也说了,作者喜欢使用一些技术分享中不常用的词汇)
通过使用高级技术工程师Hugh Malan开发的一套定制化BC1压缩器,SDF能在被压缩后依然能得到接近16bit的精度。(*1byte=8bits)
There is a bonus slide right after this one with an in-depth explanation of Hugh’s work that you can look at, but to sum it up, we split up the data into three channels and then reassemble them after we sample the SDF in the ray-march shader. This approach worked because SDF data is smooth.
这里(*原文的bonus页)中提供了对这套方案的详细解释,不过总的来说,我们是将数据分别存入三个通道中,之后在ray-march shader采样SDF的过程中重新集成——这个方式之所以能生效,是因为SDF数据是平滑的。
最终对于不同的射线长度,这(相对于无压缩的纹理数据)大约减少了10-30%的内存瓶颈——应对于我们最频繁采样的体素数据。
由于这是我们的体素方法中的最后一部分NVDF数据,至此我们可以整体对照一下单个系统的内存开销:
- 垂直轮廓法VP, 大约0.5Mb
- 封包法EVM,大约9Mb
- 体素方法VM,25Mb
因此你可以看到额外的细节是从哪里来的——没有什么是能凭空出现的(Nothing comes for free),但有了体素云我们就能实现很多其它方法无法达到的效果。
不过这也不是我们唯一的加速体素ray-march的方法。
首先,我们定义了射线的边界,以限制潜在的采样数量。
射线从摄像机发出,以体素栅格边界作为终点——或是命中范围内的几何体。
下一步,我们需要计算下一步采样位置的步长。这个距离的计算是基于三个来源:
- 基于距离场的有向距离值。
- 在云体积内自适应随距离逐渐递增的步长值。
- 以及一个分时抖动的偏移值(temporally jittered offset)以消除降采样带来的噪声。
其中SDF被用来在云层外的位置以最佳方式放置采样点。(*图中空心圆的部分)
而自适应步长是基于到摄像机的距离来在云体积中放置采样点。这样,距离摄像机越远的云就能使用尽量大的步长,不同距离的云采样都能以相对最佳的步长进行。
而抖动偏移值从近处的动态到远处的静态云层之间进行切换,以降低采样精度不足带来的视觉故障,同时也避免使远处云层闪烁(shimmer)。(*这里应该是说不同距离有不同抖动偏移值)
有向距离值(步长)以自适应步长作为最大值,因而当在云层内采样(有向距离值变为负值)时,我们切换到使用自适应步长值。
步长值之后会被累加到采样距离上,以推算下一步的采样位置。
当要进行采样时,我们也查找SDF来进行一项优化。
如果SDF的值大于0,意味着采样点在云体积外,因此不需要进行采样操作。
如果值小于0,则按之前(*前一篇)描述的收集密度和光照样本,最终延射线方向累加。
之前的部分中已经数次涉及到特定场合的性能表现,而现在是时候系统性地看看性能情况了。重要的是对不同情况下可见物体的数量有一个基本的把握:
- 当飞跃云层时,云是视觉中主要被渲染的物体。
- 当从地面远距离观看时,云的开销应按比例缩小以让位于其它的物体。
- 动态平衡这项开销是衡量我们的云系统是否成功的关键。
因此,让我们看看不同情况下的渲染数据:第一种是从地面观看云层时,需要混合地面和云层;第二种是全屏云层。每个案例都以960x540分辨率渲染。
从地面开始,在没有光照或ray-march优化时,开销是10毫秒。
通过基于体素的光照优化(*之前介绍过,经过解耦预运算),降低到6.1毫秒。
经过ray-march优化,降低到2.2毫秒。
加上其它几何体(geometry)pass的开销,总开销是9.2毫秒。
当我们来到空中,在云层和可见的远景之中。在没有光照或ray-march优化时,开销是12毫秒——这种增量主要来自更多沿着俯瞰的方向穿过云层的射线,这部分开销可以通过对云的高度做偏移量(来减少射线的长度)以进行缓解。
通过基于体素的光照优化,降低到8.2毫秒。
经过ray-march优化,降低到4毫秒。
从图中可以看出,几何体pass的开销降低了2毫秒,而云渲染的开销增加了2毫秒——仍然是比较平衡的。
当来到全是云层的高空,原始无优化的开销是10毫秒。
通过基于体素的光照优化,降低到8毫秒。
经过ray-march优化,降低到4毫秒。
几何体pass的开销降低了1毫秒,而云渲染的开销和之前一致。
因而在不同的情况中,性能预算被从地面几何体很好地传递(handoff)给了体积云。
总结一下:
- 我们使用了压缩的SDF来规避内存瓶颈,并用来优化射线采样的步长。
- 我们(在逐步采样的过程)组合了SDF、自适应步长和抖动采样的方式,以获得在降采样精度情况下最佳的降噪效果。
- 体积云的整体开销在2.2毫秒到4毫秒之间,取决于观看的位置在地面还是空中。
- 性能开销从地面到空中有合适的规模适应(scales properly)。
2 渲染——Rendering
*这里的rendering主要概括了前述方案整合后的高帧数模式的策略。
30hz、60hz、40hz或其它——每个人都有自己倾向选择的游玩帧数(*刷新率,虽然不完全一样)。我们作为图形程序员和艺术家的工作是确保各种渲染模式都是受支持的。在基础性能表现的帧率勉强达到(squeaks)30hz模式的标准时,我们还需要支持高帧率模式——意味着每帧只有一半的渲染时间预算。
*其实谁都想画面又好帧数又好,但实际上不可能兼得。这里作者团队的思路是先按高画质30帧来做。
Recall that for the Envelope Method, we did away with temporal upscaling and split the render into two passes: High res in the distance to prevent aliasing and low res up close to improve performance for the most expensive parts of the ray-march. Would a similar approach work for 40 and 60hz modes? The answer is yes. For High framerate modes, we do this exactly the same way.
回顾在封包法中的超精度策略,我们执行了一项分时超精度(temporal upscaling)算法,并将渲染步骤拆分成两个pass——远距离的高精度pass,以避免走样;近距离的低精度pass,以提升开销最大的ray-march部分的性能。类似的做法是否能用于40和60hz的模式呢?答案是肯定的。对于更高帧率的模式,我们采用了完全相同的策略。
这里展示了一个例子:
云层在30hz模式下的帧开销是4毫秒。当我们将渲染拆分到不同分辨率的两个pass中,开销降低到2.1毫秒——这个方案被用于40和60hz模式中。
让我们将两种模式并排做一个对比。
可以看到在60hz的视频中,在有锐利和致密特性的云层处,画面会显得更“像素化”(pixelation ),但低密度的区域能较好掩盖这一点。除此之外结果已经足够好了,因为这种体验本身几乎没有损失。
*有兴趣的可以去看看1080P版本的原始PPT,压缩后的图片和GIF已经无法反映这种区别了。
小结一下:
- 我们把云的渲染拆分成了两个pass,近处低精度、远处高精度。
- 这几乎使渲染时间减半了——而这能很好符合60hz模式的渲染时间需求。
- 40hz模式也使用同样的方法。
*虽然都知道将分辨率会提高精度,但作者的这种组合策略确实非常符合云的视觉特点及需求——近处相对模糊而远处清晰。
3 制作过程——Production
*到了这一步才算进入很多人喜欢提的一个词,“工业化”制作。第三代体积云系统正式作为一个可以丰富参数化的可交付模块提供使用了。
下面让我们看看这个系统在游戏中的一些实际应用。分别从为背景元素和可探索元素构建云的形体开始。
This is a “chickenscratch” doodle that I made of post apocalyptic Los Angeles. It helped to lay this out along the east-west and North-South views because these are the directions against which we need a good composition of silhouettes in terms of the arc of the sun.
这里通过潦草的手绘草稿(“chickenscratch” doodle)展示了洛杉矶的后启示录概念图。这有助于我们基于“从东到西”和“从北到南”的视图来安排景物的排列,因为在这些方向上我们需要安排一组好的物体剪影组合——对应各方向的太阳弧度。
我们从放置大规模的云层开始,因此它能在一天的各时段、从各角度观察都有不错的效果。
下一步,我们添加其它的特性和相邻云层结构。(*原文是connective tissue)
对于一部分云我们用Atlas工具添加通道和孔洞,以增加探索体验。
在制作时我们也会试飞一下以快速度体验天空云层中的实际情况。
从云层孔洞中穿过自身就足够有趣,以至于有时很难把工作和玩区分开。
*so it was hard to distinguish work from play at times——开发中的内容往往会重复很多次而让人疲倦,尽管如此还能觉得有趣,这是开发游戏中难得的体验。
让我们看看普通速度飞跃通道的视频(*虽然GIF还是加速过)。
注意光照质量在云的内部以及通道的背面的效果——光照运转良好。
能看到翅膀尖端的细小气流么?这是通过检测翅膀与云交叠时发射粒子来实现的。
以防万一你会觉得“云层不会有这样的通道”,这里是一张我拍摄的照片——右边的黑点是一只鸟,而不是一个机器恐龙。
我们对每块云都多次执行这一步骤,之后将它们组成一组云的形体。
这里展示了实际游戏中不同位置观察到的云形体。
对于转场动画我们可以使用一个变换(transform)来移动已有的云层,避免增加额外的内存。
*这里是说运行时把已有的体积云移动入画,就不用额外生成动画用的体积云了。
对于游戏最后的BOSS战,我们构建了环绕的云形体,使场地像一个竞技场。
总结一下:
- 以体素云方式建模比旧的“人造云层”方案更简单。
- 这是工作流程上实在的范式转换(Paradigm shift),因为你可以制作任何形体的云——甚至是在天空写字。
- 转场动画能使用已有的云形体,通过空间变换(移动云)来节省内存。
- 也为例如BOSS战等场合订制了专门的云形体。
4 雷鸟降临——The Stormbird Encounter
被我们称为“体素云方法”的体积云系统从结果来看运作良好——可以看到和之前的方案比较来说,体素云实现了各种突破。
演进(Evolution,可以理解成动态流动)是未来待开发的一项内容。但要实现这一点就不仅需要解决实时体素云的演进,还需要生成实时变化的SDF了——这就留给未来继续开发吧。
我们把这项成果视为完全的成功——因为我们不仅很罕见地保留了初期预想的所有内容,并且创造了更多。
一项有价值的takeaway是:云的密度不仅仅是关于体素的密度,也关于结果的视觉感知复杂度。采用高精度当然是其中一项方案,但在计算机图形领域往往有不止一种问题解法——如我们所介绍的那样。
在结束讲座前,我还有一项内容想分享。游戏中,我们想提供不仅仅是飞跃云层,而是额外创造在一片风暴云中的一段独特的游戏体验。
游戏中的雷鸟有着很多背景设定(lore)。这是一只天空中的顶级捕食者(apex predator),可以控制小范围的天气,以闪电制造风暴云。为什么之前我们一直没在游戏中展示这一点?很简单,因为之前的技术方案不支持,直到现在(*体素云)。
主世界设计师Elijah Houck构想了很多追击玩法(cat and mouse game)的方案。它是一个顶级捕食者,而你的坐骑相对显得很小。Elijah想象它把玩家引入它的巢穴——一个风暴云中,之后突袭并击杀了你的坐骑,好在玩家角色——alloy有滑翔器。
这里展示了这段追击战,在最后击杀它后风暴云也消散了。
*这里原PPT有一段一分半的演示,这里没法放进来了——效果很不错。
因此,留给我的任务是设计云的外轮廓、完成光照方案、设计云的内部通道——以及最重要的是让它看起来不像一个简单的云孔洞。最后,在击杀雷鸟后要让云层消散。
我们希望它呈现为一个很局部的风暴,而不是类似西之禁地中的超大型风暴。
这里展示了一个早期的流体模拟尝试——一缕垂直扩散的云,底部有着纤细材质的边沿。
这里展示了添加了(来自西之禁地中的)辉光代码后的效果——这一早期结果在两个角度上改变了风暴云的开发思路。
首先,我们认识到这相较于我们想构建的遭遇雷鸟的云(需要有足够的内部结构)来说太小了。
其次,注意到底部的辉光效果在视觉上的开口效果。这使我们考虑使用内部光照来突出展示开口。
因此我确定无论地面上是否有放电现象,云的内部需要被照亮来突出某些内部结构。我也试着添加了一些预制好的闪电效果。
在风暴云范围下方,我们也使用了局部天气系统(来制造降雨)。
这里是游戏中整个雷云外部的远距离完整展现。
在云的内部,我希望玩家遭遇一些预期外的内容,不止是建模好的孔洞——而是一些对人类来说很诡异而对雷鸟来说很舒适的事物。玩家遭遇它时,会觉得既可怕又合理。
合理的一种考虑是云层中可能有一个能量中心,使雷鸟能维持这种非自然的风暴。当击杀雷鸟后,能量中心和云都消散了。
图中展示了被我昵称为“the Egg”的这一能量中心。其中小型的电弧显然受到了Nikola Tesla的启发。
我们也持续调整了很多版,直到“egg”的下方能明显看到放电现象——这让它起到了一个视觉突出的作用。
作为实现的概括,这是另一处我们取自超级风暴系统并与体素云结合的光照技术。
对于内部空间,我使用了Atlas工具来切开孔洞和创建egg区域。我确保在此过程增加了足够的小开口(pockets),使雷鸟能隐藏在其中并发动突袭。
我也添加了足够空间的大开口,使光线能进入云内部,使其在一天不同时段呈现不同效果——毕竟这是一个开放世界游戏。
基于所有这些已经实现的细节,Elijah制作了遭遇战的音频,提供了一些很棒的音效。
因为我们升级到了新的体素云渲染方案,并且开始探索云的物理特性以及环境交互,以及所有这些特性都开放给游戏脚本来调度——我们最终能为开发组提供更多,并为玩家呈现独一无二的云层飞跃体验。让人兴奋的是,这仅是这类游玩体验的开端。
因此最后我们把图表添加了两行——现在只有体素云系统能用于任务设计以及有着更多潜在的扩展可能性。
*这个雷鸟的案例就是一个既有压力但又良性向上的开发环境中,从一个小的原型到交付的功能的方方面面,其中有很多开发者的自主创新和高要求——得益于体积云系统的“范式转换”。
结语
作为一款“偏罐头”的一度独占的3A大作的续作的DLC中出现的内容,我对于有多少人能实际玩到这些让人振奋的体积云效果感到不乐观;至于后续移植到PC,这种针对PS5踩在性能及格线上的高质量效果,要适配各种不同组合的PC硬件想来也是让人头大的。对于“开放世界”,可能玩家第一时间会想到“罐头”,而体积云的制作者想到的是——它在time of day需要有不同的合理表现。
无论如何,至少在PS5这个世代的初期,这些索尼第一方游戏的整体质量是让人放心而振奋的——和现在主要第一方游戏的境遇可以说是天上地下。
虽然我没有玩过这款游戏,但在本来就数量不多的高画质3A游戏中,这款游戏里的体积云应该是独一份的了——更何况Nubis3的开发者已经是十年以上的资深体积云渲染专家了。
一方面,平衡内存和指令调用的实践智慧让人印象深刻——即使需要降低分辨率来提高帧数,也不是简单的整体降低,而是区分了两个pass;另一方面,作者通过照片和观察生活中的物理现象的方式来归纳模型——这种以实证为基础的方法也让人觉得很靠谱。
参与开发的成员,前置的讲座,以及参考文献
最后是本文的资料链接:
Nubis3: Methods (and madness) to model and render immersive real-time voxel-based cloud 1080P PPTX PDF