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);
// ...
};

4. 参考资料

  1. Cesium原理篇:1最长的一帧之渲染调度 - fu*k - 博客园 (cnblogs.com)
  2. CesiumJS 2022^ 源码解读1 使用 requestAnimationFrame 循环触发帧动画 - 四季留歌 - 博客园 (cnblogs.com)
  3. GitHub - CesiumGS/cesium: An open-source JavaScript library for world-class 3D globes and maps

Cesium渲染调度的更多相关文章

  1. Cesium原理篇:3D Tiles(1)渲染调度

    Cesium在2016年3月份左右推出3D Tiles数据规范,在glTF基础上提供了LOD能力,定位就是Web环境下海量三维模型数据.虽然目前3D Tiles还是Beta阶段,有不少硬伤,但3D T ...

  2. Cesium原理篇:3D Tiles(1)渲染调度【转】

    Cesium在2016年3月份左右推出3D Tiles数据规范,在glTF基础上提供了LOD能力,定位就是Web环境下海量三维模型数据.虽然目前3D Tiles还是Beta阶段,有不少硬伤,但3D T ...

  3. Cesium原理篇:1最长的一帧之渲染调度

    原计划开始着手地形系列,但发现如果想要从逻辑上彻底了解地形相关的细节,那还是需要了解Cesium的数据调度过程,这样才能更好的理解,因此,打算先整体介绍一下Cesium的渲染过程,然后在过渡到其中的两 ...

  4. 第一章 渲染调度来龙去脉——插入自己的shader

    总有人会问,这个或者那个功能怎么弄,或者看到别人做了什么酷炫的效果也想仿造.其实,功能的实现无非两种: 1.调用Cesium现有的API组合实现:往往照猫画虎,还存在性能不过关的问题,绕了半天其实终究 ...

  5. 1.2 Cesium渲染流程

    “从前有座山,山里有座庙,庙里有个......”我们喜欢这样讲故事,有头有尾巴.Cesium实时刷新,就是说每一帧都在更新,(但这也是一般状态下,如果场景完全静悄悄也可请求渲染模式,这时就不是每一帧都 ...

  6. Cesium原理篇:3D Tiles(3)个人总结

    个人结论:目前,在演示层面,3D Tiles问题不大,但项目应用上就不够成熟了,所以问问自己,你是想吃瓜呢还是想吃螃蟹? 好的方面 数据规范 我非常喜欢glTF的整体设计,概括有四点:第一,数据块(B ...

  7. Cesium教程系列汇总

    Cesium系列目录: 应用篇 入门 Cesium应用篇:1快速搭建 影像 Cesium应用篇:2影像服务(上) Cesium应用篇:2影像服务(下) 控件 Cesium应用篇:3控件(1)Clock ...

  8. Cesium原理篇:5最长的一帧之影像

    如果把地球比做一个人,地形就相当于这个人的骨骼,而影像就相当于这个人的外表了.之前的几个系列,我们全面的介绍了Cesium的地形内容,详见: Cesium原理篇:1最长的一帧之渲染调度 Cesium原 ...

  9. Cesium原理篇:3最长的一帧之地形(2:高度图)

           这一篇,接着上一篇,内容集中在高度图方式构建地球网格的细节方面.        此时,Globe对每一个切片(GlobeSurfaceTile)创建对应的TileTerrain类,用来维 ...

  10. Cesium原理篇:6 Renderer模块(1: Buffer)

    刚刚结束完地球切片的渲染调度后,打算介绍一下目前大家都很关注的3D Tiles方面的内容,但发现要讲3D Tiles,或者充分理解它,需要对DataSource,Primitive要有基础,而这要求对 ...

随机推荐

  1. 前端内容(HTML CSS javaScript)

    前端内容 前端基础之HTML 前端基础之HTML HTML标签使用 HTML之form表单 前端基础之CSS 前端基础之CSS CSS字体颜色 背景 盒子模型 CSS浮动 溢出 头像框 CSS定位 i ...

  2. Django框架三板斧本质-jsonResponse对象-form表单上传文件request对象方法-FBV与CBV区别

    目录 一:视图层 2.三板斧(HttpResponse对象) 4.HttpResponse() 5.render() 6.redirect() 7.也可以是一个完整的URL 二:三板斧本质 1.Dja ...

  3. PyTorch复现GoogleNet学习笔记

    PyTorch复现GoogleNet学习笔记 一篇简单的学习笔记,实现五类花分类,这里只介绍复现的一些细节 如果想了解更多有关网络的细节,请去看论文<Going Deeper with Conv ...

  4. 如何在 .Net 7 中将 Query 绑定到数组

    在 .Net 7 中,我们可以通过绑定数组的方式来接收来自查询字符串的参数.这样就不需要再使用逗号分隔的字符串来获取参数了. 代码演示 假设我们需要从 query 上接受多个 id 并返回查询的结果. ...

  5. vue中mixins(混入)的用法

    vue中mixin的使用详解 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能.一个混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被&quo ...

  6. 掌握webpack(一)一张图让你明白webpack中output的filename、path、publicPath与主流插件的关系

    webpack的核心概念,放到2022年相信很多的小伙伴都已经非常清楚了.但是,对于webpack配置中的output.path.output.filename以及output.publicPath, ...

  7. 在 C# 9 中使用 foreach 扩展

    在 C# 9 中,foreach 循环可以使用扩展方法.在本文中,我们将通过例子回顾 C# 9 中如何扩展 foreach 循环. 代码演示 下面是一个对树形结构进行深度优先遍历的示例代码: usin ...

  8. Vue双向绑定原理梳理

    简介 vue数据双向绑定主要是指:数据变化更新视图,视图变化更新数据. 实现方式:数据劫持 结合 发布者-订阅者 模式. 数据劫持通过 Object.defineProperty()方法. 对对象的劫 ...

  9. 通过Docker启动Solace,并在Spring Boot通过JMS整合Solace

    1 简介 Solace是一个强大的实时性的事件驱动消息队列.本文将介绍如何在Spring中使用,虽然代码使用的是Spring Boot,但并没有使用相关starter,跟Spring的整合一样,可通用 ...

  10. 时间日期相关总结-System类的常用方法

    时间日期相关总结 Date类 A.构造方法 Date();根据当前系统时间创建日期对象 Date(long time);根据传入的毫秒值时间创建日期对象 B.成员方法 long getTime(); ...