CesiumJS 2022^ 原理[2] 渲染架构之三维物体 - 创建并执行指令

Python微信订餐小程序课程视频

https://blog.csdn.net/m0_56069948/article/details/122285951

Python实战量化交易理财系统

https://blog.csdn.net/m0_56069948/article/details/122285941
目录* 回顾
+ 预备知识:指令
+ 预备知识:通道

  • 1. 生成并执行指令
    • 1.1. Primitive 生成指令
    • 1.2. Context 对象负责执行 WebGL 底层代码
  • 2. 多段视锥体技术
  • 3. 指令对象的转移
    • 筛选可见集
  • 4. 本篇总结

回顾

书接上文,Scene.js 模块内的 render 函数会将控制权交给 WebGL,执行 CesiumJS 自己封装的指令对象,画出每一帧来。

模块内的 render 函数首先会更新一批状态信息,譬如帧状态、雾效、Uniform 值、通道状态、三维场景中的环境信息等,然后就开始更新并执行指令,调用的是 Scene 原型链上的 updateAndExecuteCommands 方法。

整个过程大致的调用链是这样的(function 关键字简写为 fn):

[Module Scene.js]
- fn render()- Scene.prototype.updateAndExecuteCommands()- fn executeCommandsInViewport()- fn updateAndRenderPrimitives()[Module Primitive.js]- fn createCommands()- fn updateAndQueueCommands()- fn executeCommands()- fn executeCommand()

本篇讲解的是从 Scene 原型链上的 updateAndExcuteCommands() 方法开始,期间 Scene 中的 Primitives 是如何创建指令,又最终如何被 WebGL 执行的。

这个过程涉及非常多细节代码,但是为了快速聚焦整个过程,本篇先介绍两个 CesiumJS 封装的概念:指令和通道。

预备知识:指令

WebGL 是一种依赖于“全局状态”的绘图 API,面向对象特征比较弱,为了修改全局状态上的顶点数据、着色器程序、帧缓冲、纹理等“资源”,必须通过 gl.XXX 函数调用触发全局状态的改变。

而图形编程基础提出的渲染管线、通道等概念偏向于面向对象,显然 WebGL 这种偏过程的风格需要被 JavaScript 运行时引擎封装。

CesiumJS 将 WebGL 的绘制过程,也就是行为,封装成了“指令”,不同的指令对象有不同的用途。指令对象保存的行为,具体就是指由 Primitive 对象(不一定全是 Primitive)生成的 WebGL 所需的数据资源(缓冲、纹理、唯一值等),以及着色器对象。数据资源和着色器对象仍然是 CesiumJS 封装的对象,而不是 WebGL 原生的对象,这是为了更好地与 CesiumJS 各种对象结合去绘图。

CesiumJS 有三类指令:

  • DrawCommand 绘图指令
  • ClearCommand 清屏指令
  • ComputeCommand 计算指令

绘图指令最终会把控制权交给 Context 对象,根据自身的着色器对象,绘制自己身上的数据资源。

清屏指令比较简单,目的就是调用 WebGL 的清屏函数,清空各类缓冲区并填充清空后的颜色值,依旧会把控制权传递给 Context 对象。

计算指令则借助 WebGL 并行计算的特点,将指令对象上的数据在着色器中编码、计算、解码,最后写入到输出缓冲(通常是帧缓冲的纹理上),同样控制权会给到 Context 对象。

预备知识:通道

一帧是由多个通道顺序绘制构成的,在 CesiumJS 中,通道英文单词是 Pass

既然通道的绘制是有顺序的,其顺序保存在 Renderer/Pass.js 模块导出的冻结对象中,目前(1.92版本)有 10 个优先顺序等级:

const Pass = {ENVIRONMENT: 0,COMPUTE: 1,GLOBE: 2,TERRAIN\_CLASSIFICATION: 3,CESIUM\_3D\_TILE: 4,CESIUM\_3D\_TILE\_CLASSIFICATION: 5,CESIUM\_3D\_TILE\_CLASSIFICATION\_IGNORE\_SHOW: 6,OPAQUE: 7,TRANSLUCENT: 8,OVERLAY: 9,NUMBER\_OF\_PASSES: 10,
}

以上为例,第一优先被绘制的指令,是环境(ENVIRONMENT: 0)方面的对象、物体。而不透明(OPAQUE: 7)的三维对象的绘制指令,则要先于透明(TRANSLUCENT: 8)物体被执行。

CesiumJS 会在每一帧即将开始绘制前,对所有已经收集好的指令根据通道进行排序,实现顺序绘制,下文会细谈。

1. 生成并执行指令

