版权没有,请尊重翻译成果,有翻译错误请指出,规范性转载。@秋意正寒html
本文经过解读 Scene.render 方法,观察 WebGL 在 Cesium 1.9 中如何渲染一帧。读者能够在 Scene.render 方法处打断点进入调试。git
因为 Cesium 专一于可视化地理空间内容,所以多光源的场景并不擅长、很少见,Cesium 使用的是传统的前向阴影流水线。Cesium 的流水线之因此独特,是由于它使用了多个视锥体来支持大范围的视距,而不须要对z轴进行扭曲变化(这句翻译得不是很好)。github
Cesium 把每一帧的生命周期相关的数据存储在一个叫 FrameState
(参考 FrameState.js) 的对象中。在帧最开始时,初始化相机参数、时间之类的东西。帧的状态可用于其余的对象,例如 Primitive 对象能够调用当前帧的状态数据。web
UniformState(参考 UniformState.js)是 FrameState 的一部分,它具备共有的、预先计算好的 uniforms。在帧开始时,它计算视图矩阵、太阳向量等 uniforms。canvas
Cesium 的动画、更新、渲染流水线是很经典的,动画的步骤多是对 WebGL 无交互地对 primitive 的移动、改变其材质属性、添加删除 primitive 等。这并非 Scene.render 的一部分,这些动画可能会在渲染一帧以前经过代码显式指定,或者使用 Entity API 的 Property 在后面默默改变。数组
经典的 动画-更新-渲染 流水线。缓存
Scene.update 这个方法最主要的第一步是更新全部在 Scene 中的 primitives。(参考Scene.js > function updatePrimitives(scene) {})框架
在这一步,每个 primitive 将会:函数
DrawCommand
对象,这些对象表明的是 primitive 们建立的 drawcall 和 WebGL 资源。像 polyline、billboard集合可能会返回一个 DrawCommand,Globe 对象或 Model 等则可能会返回数百个 DrawCommands。大多数帧会包含几百到几千个 DrawCommand。译者注:下面代码选自 1.9,在 1.75 已经找不到这个函数了,commandList 也找不到了。不过,这个 updatePrimitives 函数是在 function render 函数中调用的,render 函数(不是Scene.prototype.render 方法)在1.75版本中却还在,_primitives.update() 这一步也移动到了 scene.updateAndExecuteCommands() 方法中的 executeCommandsInViewport() 函数里了,commandList 也被拆开了。有兴趣的读者能够对比研究研究。性能
// Scene.js 中的 updatePrimitives() 函数 -- v1.9 function updatePrimitives(scene) { var context = scene.context; var frameState = scene._frameState; var commandList = scene._commandList; if (scene._globe) { scene._globe.update(context, frameState, commandList); } scene._primitives.update(context, frameState, commandList); if (defined(scene.moon)) { scene.moon.update(context, frameState, commandList); } } // 调用 function render(scene, time) { // ... updatePrimitives(scene); // ... }
Cesium 中的地球对象:Globe,地形和卫星影像瓦片的引擎,同样是一个 “primitive”。它的更新功能指挥着瓦片的层次调度、剔除,以及负责管理加载地形瓦片和影像瓦片的内存。
剔除,是图形引擎对看不见的物体进行消除的优化方法,这样流水线就没必要处理那些看不到的对象了。经过了可见性测试的物体,被称做“潜在可见性数据集”,将随着流水线传递下去。为了提升速度,可见性测试使用了不精确的测试方法,全部这些 “潜在可见性数据集” 可能最终是可见的,也多是不可见的。
对于独立的绘制命令,Cesium 支持使用命令的的 boundingVolume (世界坐标空间下)进行视锥体和地平线的自动剔除。(这句话翻译得不太好,不太懂表达了什么)对于能自我剔除的 primitive,例如 Globe 对象,能够关闭这个功能。
传统的图形引擎检查每个绘制命令,进行可见性测试,从而找到潜在的可见数据集。Cesium 的 createPotentiallyVisibleSet 函数(译者注:如今移动到 Scene.view 属性内了)先走了第一步,它将绘制命令动态地分为多个视锥体(一般是三个),这些视锥体把全部的绘制命令绑定在一块儿,并保持必定的远近比例以免z值冲突。每一个视锥体的截头体的张角和宽高比是同样的,只有近平面和远平面的举例不一样。
这个函数作了优化。它利用时间上的连续,若是先后帧的绘制命令条件合适,那么已经计算好的视锥体及其截头体将会被重用,以减小计算量。
上图左边:多个视锥体(紫橙绿);右边:一个视锥体的截头体的绘制命令
译者注
这段文章啃得生硬,不知道讲了什么东西,应该是 Cesium 的多视锥体机制能更好地优化剔除吧,源码要去了解 createPotentiallyVisibleSet 是怎么作的。注意版本。
每一个视锥体都有本身的绘制命令列表,如今就能够触发 WebGL 的 drawElements 和 drawArrays 了。
Cesium 的渲染流水线核心是 executeCommand
函数,你能在 Scene.js 中找到。
首先,清除颜色缓存。若是使用了与顺序无关的透明度、快速近似抗锯齿(FXAA),则它们的缓存也被清除。
而后,使用整个视锥体(不是上面分开的那三个)绘制一些特殊的 primitive:
接下来,从最远的视锥体开始,按如下步骤执行每一个视锥体中的绘制命令:
OIT.js
中的 executeCommands() 函数。使用多个视锥体会致使一些有趣的状况,例如一个绘制命令跨了两个视锥,那么命令就会被执行两次。
至此,每一个视锥体的截头体的全部绘制命令已经执行,若是使用 OIT,则执行最后的 OIT 复合遍历将被执行。若是 FXAA (快速抗锯齿)启用了,那么还会执行全屏传递以进行抗锯齿。
与 Heads-Up Display 相似,最后执行的是 overlay pass 绘制命令。(这句话仍是不懂)
Cesium 在 1.9 版本时的渲染流水线。
在每一个视锥体中,由 primitive 传过来的绘制命令是按顺序执行的。例如,Globe 对其绘制命令进行了从前到后的排序,以利用 GPU early-z 的优点(z优先?不懂,须要查资料学习)。
绘制命令的数量决定了性能如何,所以 primitive 经过把多个对象的组合在一块儿,仅发出一条绘制命令来提升性能。例如,BillboardCollection 在一个顶点缓存中存储尽量多的 Billboard,并使用同一个着色器进行渲染。
Cesium 的拾取功能利用了颜色缓存。每个可拾取的对象都有一个惟一的 id(即颜色)。
给定视窗坐标系的 (x,y) 坐标,为了肯定拾取了什么,则须要将帧渲染到屏幕以外的帧缓存,这个写在外面的帧缓存记录的颜色值即为拾取的 id。随后,使用 WebGL 的 readPixels 函数,读取颜色,拿到id,而后就能返回拾取的对象了。
Scene.pick 方法的流水线和 Scene.render 方法很相似,不过拾取的东西并不须要包括天空盒、太阳、大气层,因此能简化一些。
下列计划将提高帧渲染的性能。
上面关于在 Scene.render 方法中的遍历顺序( opaque不透明,半透明translucent,overlay覆盖)其实在普通的图形引擎中很常见。实际上,不透明还要分开成 globe 和 opaque(不太懂是什么意思,应该说的是不透明的东西还能继续分解为地球对象和其余不透明对象)。
能够这么说,分开后的不透明物体顺序是:基本的 globe对象、贴地的矢量数据和通常的不透明对象。
阴影将经过阴影映射实现。场景从可产生阴影的光线触发,进行渲染,每一个能投射的物体均做用于深度缓存,或者阴影贴图(模型到光源的距离?)。而后,在主色通道中,每一个能接收阴影的对象检查每一个灯光的阴影贴图中的距离值,检查是否在阴影内。
这个实现很是复杂,须要解决混叠伪影、柔和阴影、多视锥截头体以及地形引擎等因素。
阴影子集添加了对深度纹理的支持。例如,深度纹理能用来对 billboards 在地形上的深度测试,并根据深度值从新构造它在世界空间中的位置。
添加阴影后,提供了对不一样角度进行渲染的能力。WebVR基于此。
每一个眼睛使用一个视锥体进行渲染便可。
阴影的另外一个扩展能力是对立方体进行六面贴图,以进行环境渲染,将环境贴到盒子的六个面上,以显示盒子位于场景中的何处。这个功能会很是消耗计算资源(做者猜想),因此可能并不会用在实时计算上。
Scene.render 具备一些后处理效果,例如辉光、FXAA和OIT合成等。
官方计划建立一个通用的后处理框架,将纹理做为输入,经过一或多个后处理阶段来处理它们。这些后处理操做基本上是在视图窗口上的帧运行片元着色器,而后输出。
与其用硬算的方式制造辉光,能够用后处理的方式更好地完成,还能作景深、SSAO、发光、运动模糊等效果。
Cesium 使用旧式的 GPGPU 进行 GPU 加速来计算图像重投影。过程当中,离屏渲染一个与屏幕(就是canvas)对齐的帧,而后推到着色器中。(不知道说了什么)
将来的渲染流水线
译者注
渲染流水线这个过程是至关的长,并且这个“将来的渲染流水线” 不必定适用了,不过大致框架的思路已经阐明,但愿各位读者能获得一些启发,有些地方翻译得并非很好。
做者和 Dan Bagnell 编写了大多数的渲染器。要得到细节能够参考 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.