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. Redis的常见应用场景

    缓存.分布式数据共享.setnx分布式锁.incrby全局id进行分库分表.计数器.限流(ip为key,计数超过阈值则返回false).购物车(hash,用户key-商品field-数量value). ...

  2. SpringBoot内置tomcat启动过程及原理

    作者:李岩科 1 背景 SpringBoot 是一个框架,一种全新的编程规范,他的产生简化了框架的使用,同时也提供了很多便捷的功能,比如内置 tomcat 就是其中一项,他让我们省去了搭建 tomca ...

  3. python 水仙花数、菱形、99乘法表、直角三角形

    空心菱形 i = 1 while i <= 3: # 控制行数 j = 1 k = 1 while j <= 3-i: # 控制空格数量 print(" ", end= ...

  4. RedisTemplate设置redis的key时出现\xac\xed\x00\x05t\x00\x0f前缀

    1.问题描述 使用redisTemplate设置redis的key-value,程序运行没有问题,但是却在redis客户端查不到设置的key-value. 2.产生原因 出现这种乱码前缀的原因是没有进 ...

  5. s2-007

    漏洞名称 S2-007 CVE-2012-0838 远程代码执行 利用条件 Struts 2.0.0 - Struts 2.2.3 漏洞原理 age来自于用户输入,传递一个非整数给id导致错误,str ...

  6. Java运算的精度和溢出问题

    double和float的0.1问题 代码如下 public class demo2 { public static void main(String[] args) { float f=0.1f; ...

  7. Docker部署python-Flask应用

    title: Docker部署python Flask应用 date: 2022-11-19 13:00:25 tags: - python 环境 系统:windows10 python:python ...

  8. 洛谷P1259 黑白棋子的移动 题解

    本蒟蒻这题用的打表做法,其实也可以理解为是一种递推. 先来观察一下样例: 当n为7时,输出共有14行,易得输出行数为2n. ooooooo*******-- oooooo--******o* oooo ...

  9. Ubuntu安装Anaconda并且配置国内镜像教程

    前言 我们在学习 Python 的时候需要不同的 Python 版本,关系到电脑环境变量配置换来换去很是麻烦,所以这个时候我们需要一个虚拟的 Python 环境变量,我之前也装过 virtualenv ...

  10. SQLSERVER 的 nolock 到底是怎样的无锁?

    一:背景 1. 讲故事 相信绝大部分用 SQLSERVER 作为底层存储的程序员都知道 nolock 关键词,即使当时不知道也会在踩过若干阻塞坑之后果断的加上 nolock,但这玩意有什么注意事项呢? ...