原型链上的 updateAndExecuteCommands 方法会做模式判断,我们一般使用的是三维模式(SceneMode.SCENE3D),所以只需要看 else if 分支即可,也就是

executeCommandsInViewport(true, this, passState, backgroundColor);

此处,this 就是 Scene 自己。

executeCommandsInViewport() 是一个 Scene.js 模块内的函数,这个函数比较短,对于当前我们关心的东西,只需要看它调用的 updateAndRenderPrimitives() 和最后的 executeCommands() 函数调用即可。

1.1. Primitive 生成指令

[Module Scene.js]
- fn updateAndRenderPrimitives()[Module Primitive.js]- fn createCommands()- fn updateAndQueueCommands()

Scene.js 模块内的函数 updateAndRenderPrimitives() 负责更新 Scene 上所有的 Primitive。

期间,控制权会通过 PrimitiveCollection 转移到 Primitive 类(或者有类似结构的类,譬如 Cesium3DTileset 等)上,令其更新本身的数据资源,根据情况创建新的着色器,并随之创建 绘图指令,最终在 Primitive.js 模块内的 updateAndQueueCommands() 函数排序、并推入帧状态对象的指令列表上。

1.2. Context 对象负责执行 WebGL 底层代码

[Module Scene.js]
- fn executeCommands()
- fn executeCommand() // 收到 Command 和 Context[Module Context.js]- Context.prototype.draw()

另一个模块内的函数 executeCommands() 则负责执行这些指令(中间还有一些小插曲,下文再提)。

此时,上文的“通道”再次起作用,此函数内会根据 Pass 的优先顺序依次更新唯一值状态(UniformState),然后下发给 executeCommand() 函数(注意少了个s)以具体的某个指令对象以及 Context 对象。

除了 executeCommand() 函数外,Scene.js 模块内仍还有其它类似的函数,例如 executeIdCommand() 负责执行绘制 ID 信息纹理的指令,executeTranslucentCommandsBackToFront() / executeTranslucentCommandsFrontToBack() 函数负责透明物体的指令等。甚至在 Scene 对象自己的属性中,就有清屏指令字段,会在 executeCommands() 函数中直接执行,不经过上述几个执行具体指令的函数。

Context 对象是对 WebGL(2)RenderingContext 等 WebGL 原生接口、参数的封装,所有指令对象最终都会由其进行拆包装、组装 WebGL 函数调用并执行绘图、计算、清屏(见上文介绍的预备知识:指令)。

2. 多段视锥体技术

先介绍一个概念,WebGL 中的深度。

简单的说,屏幕朝里,三维物体的前后顺序就是深度。CesiumJS 的深度非常大,即使不考虑远太空,只考虑地球表面附近的范围,WebGL 的数值范围也不太够用。聪明的前辈们想到了使用对数函数压缩深度的值域,因为一个非常大的数字只需取其对数,很快就能小下来。

Scene 对象上有一个可读可写访问器 logarithmicDepthBuffer,它指示是否启用对数深度。

现在,CesiumJS 通常使用的就是对数深度。

对数深度解决的不仅仅只是普通深度值值域不够的问题,还解决了不支持对数深度技术之前使用的多段视锥技术问题。

再次简单的说,多段视锥技术将视锥体由远及近切成多个段,保证了相机近段的指令足够多以保证效果,远段尽量少满足性能。

你在 Scene.js 模块中的 executeCommands() 函数的最后能找到一个循环体:

// Execute commands in each frustum in back to front order
let j;
for (let i = 0; i < numFrustums; ++i) {// ...
}

打开调试工具,很容易击中这个断点,查看 numFrustums 变量的值,有启用对数深度的 CesiumJS 程序,一般 numFrustums 都是 1。

3. 指令对象的转移

在本文第 1 节中,详细说明了指令对象的生成与被执行。

上述其实忽略了很多细节,现在捡起来说。

指令对象在 Primitive(或类似的类)生成后,由 模块内的 updateAndQueueCommands() 函数排序并推入帧状态对象的指令列表上:

// updateAndQueueCommands 函数中,函数接收来自 Scene 逐级传入的帧状态对象 -- frameState
const commandList = frameState.commandList;
const passes = frameState.passes;
if (passes.render || passes.pick) {// ... 省略部分代码commandList.push(colorCommand);
}

frameState.commandList 就是个普通的数组。

而在执行时,却是从 View 对象上的 frustumCommandsList 上取的指令:

