Cesium渲染调度
1. 引言
Cesium是一款三维地球和地图可视化开源JavaScript库,使用WebGL来进行硬件加速图形,使用时不需要任何插件支持,基于Apache2.0许可的开源程序,可以免费用于商业和非商业用途
Cesium官网:Cesium: The Platform for 3D Geospatial
Cesium GitHub站点:CesiumGS/cesium: An open-source JavaScript library for world-class 3D globes and maps (github.com)
API文档:Index - Cesium Documentation
通过阅读源码,理清代码逻辑,有助于扩展与开发,笔者主要参考了以下两个系列的文章
本文描述Cesium的渲染流程
2. 概述
Cesium渲染流程大致如图:
- endFrame()准备需要渲染的数据
- updateAndExecuteCommands()更新和调度渲染指令(图元对象含有渲染指令)
- Scene.render()进行绘制渲染
- CesiumWidget展示Scene.render()的内容并让其不断绘制(RenderLoop)
3. 渲染流程
Cesium的使用,往往是从创建Viewer开始的:
// ... <div id="cesiumContainer"></div> <script> const viewer = new Cesium.Viewer('cesiumContainer') </script> // ...
new一个Viewer就可以创建一个地球,这其中经历了什么样的过程呢?
Viewer构造函数大致如下:
function Viewer(container, options) { // ... container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); // Cesium widget container const cesiumWidgetContainer = document.createElement("div"); // Cesium widget const cesiumWidget = new CesiumWidget(cesiumWidgetContainer); // ... }
Viewer主要由CesiumWidget构成,其构造函数大致如下:
function CesiumWidget(container, options) { // ... container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); // ... const canvas = document.createElement("canvas"); const scene = new Scene({ canvas: canvas, // ... }); //Set the base imagery layer let imageryProvider = options.globe === false ? false : options.imageryProvider; if (!defined(imageryProvider)) { imageryProvider = createWorldImagery(); } //Set the terrain provider if one is provided. if (defined(options.terrainProvider) && options.globe !== false) { scene.terrainProvider = options.terrainProvider; } // 设置循环渲染 this._useDefaultRenderLoop = undefined; this.useDefaultRenderLoop = defaultValue( options.useDefaultRenderLoop, true ); // ... }
CesiumWidget中默认渲染函数useDefaultRenderLoop指向startRenderLoop:
useDefaultRenderLoop: { // ... startRenderLoop(this); },
渲染函数startRenderLoop为:
function startRenderLoop(widget) { // ... let lastFrameTime = 0; function render(frameTime) { // .... widget.render(); requestAnimationFrame(render); // ... } requestAnimationFrame(render); }
widget的render函数指向scene的render函数:
CesiumWidget.prototype.render = function () { // ... this._scene.render(currentTime); };
scene的render函数:
function render(scene) { // ... if (defined(scene.globe)) { scene.globe.beginFrame(frameState); } scene.updateEnvironment(); scene.updateAndExecuteCommands(passState, backgroundColor); scene.resolveFramebuffers(passState); if (defined(scene.globe)) { scene.globe.endFrame(frameState); } // ... }
updateAndExecuteCommands()负责管理,创建执行Commands的Tasks,自己并不负责Tasks内容的实现
scene.globe.endFrame()中,会对该帧所涉及的GlobeTile的下载,解析等进行处理
先看看scene.globe.endFrame()的数据处理:
scene.globe.endFrame()指向surface.endFrame()
Globe.prototype.endFrame = function (frameState) { this._surface.endFrame(frameState); };
surface是图元QuadtreePrimitive:
this._surface = new QuadtreePrimitive({ tileProvider: new GlobeSurfaceTileProvider({ terrainProvider: terrainProvider, imageryLayers: imageryLayerCollection, surfaceShaderSet: this._surfaceShaderSet, }), });
QuadtreePrimitive的endFrame()方法进行影像和高程的处理:
QuadtreePrimitive.prototype.endFrame = function (frameState) { // ... // Load/create resources for terrain and imagery. Prepare texture re-projections for the next frame. processTileLoadQueue(this, frameState); updateHeights(this, frameState); updateTileLoadProgress(this, frameState); };
再来看看updateAndExecuteCommands()方法:
Scene.prototype.updateAndExecuteCommands = function (passState, backgroundColor) { // ... executeCommandsInViewport(true, this, passState, backgroundColor); // ... };
updateAndExecuteCommands()方法主要进行更新执行各种Commands:
function executeCommandsInViewport(firstViewport, scene, passState, backgroundColor) { // ... updateAndRenderPrimitives(scene); executeCommands(scene, passState); }
updateAndRenderPrimitives()进行更新:
this._primitives = new PrimitiveCollection(); function updateAndRenderPrimitives(scene) { // ... scene._groundPrimitives.update(frameState); scene._primitives.update(frameState); // ... }
PrimitiveCollection.prototype.update = function (frameState) { // ... for (let i = 0; i < primitives.length; ++i) { primitives[i].update(frameState); } };
进行纹理、外观和材质的更新:
Primitive.prototype.update = function (frameState) { // ... this._batchTable.update(frameState); // ... // Create or recreate render state and shader program if appearance/material changed commandFunc(this, appearance,material,translucent,twoPasses,this._colorCommands,this._pickCommands,frameState); };
BatchTable.prototype.update = function (frameState) { // ... updateTexture(this); };
function updateTexture(batchTable) { const dimensions = batchTable._textureDimensions; batchTable._texture.copyFrom({ source: { width: dimensions.x, height: dimensions.y, arrayBufferView: batchTable._batchValues, }, }); }
executeCommands()调度执行命令
function executeCommands(scene, passState) { // ... // Draw terrain classification executeCommand(commands[j], scene, context, passState); // Draw 3D Tiles executeCommand(commands[j], scene, context, passState) // Draw classifications. Modifies 3D Tiles color. executeCommand(commands[j], scene, context, passState); // ... }
function executeCommand(command, scene, context, passState, debugFramebuffer) { // ... if (command instanceof ClearCommand) { command.execute(context, passState); return; } // ... command.execute(context, passState); // ... }
比如ClearCommand,它会执行clear命令:
ClearCommand.prototype.execute = function (context, passState) { context.clear(this, passState); };
最终命令变成GL指令:
Context.prototype.clear = function (clearCommand, passState) { // ... const c = clearCommand.color; gl.clearColor(c.red, c.green, c.blue, c.alpha); // ... };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下