前言
其实在主机游戏一直以来的发展中,一开始留给体积渲染的性能空间是不大的。即使设备的性能逐渐提升,解放了更多高级渲染特性,体积云的性价比肯定是排在体积光、复杂水体、精细化头发等之后(这几个方面我之前都有文章介绍过了)。
但是在画面高清化的偏写实游戏中,云也像体积光和水体一样,必须要跟上整体画质进化,因此人们也很难想象3A大作中的云是一点动态没有的“纸片云”——属于氛围都烘托到这了,不做体积云肯定也是不行的。
概念原画中的云
我个人选择这篇2015年的SIGGRAPH分享作为和大家共同研习体积云渲染的起点,大概有两方面原因:
- 一方面,近10年以前的技术其实现在差不多能刚好或即将下沉到低性能主机设备和移动端,因此是今后10年仍不过时的技术。
- 另一方面,体积云渲染基本也是从这个阶段开始实现了比较好的动态、光照着色以及阴影,后面的发展可以理解为以此为基础做的迭代。
文章还是以翻译原文的讲稿为主,重点部分会摘一些原文。由于篇幅原因会分为上下两篇,打星号的部分则是我个人的补充说明。
1 早期探索方向
云的实现目标(游戏内渲染)
- 艺术家可定制的
- 真实感——多种不同样式的云
- 与天气系统集成
- 随时间流动和变化
- 具有史诗感
早期云的探索
真实感的CG云不是一个简单的课题。因此,在我们尝试解决创建完整天空系统的全部课题前,我们考虑先探索对独立的云资源进行制作和光照的不同方式。
我们最早成功的建模方法是使用定制化的流体生成器来产生云。生成的结果效果很好,但对艺术家来说很难控制——如果他们没有任何流体模拟的经验。Guerrilla毕竟还是一个游戏公司(不能要求艺术人员懂复杂物理)。
最终我们选择了从简单形体中为云建模的方案:
- 将它们体素化
- 将这些体素输入到我们的流体生成器中
- 直到我们得到一个像云的形体
*到这里还是说的建模软件Houdini里的事。
然后我们开发了一个基于预计算的光照模型,包含了直接和次级散射光照。图中的结果是在Houdini中基于CPU计算10秒后得出的。
我们探索了3种把这类云资源放入游戏的方式。
最初我们尝试把云作为我们场景景观的一部分,这意味着把它们从流体生成器种导出为多边形结果,使用球谐光照(spherical harmonics)方式烘焙光照数据。这只对有一定厚度的云有效,对于蓬松的云则不行。
因此,我们考虑尝试强化billboard(广告牌模式,指始终朝向摄像机的一种渲染方式)方案以支持更多云的朝向,以及一天中不同时间的样式。虽然成功了,但我们发现无法快速的重新生成云的内阴影。
我们尝试把所有(预生成的)云作为一个集合,来制造能基于深度与大气系统混合的天穹。某种程度上这是可行的。
在这一点上我们退回了一步来评估哪些部分是不可行的。这一方案生成的云无法随时间变化;这也不是一个使云在物体上方穿过时的好方案;并且这些方案的内存占用和overdraw都太高了。
所以传统的基于(加载很多)资源的方法可能不可行。
*这部分主要在说预生成云的常见问题:比较固定不动态(阴影、流动等);无法很好处理穿插(穿插会是“图片和模型穿插”);上规模后性能开销较大。
2 体积云分析与尝试
*在这个项目当时的情况,全体积方式渲染的云还是比较难接受的,因为性能开销较大。后面他们探索之后大概就是取了一个折衷的方案。
- 通常开销很大:需要读取很多纹理;需要光线步进(着色、阴影等方面);会有很多嵌套的循环。
- 可以使用很多经过验证的实时体积光照方案。
- 有已经很成熟的通过噪声图来建模云的方案。
- 我们能否通过取舍在性能开销和好效果之间取一个平衡?
我们最初的尝试是:在摄像机前堆叠很多多边形,并通过它们作为3D Perlin噪声的容器。虽然运行极度慢,但效果是可以的——不过我们想要更多不同类型的云而不仅仅是这一种带状云。
因此我们在Houdini中通过生成平铺3D纹理的方式来模拟云的形体。使用Houdini的GL扩展,我们构建了一个原型的GL shader来开发云的系统和着色模型。
最终,通过很多hack的方式,我们得到了和参考很接近的模仿效果。
However, it all fell apart when we put the clouds in motion. It also took 1 second per frame to compute. For me coming from animated vfx, this was pretty impressive, but my colleagues were still not impressed.
然而,这一切在我们为云加入动态时都崩塌了——它需要消耗每帧1秒来运算。对于我这个从事动画和VFX(动效)领域的人来说,这个结果已经很让人印象深刻了,不过我的同事似乎并没有那么振奋(1秒离实时还差很多,只渲染云的话“实时”至少要是0.01秒内)。
So I thought, Instead of explicitly defining clouds with pre determined shapes, what if we could develop some good noises at lower resolutions that have the characteristics we like and then find a way to blend between them based on a set of rules. There has been previous work like this but none of it came close to our look goals.
所以我考虑,与其明确地通过预计算的形体来定义云,如果我们可以开发一些在低分辨率下也能有好的效果、并包含一些我们期望特性的噪声,我们就可以找到基于一定规则来混合两者的方式。这里有一些前置工作,不过都和我们最终的视觉目标关系较远(因此略去不介绍了)。
3 建模
*这里是广义上的建模概念,更类似“数学建模”中的建模——具体就是设计图形算法、数据方案、渲染方案等。
这些最终把我们导向了在《地平线》中采用的方案。为了更好的解释,我把整个方案拆解成4个章节:建模、光照、渲染和优化。
在开始解释我们如何为云的形体建模前,铺垫一下对云的基础理解是很有帮助的——到底什么是云以及它如何变化成不同形体。
为云分类能帮助我们更好的交流,以及明确定义我们绘制它们的场合。基础的云有如下类型:
- 层云群体(strato clouds)包含层云(stratus)、积云(cumulus)、层积云(stratocumulus)
- 高云(alto clouds),指层云之上的带状(bandy)或团状(puffy)的云
- 卷云(cirro clouds)指在上层大气的有着大电弧带的云和小型团状的云
- 最终还有所有这些云的大综合,积雨云(cumulonimbus)
作为对比,珠穆朗玛峰海拔是8000米以上(对应图中从1500米开始的覆盖范围)。
在研究了云的类型后,我们又学习了影响云形状的外力要素。
The best source we had was a book from 1961 by two meteorologists, called “The Clouds” as creatively as research books from the 60’s were titled. What it lacked in charm it made up for with useful empirical results and concepts that help with modeling a cloud system.
最好的素材是一本1961年的两位气象学家写的书,书名叫《The Clouds》——它和其它60年代的名声响亮的科研读本一样富有创造性。书中有着尽管缺乏阅读魅力但富含经验性结果的内容和概念,它们帮助我们建模了云系统:
- 在低温下密度增加
- 随着海拔提高温度降低
- 密度增加会加速雨雪的形成
- 风向会随着海拔高度变化
- 云层会随着地热缓慢上升
- 紧密的区域会随着自身上升形成较圆的形状
- 受光照的区域像雾一样漫反射
- 大气涡流持续地使云扭曲
我们的建模方法使用光线步进(ray marching 这个词后文都保持英文)来产生云。
我们从摄像机发出射线(开始步进)以采样噪声图和一组渐变,以在采样器中确定云的形体。
在ray march的过程中,我们使用采样器来构建一个Alpha通道,并计算基础光照(如图)。
There are many examples of real-time volume clouds on the internet. The usual approach involves drawing them in a height zone above the camera using something called fBm, Fractal Brownian Motion. This is done by layering Perlin noises of different frequencies until you get something detailed.
网上有很多实时体积云的例子。通常的一种绘制方式是在摄像机上方的一个高度区域使用fBm绘制(Fractal Brownian Motion 分形布朗运动)——是通过层叠不同频率的Perlin噪声的方式得到一些细节的结果。
This noise is then usually combined somehow with a gradient to define a change in cloud density over height.
这一噪声结果通常会与一个渐变参数进行组合,以定义云在不同高度的密度。
*fBm这里可以简单理解成一种噪声组合采样方案,组合采样策略如文中所述。
这样产生了看着不错但非常“程序性外观”的云。哪里出错了?它们没有更大范围的外轮廓,以及缺乏视觉提示——我们无法基于云的形体明确感受到它的流动变化。
作为对比,在这个照片中我们能对云有较好的整体感受。这些云像工厂中升起的泡泡团一样上升——注意云形体顶部的圆形结构和底部的蓬松结构。
fBm方法能得到较好的蓬松形体,但缺乏能带来一些动态感的凸出和鼓起的部分。我们需要改进以超过网上那些随处可见的shader中的效果。
这些被我称为billow(鼓起、鼓包)的部分,它们是聚拢的,有时有着接近花椰菜(cauliflower)的形状。
由于Perlin噪声并不提供切割出它们的方案,因此我们需要开发自己的噪声算法。
Worley噪声在1996年被Steven Worley发明,通常被用来模拟焦散(caustics 这个词的翻译之前讲水渲染提到过)或水下效果。图中可以看到它颜色反转时的效果。
- 它可以产生紧密挤压的鼓包形体
- 我们将它以标准Perlin fBm的方式来分层
- 我们把它作为Perlin噪声的一种补充,这使我们能在保持前者的联通性的基础上增加一些鼓包的形体。
- 我们把这个方案称为"Perlin-Worley"噪声
在游戏中,为了性能考虑最好把噪声存储成平铺3D纹理(而不是实时生成)——同时我们也希望尽量减少纹理读取,并尽量降低其分辨率。
我们把噪声组合压缩到了2张3D纹理和1张2D纹理。
- 第一个3D纹理有4个通道,它有着128^3的分辨率
- 第一个通道是前面提到的Perlin - Worley噪声,其它3个通道是频率递增的Worley噪声
- 这个3D纹理被用来定义云的基本形体
- 第二个3D纹理有3个通道,它有着32^3的分辨率
- 3个通道都是频率递增的Worley噪声
- 这个纹理被用来给基础的云形体增加一些细节
- 我们的2D纹理有3个通道,它有着128^2的分辨率
- 它们都存储非发散的卷曲噪声(curl noise),被用来模拟云的流动
- 我们使用这个噪声来扭曲云的形体,以添加一些涡流的感觉
回到标准方案中被称为高度渐变(基于海拔改变噪声信号)的部分:
- 我们使用了3个预运算结果用来表示低海拔的云类型(用前面提到的基于采样位置参数化的方式)
- 我们也使用了一张值在0到1之间的渐变图来表示对应采样位置希望的云覆盖率
图中右侧区域是《地平线》中摄像机旋转30度向上的视图,我们将展示基于这一摄像机上方区域绘制云层的过程(文稿中没有,是结合后面的图一起看。完整过程有兴趣可以去看PPT中的视频):
- 首先,我们通过采样第一张3D纹理并乘以高度渐变参数值,构建了云的基础形体
- 下一步是乘以覆盖率参数并减少云层底部的密度
这保证了云层底部蓬松的同时,在整体的观感上更接近自然界的样子;同时也记住密度需要基于海拔变化。在这一步得到的云的形体基础上,我们再来添加细节。
下一个步骤是:
- 通过采样第2个3D纹理并相减来在边缘处削弱云的形体(一点小提示,通过反转Worley噪音图的颜色可以得到很好的蓬松形体效果)
- 我们也通过2D卷曲噪音来扭曲第2个3D纹理,以模拟漩涡状扭曲类似大气扰动的感觉
这是游戏中最终呈现的效果。我通过调整覆盖率参数来使它们变厚,然后调整高度渐变参数使积云变成层云(文稿中也没有,需要看视频)。
现在我们已经有了得体的静态云层效果,接下来我们要添加动态和天气系统。
云覆盖率和云类型是我们天气系统中开放出的2个功能模块。
对于积雨云和降雨我们有额外的控制模块。
图中左下角的图片展示了渲染出图中效果云层对应的天气设定。
其中浅粉色和白色的图案是天气系统的输出。红色是覆盖率,绿色是降水量,蓝色是云类型。天气系统在游戏运行时通过模拟系统来控制住几个通道。图中展示的是头顶有积雨云、远距离有层云的情况。我们也能控制偏移量来使模拟基于艺术家的感觉进行调整。
默认设置状态是积云和层云的组合。区域中有更多红色部分和少量蓝色部分,使它们更像层云。这部分组合可以从图中左下角的分布图看到。
图中调整降水量参数使云层变为了80%覆盖率的积雨云。
降水量控制不仅使云的外观变化,还带来降雨效果(空间VFX粒子的方式)。图中是降水量调整到100%的情况。
如果我们增加风速,并增加降雨概率,就能模拟出类似暴风雨云层的效果。
我们的天气系统也能确保云层始终呈现出丰富的外观,并能越过山峰。
云的绘制范围在15,000到35,000米之间。通常云的外观是50%覆盖率的积云效果。
在E3的预告片里,我们用定制化的参数纹理覆盖了天气系统的输入。你可以在图中左下角看到对应的纹理,通过这种方式我们可以绘制定制化的天空。
总结一下我们的建模方案:
- 我们遵照标准的ray-march与采样器的方案框架
- 但我们基于两层细节纹理来构建云层:一个低频率的基础外形,与一个高频率的细节扰动
- 我们的定制化噪声基于Perlin、Worley和卷曲(Curl )噪声
- 我们使用一组预设纹理来对应不同云类型的高度密度变化,以及覆盖率
- 固定的转场天气系统则由定制化的纹理来驱动
- 所有效果都是能受风向影响动画化的
结语
其实以后人的视角来看,可能会觉得他们当时的探索(作为索尼第一方来说)甚至就是“从零开始”了,但这其实也正是图形技术在游戏工业化中的显著特点:
- 一方面发展非常快,机器性能只要能达到,就有很多在离线渲染领域已经做过的方案慢慢可以改到实时渲染中。熟悉两者关系的可能知道,离线领域对各种课题的工业化(包括光线追踪)都领先十几年甚至几十年,包含数据结构和图形算法等很多方面。
- 另一方面就是需要针对特定的使用情景做定制化开发,具体到某款主机某个游戏,真就得他们自己试才能找到当时最好的方案。
由于分享人是VFX方面的leader,因此文中可以兼具视觉模型的探索与物理模型的探索这两种不同的视角。但是无论如何,实时渲染中的体积云都还是一项相对昂贵的技术。在2015年,游戏中任何形式的体积云都还是比较超前的,大多数还是在使用“天空盒子”这种保守的方案。
篇幅原因这次仅包含了他们正式方案中最长的建模(Modeling)部分,下一篇会覆盖后续的光照、渲染、优化几个部分。
最后是资料链接:
The Real-time Volumetric Cloudscapes of Horizon: Zero Dawn这篇分享的PDF地址
这篇分享的PPT地址(含视频版)