// Scene.js 模块的 executeCommands 函数中const frustumCommandsList = view.frustumCommandsList;
const numFrustums = frustumCommandsList.length;let j;
for (let i = 0; i < numFrustums; ++i) {const index = numFrustums - i - 1;const frustumCommands = frustumCommandsList[index];// ...// 截取不透明物体的通道指令执行代码片段us.updatePass(Pass.OPAQUE);commands = frustumCommands.commands[Pass.OPAQUE];length = frustumCommands.indices[Pass.OPAQUE];for (j = 0; j < length; ++j) {executeCommand(commands[j], scene, context, passState);}// ...
}

其中,下发给 executeCommand() 函数的 commands[j] 参数,就是具体的某个指令对象。

所以这两个过程之间,是怎么做指令对象传递的?

答案就在 View 原型链上的 createPotentiallyVisibleSet 方法中。

筛选可见集

View 对象是 Scene、Camera 之间的纽带,负责“眼睛”与“世界”之间信息的处理,即视图。

View 原型链上的 createPotentiallyVisibleSet 方法的作用,就是把 Scene 上的计算指令、覆盖物指令,帧状态上的指令列表,根据 View 的可见范围做一次筛选,减少要执行指令个数提升性能。

具体来说,就是把分散在各处的指令,筛选后绑至 View 对象的 frustumCommandsList 列表中,借助 View.js 模块内的 insertIntoBin() 函数完成:

// View.js 模块内function insertIntoBin(view, scene, command, commandNear, commandFar) {// ...const frustumCommandsList = view.frustumCommandsList;const length = frustumCommandsList.length;for (let i = 0; i < length; ++i) {// ...frustumCommands.commands[pass][index] = command;// ...}// ...
}   

这个函数内做了对指令的筛选判断。

4. 本篇总结

本篇调查清楚了 Scene 对象上各种三维物体是如何绘制的,即借助 指令 对象保存待绘制的信息,最后交由 Context 对象完成 WebGL 代码的执行。

期间,发生了指令的分类和可见集的筛选;篇幅原因,CesiumJS 在这漫长的渲染过程中还做了很多细节的事情。

不过,Cesium 的三维物体的渲染架构就算讲完了。

接下来,则是另两个比较头痛的话题:

  • 地球的渲染架构(瓦片四叉树)
  • 具备创建指令的各路数据源(Entity、DataSource、Model、Cesium3DTileset等)

