译者注:本文翻译自Cesium官方博文《Graphics Tech in Cesium - Rendering a Frame》,May 14, 2015 by Patrick Cozzi。
目录
- 设置
- 更新
- 潜在可见集合
- 渲染
- 排序和批处理
- 拾取
- 未来的工作
- 地面通道
- 阴影
- 深度纹理
- WebVR
- 立方体贴图通道
- 后处理效果
- 计算通道
- 致谢
- 参考
本文通过追溯Cesium的Scene.render,解释了Cesium 1.9如何使用其WebGL渲染器渲染每一帧。在Scene.render中放置一个断点,运行一个Cesium应用,然后继续。
由于Cesium专注于可视化地理空间内容,因此使用许多不同光源的场景并不常见,因此Cesium使用传统的前向阴影管线(Forward Rendering)。 Cesium的管道之所以独特,是因为它使用了多个视锥体来支持巨大的视距,避免造成Z-fighting现象[Cozzi13]。
译者注:正向渲染/前向渲染(Forward Rendering)与延迟渲染(Deferred Rendering)相对,延迟渲染多用于多光照的场合。参看《正向渲染和延迟渲染彼此之间有什么不同》。
设置
Cesium将具有帧生存期的常量存储在FrameState对象中。在每一帧的开始阶段,将使用诸如相机参数和仿真时间之类的值对其进行初始化。 这个FrameState对可用于其他对象,例如在整个帧周期中生成命令(绘图调用)的图元(primitives)。
UniformState是FrameState的一部分,具有通用的预先计算的着色器uniform变量。 在每一帧的开始阶段,诸如视图矩阵和太阳光线矢量等uniform变量将会被计算。
更新
Cesium具有经典的动画/更新/渲染管线,动画步骤可以在不与WebGL交互的情况下移动图元(primitives,Cesium表示可渲染对象的术语),更改材质属性,添加/删除图元等。 这不是Scene.render的一部分,它可能会在应用程序代码中,通过在渲染帧之前显式设置属性时发生;或者可能会在Cesium中隐式地,通过使用Entity API分配时间变值触发。
经典的动画/更新/渲染管线
Scene.render的第一步是更新场景中的所有图元。 在此步骤中,每个图元会
- 创建/更新其WebGL资源。例如,编译/链接着色器,加载纹理,更新顶点缓冲区等。Cesium永远不会在Scene.render之外调用WebGL,因为这样做会增加requestAnimationFrame的耗时,并使其难以与其他WebGL引擎整合。
- 返回一组DrawCommand对象的列表,这些对象可以表示成绘图调用命令,并引用了由图元创建的WebGL资源。 有些图元(例如折线或布告板(billboard)集合)可能会返回单个命令;而其他的图元(例如Globe或3D模型),可能会返回数百个命令。 大多数帧将是几百到几千个命令的。
Globe对象是Cesium的地形和图像引擎,可以看作是一个图元(primitive)。它的更新函数可处理多层级结构的细节和拣选,以及用于加载地形和图像图块的核心外内存管理。
潜在可见集合
拣选是图形引擎常见的优化方法,能够快速的消除视野外的对象;以便管道的其余部分不必处理这些对象。通过可见性测试的对象就是“潜在可见性集”,并继续沿管道传输。它们可能是可见的,因为使用了不精确的保守可见性测试来提高速度。
Cesium通过使用commands的世界空间的boundingVolume(包围盒)对象,来对单个命令(图元,例如执行自己拣选操作的Globe,可以禁用此功能),自动执行视锥和水平剔除[Ring13a,Ring13b]。
传统的图形引擎可以通过检查每个命令(command)的可见性测试来找到潜在的可见集。 Cesium的createPotentiallyVisibleSet函数更进一步,将命令动态地分为多个视锥(通常是三个),它们将所有命令限制在一定的范围之内,并保持恒定的远近比以避免深度冲突( z-fighting)。每个视锥体具有相同的视场和宽高比,只有近平面和远平面的距离不同。作为一种优化,此函数利用时间相干性,并且如果对于该帧的命令仍然合理,则将重用最后计算的视锥。
左:多视锥体;右: 在视锥体中的命令
渲染
每个视锥体都有各自的命令列表,组成视锥体列表后,我们现在可以执行命令了——也就是执行WebGL的drawElements/drawArrays的调用。以下会顺着追踪Cesium的executeCommands相关的内容,因为这是Cesium渲染管线的核心。
首先,清除颜色缓冲区。如果使用了与顺序无关的透明度(OIT)[McGuire13,Bagnell13]或快速近似抗锯齿(FXAA),则它们的缓冲区也将被清除(有关更多信息,请参见下文)。
然后,使用整个视锥体(不是单个计算的视锥之一)来渲染一些特殊情况的图元:
- 包含星星的天空盒。 老式的优化方法是先渲染天空盒,然后跳过清除颜色缓冲区的操作。 如今,这实际上会影响性能,因为清除颜色缓冲区有助于最大程度地压缩GPU(与清除深度相同)。最佳做法是使天空盒最后渲染以利用Early-Z。Cesium首先渲染天空盒,因为它必须这样做,需要在每个视锥体之后清除深度(正如下面所描述的那样)。
- 天空大气。来自[ONeil05]的基本大气。
- 太阳。如果太阳是可见的,则渲染太阳的布告板(billboard)。如果还启用了泛光过滤器,则会剪掉太阳,然后几个通道将会被渲染:对颜色缓冲区进行降采样,变亮,模糊(分别在水平和垂直通道中进行),然后进行升采样并与原始混合。
接下来,从最远的视锥开始,按照以下步骤执行每个视锥中的命令:
- 视锥体特定的uniform状态量将会被设置。这只是视锥体的近距离和远距离。
- 深度缓冲区将会被清空。
- 首先执行不透明图元的命令。 执行命令会设置WebGL状态,例如渲染状态(深度,混合等),顶点数组,纹理,着色器程序和统一,然后发出绘图调用。
- 接下来,执行半透明命令。如果由于缺少浮点纹理而不支持OIT,则将命令从头到尾排序,然后执行。否则,OIT用于提高相交半透明对象的视觉质量,并避免排序的CPU开销。命令的着色器针对OIT进行了修补(并缓存),如果支持MRT,则通过一次OIT渲染进行渲染,或者作为后备通过两次渲染。可以参阅OIT.executeCommands。
使用多个视锥会导致一些有趣的情况,例如如果命令重叠多个视锥,则命令可以执行多次。详细信息请参见[Cozzi13]。
至此,每个视锥体的命令已执行。如果使用OIT,则执行最后的OIT复合通道。如果启用了FXAA,则会执行全屏通道以进行抗锯齿。
与平视显示器(HUD)相似,覆盖通道的命令最后执行。
Cesium当前的渲染管线。
排序和批处理
在每个视锥中,保证按图元返回命令的顺序执行命令。例如,Globe从头到尾对其命令进行排序,以利用GPU Early-Z优化。
由于性能通常取决于命令的数量,因此许多图元使用批处理通过将不同的对象组合为一个命令来减少命令的数量。 例如,BillboardCollection在一个顶点缓冲区中存储尽可能多的布告板,并使用相同的着色器对其进行渲染。
拾取
Cesium使用颜色缓冲区实现拾取。每个可选取的对象都有一个唯一的ID(颜色)。为了确定在给定的(x,y)窗口坐标中拾取到内容,将帧渲染到屏幕外的帧缓冲区,其中写入的颜色为拾取ID。然后,使用WebGL的readPixels读取颜色,并将其用于返回拾取的对象。
Scene.pick的管道类似于Scene.render,但由于例如天空盒,大气层和太阳无法拾取而得以简化。
未来的工作
关于一帧中进行的渲染工作,有一些正在进行中还处于计划阶段的提升。
地面通道
上面描述的Scene.render中的通道在图形引擎中很常见:OPAQUE,TRANSLUCENT,然后是OVERLAY。 实际上,OPAQUE分为GLOBE和OPAQUE。 可能会对其进行扩展,以便其顺序为:基本globe,固定在地面上的矢量数据,然后是一般的不透明对象。 参见#2172。
阴影
阴影将通过shadow mapping实现。从每个阴影投射光的角度渲染场景,并且每个显示投射对象都有助于深度缓冲区或阴影贴图,即从灯光角度到每个对象的距离。然后,在主色通道中,每个阴影接收对象检查每个光源阴影图中的距离,以查看其片段是否在阴影内。实际的生产实现非常复杂,需要解决锯齿伪像,柔和阴影,多个视锥体以及Cesium的核心外地形引擎。 参见#2594。
深度纹理
添加阴影的一个子集增加了对深度纹理的支持,例如,可以将其用于针对地形进行深度测试的告示板,并根据深度重构世界空间的位置。
WebVR
添加阴影的另一部分是从不同角度渲染场景的能力。WebVR支持可以基于此。标准相机和视锥用于拣选和LOD选择,然后使用两个偏心的视锥(每个眼睛一个)进行渲染。NICTA的VR插件使用类似的方法,但是使用了两个画布。
立方体贴图通道
阴影的另一个扩展是渲染立方体贴图的能力,即形成一个盒子的六个2D纹理描述了盒子中间某个点周围的环境。立方体贴图可用于反射,折射和基于图像的照明。立方体贴图通道的使用代价可能会变得昂贵,因此我怀疑这将仅少量用于即时生成。
后处理效果
Scene.render具有一些后期处理效果,这些效果经过硬编码,例如太阳泛光,FXAA甚至是OIT合成。我们计划创建一个通用的后处理框架,将纹理作为输入,通过一个或多个后处理阶段运行它们,这些通道基本上是在视口对齐的四边形上运行的片段着色器,然后输出一个或多个纹理。例如,这将用驱动后处理框架的数据代替许多硬编码的太阳泛光,并打开许多新效果,例如景深,SSAO,发光,运动模糊等。 请参阅这些说明。
计算通道
Cesium会使用老式的GPGPU来进行GPU加速的图像重投影,在该渲染过程中,它将渲染一个与屏幕视口对齐的四边形,以将重投影推向着色器。这可以通过在帧开始时的计算过程中使用后处理框架来完成。参见#751。
潜在的未来Cesium渲染管线(新阶段以粗体显示)。
致谢
我和Dan Bagnell编写了大多数Cesium渲染器。要获得娱乐,请参阅我们的Cesium Wiki注释。 当我还在读高中时,Ed Mackey在90年代就在AGI进行了最初的多视锥体实现。
参考
[Bagnell13] Dan Bagnell. Weighted Blended Order-Independent Transparency. 2013
[Cozzi13] Patrick Cozzi. Using Multiple Frustums for Massive Worlds. In Rendering Massive Virtual Worlds Course. SIGGRAPH 2013.
[McGuire13] McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013
[ONeil05] Sean O’Neil. Accurate Atmospheric Scattering. In GPU Gems. Edited by Matt Pharr and Randima Fernando. 2005.
[Ring13a] Kevin Ring. Horizon Culling. 2013.
[Ring13b] Kevin Ring. Computing the horizon occlusion point. 2013.