指令和通道的概念仍然会继续使用,敬请期待。

  • 回顾

  • 预备知识:指令

  • 预备知识:通道

  • 1. 生成并执行指令

  • 1.1. Primitive 生成指令

  • 1.2. Context 对象负责执行 WebGL 底层代码

  • 2. 多段视锥体技术

  • 3. 指令对象的转移

  • 筛选可见集

  • 4. 本篇总结

    __EOF__

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-16LUkhXl-1649652132633)(https://blog.csdn.net/onsummer)]四季留歌 - 本文链接: https://blog.csdn.net/onsummer/p/16129435.html

  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角**【[推荐](javascript:void(0)😉】**一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401120.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Selenium2Library+ride学习笔记

一、环境部署 1.安装python2.7编译环境、ride环境以及Selenium2Library环境&#xff0c;环境部署可参见前面几节。 2.启动RIDE编译环境&#xff0c;导入Selenium2Library库。     3. 执行F5,可查看Selenium2Library自带的关键字(Keyword)。 二、常用关鍵字解释 1. open b…

Android——线程通讯 Handler、Looper、Message;

线程通讯问题 &#xff08;主要用到了Handler类&#xff0c;Looper类和Message类以及MessageQueue&#xff09; 在Android中主线程如何向子线程中发送消息的问题。让我们来想想&#xff0c;这其中的过程&#xff0c;无非就是创建一个Handler对象&#xff0c;然后一个线程发消息…

关于flash网页播放中wmode和direct

网页中播放带有stage3d内容的时候&#xff0c;必须加上 <param name"wmode" value"direct" /> 不然就不能正常显示&#xff0c;比如下面这段代码&#xff1a; <object width"400" height"300" data"http://files.cnblo…

Abp 实现通过手机号注册用户

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 前言 Abp 的 Identity 模块&#xff0c;实现了用户的管理&#xff0c;但是对于国…

android 开发不能创建目录

原来代码&#xff1a; File tempDir new File(path); //path 是一个参数if (!tempDir.exists()) {try {tempDir.mkdir(); //mkdirs 可创建多级目录&#xff0c;mkdir只能创建以及目录} catch (Exception ex) {Log.e("mkdir", "创建文件目录失败" ex.toS…

NumericUpDown使用备注

NumericUpDown使用 常用属性 DecimalPlaces 指示要显示的小数位数 Increment 每单击一下按钮时增减的数量 Maximum 最大值 Minimum 最小值转载于:https://www.cnblogs.com/blackice/archive/2012/11/12/2766417.html

关于Electron框架应用的安全测试

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 Electron框架应用的安全测试 0.Electron相关简介 electron.js是一个运行时框架&…

Exchange 2010 创建设备室邮箱

其实创建设备邮箱和创建会议室邮箱都差不多&#xff0c;只是在新建邮箱的时候&#xff0c;邮箱的类型选择的不一样&#xff1b;1、打开Exchange管理控制台&#xff0c;展开“收件人配置”选择“邮箱”节点&#xff1b;在中间空白的地方右击选择“新建邮箱”&#xff1b;或者在操…

AC日记——3的幂的和 51nod 1013

3的幂的和 思路&#xff1b; 矩阵快速幂&#xff1b; sn-1 3 1 sn * 1  0 1    1 来&#xff0c;上代码&#xff1a; #include <cstdio> #include <cstring> #include <iostream> #include <algorithm>using namespace…

简单的Excel导出(两种方式)

最近项目里面有个周报Excel导出的功能&#xff0c;为了解决这个问题&#xff0c;我显示调研Excel内核的方式实现了&#xff0c;但是被告知该方法有诸多弊端&#xff08;1、服务器需要装相应版本的Excel&#xff1b;2、如果程序中途出错服务器会有很多Excel进程&#xff09;&…

一款开源的文件搜索神器,终于不用记 find 命令了

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 这是 HelloGitHub 推出的《讲解开源项目》系列&#xff0c;用一篇文章带你快速上…

C2审核模式(c2 audit mode)

C2审核模式&#xff08;c2 audit mode&#xff09;SQL Server C2 Audit 是为了满足美国国防部针对计算机的安全访问的安全评级要求而引入的。 SQL C2Audit 可以记录shutdown,restart,成功和失败的Login,成功或者失败访问数据库对象&#xff0c;所欲数据定义的执行&#xff0c;数…

project开发的程序设计与逻辑设计

非常多时候我们要做庞大project, 就像一棵大树, 方方面面都有自己的细枝末节&#xff0c;而作为开发员的我们&#xff0c;无法时时刻刻去保持对程序的全面认知&#xff0c;所以我们要把程序设计与逻辑设计区分开来。 那么什么是程序设计和逻辑设计&#xff0c;举个样例来说&…

Java中截取文件名不要后缀

例如&#xff1a;File f new File("d:/d/abc.txt");f.getName()获得的是abc.txt,如果不需要后缀.txt&#xff0c;只要abc可以这样做&#xff1a; String test f.getName().substring(0,f.getName().lastIndexOf("."));转载于:https://www.cnblogs.com/lu…

开发者必读:2022年移动应用趋势洞察白皮书

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 华为开发者联盟与艾瑞咨询联合发布《2022年移动应用趋势洞察白皮书》&#xff0c;本…

JS的注意点

JS的跨域访问问题。 http://www.cnblogs.com/rush/archive/2012/05/15/2502264.html JS能操作的范围:HTTP协议的内容。但是不能直接访问response。 JS不能操作哪些内容。比如HTTP的一些内容。但是能操作cookie。 JS可以直接发送Web Service的SOAP请求。 CAUTION: 不仅仅是JS, A…

NYOJ 1009 So Easy[Ⅰ]【简单题】

/* 题目大意&#xff1a;求三角形的外接圆 解题思路&#xff1a;c/sin(C)2R,先求出cos&#xff0c;在求出sin 关键点&#xff1a;直接调用库 解题人&#xff1a;lingnichong 解题时间&#xff1a;2014-10-18 10:19:33 解题体会&#xff1a;简单题 */ So Easy[Ⅰ] 时间限制&…

Java 阶段面试 知识点合集 - 我们到底能走多远系列(15)

我们到底能走多远系列&#xff08;15&#xff09; 扯淡&#xff1a;这些知识点来源是通过面试涉及到的&#xff0c;面的公司不多&#xff0c;知识点涉及也不多&#xff0c;我每次面试后都在备忘录里写下有用的东西&#xff0c;集合起来分享一下&#xff0c;因为是知识点&#x…

对比学习 ——simsiam 代码解析。

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 ​ 目录 1 &#xff1a; 事先准备 。 2 &#xff1a; 代码阅读。 2.1: 数据读取…

wiki常用语法

为什么80%的码农都做不了架构师&#xff1f;>>> 说明 输入 效果 作用在任何地方 斜体字 斜体字 斜体字 粗体字 粗体字 粗体字 粗体加斜体 粗体加斜体 粗体加斜体 下划线 &#xff08;推荐替代斜体&#xff09; <u>下划线</…