cesium 绘制primitive流程(三)执行渲染命令过程

cesium 绘制primitive流程(三)执行渲染命令过程,第1张

我们从下面图片去观察cesium是怎样执行primitive的渲染命令的

1,初始化cesium场景时候会默认执行CesiumWidget类里面的startRenderLoop方法,该方法的作用是

调用浏览器requestAnimationFrame方法循环执行widget.render方法进行渲染,我们再看看

CesiumWidget里面的rander方法,这个方法调用了scene.initializeFrame()进行初始化一帧,Clock.tick()方法触发时钟更新当前时间,Scene.render方法进行渲染。我们再看看Scene.render方法

该方法大致做了以下几件事,更新帧数,预先更新3dtiles数据,预先更新相机飞行的3dtiles数据,调用render方法等

我们看看Scene.render方法,该方法内部调用Scene.updateAndExecuteCommands方法

Scene.prototype.updateAndExecuteCommands方法根据frameState.mode变迹基困量判断是否是3d场景然后执行executeCommandsInViewport方法渲染视口内的图元,

调用Scene类里的executeCommands方法执行绘制命令

executeCommands方法内先执行和环境相关的,例如天空盒,太阳,月亮等渲染指令。

接着根据遍历视锥体数组,依次执行单个视锥体的渲染命令。

先Pass.GLOBE(椭球体),接着Pass.TERRAIN_CLASSIFICATION(贴地形),接着Pass.CESIUM_3D_TILE(3dtiles),Pass.CESIUM_3D_TILE_CLASSIFICATION(贴着3dtiles),接着Pass.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW,接着Pass.OPAQUE(不透明的),Pass.TRANSLUCENT(透明的),Pass.OVERLAY(遮盖物)

然后会触发DrawCommand.prototype.execute方法

进入Centext.draw方法

该方法先设置帧缓冲区,渲染状态,uniform变量,然后调用beginDraw方法

在beginDraw方法绑定帧缓冲区,应用渲染状态(例如开启深度测试,模板测试,混合等),绑定着色程序,并限制最大纹理单元数量。

然后执行continueDraw方法姿念

设置模型矩阵,锋握设置uniform变量,绑定顶点数组,根据绘制的实例数量和是否有顶点索引来决定使用context._gl.drawElements,context.glDrawElementsInstanced,context._gl.drawArrays,context.glDrawArraysInstanced其中一种方法

⚠️本文在cesium 1.52.0下测试

cesium目前支持的格式有下面的两种地形销猛格式,分别是:

在 heightmap 1.0 terrain format 说明页面上,官方明确指出这种格式已经废弃了,推荐使用冲斗散 quantized-mesh ,关于两种格式的对比,推荐阅读 这篇文章 。

在Cesium 1.52.0下测试, Cesium 还是支持 HeightMap 格式的,未来的Release版本有可能还会继续支持。

鉴于历史原因,项目之前一直使用 HeightMap ,最近才迁移成 Quantized-Mesh ,但构建离线的地形服务流程基本一致。

基散氏本的构建流程为:

如果没有Docker环境,首先安装docker环境,建议安装Docker for Mac / Windows,不推荐使用Docker Toolbox。

这里以构建 Quantized-Mesh 为例,首先获取image。

在一个新的容器中执行。

合并tiffs,生成.vrt文件。

创建一个terrain文件夹存储切割后的瓦片,运行ctb-tile生成瓦片,这个步骤可能时间较长,可以出去喝杯咖啡。

创建Cesium layer.json描述文件。

到此,cesium Quantized-Mesh 的地形瓦片就生成完毕。

最后在程序中使用:

注:使用Node编写server的时候,需要额外的header设置。

选取一个4*3的矩形区域作为数据源:

采用的流程都是先将上述12个tif合并成一个.vrt文件,然后使用ctb-tile来进行切割。从最终得到的数据集的大小来看,Quantized-Mesh更节省空间,运行时会更节省内存:

效果对比:

第一篇CesiumJS技术日记,不管技术难不难,认真归纳是个好习惯。本文绕弯太多,要直接绘制视锥的请移步 这里 。

最近在研究视域分析,思路:使用ShadowMap.js接口开放的阴影绘制功能,指定点光源的相关参数。然而在调试的过程中发现并没有那么简单,生成ShadowMap对象时会抛出无法在参数中找到Context对象的异常,这个Context对象在Api文档中并没有提及,这个问题在 https://github.com/AnalyticalGraphicsInc/cesium/issues/4010 中提及,Cozzi说不支持,索性视域分析就放一放,先将视锥绘制出来。这里只说关键的地方,primitive的加载、鼠标事件等者友裤过程就不多说了。

方案1:确定摄像机位置->确定摄像机方向->生成新相机实体->使用lookAt(target,offset)设置相机位置姿态->获取相机视锥->绘制视锥,该方案应该是标准方法,但是调试过程中绘制的视锥总是出现在地心处,多次尝试无果(和下漫画如出一辙):

切忌钻牛角尖,尝试方案2:

方案2:确定相机位置->确定摄像机方向->生成视锥几何体->计算视锥俯仰角度->计算视锥航向角度->绘制视锥。方案2相对于前者的难点在于相机姿态的计算,原本使用myCamera.lookAt(target,offset)可以直接计算出视锥的姿态,无奈该方法绘制出错,现在只能自己算了(笔者没有找到根据两点计算姿态的函数)。

视锥的形态和初始姿态(yaw->0,pitch->0,roll->0)如下图。

视锥构造参数如下:

frustum:视锥本体,下面详细说明,

origin:轴心,就是那个尖的位置,

orientation:决定相机看的方向,后面再详细说明,

vertexFormat:该参数和视锥绘制没有关系,保持默认即可。

我们选用PerspectiveFrustum,如下是官方doc的构造函数参数。

near和far参数作用如下图,其中虚线是辅助线,不属于绘制的视锥几何体。

fov:查看的视场角,绕z轴旋转,以弧度方式输入。

aspectRatio:垂直边和水平边的比值,如下图的aspectRatio为n。

far,near,fov,aspectRatio四个参数确定了便可以确定视锥的形状。

在程序启动之后的第一件事是通过鼠标选取两个点:起点和瞄准的点,通过这两个点来计算出视锥的姿态,如下图from点便是视锥的origin,这里我们只告慧关心航向(yaw/heading)和俯仰(pitch)两个参数,不关心横滚(roll),我们希望计算出来的航向角和俯仰角通过如下图的这种方式来表示相机瞄准的方位,下图中粉色线是视锥所表示相机所发射的射线。即航向角和俯仰角为0时射线指向x轴方向,航向角增加射线绕着z轴顺时针转动,俯仰角增加射线绕着y轴转动。

计算出的姿态换算成四元素(Quaternion),用作视锥的orientation参数。

不过视锥初始化状态发射的射线(表示首简的相机所发射)并不是指向x轴方向,而是指向z方向,且航向沿着x的反方向,将视锥航向角-180°,俯仰角-90°,便是视锥理想的初始姿态。

Cesium中的经纬度坐标系和笛卡尔坐标系该选谁呢?上文中提到的姿态所参考的xyz轴是以椭球地表为参考的(x指向正东,y指向正北,z垂直于地面向上),因此选择经纬度更为直观,但是只限于短距离的计算。

俯仰角计算 ,先计算绝对值:

再根据h的正负判断俯仰角的正负值(仰为正,俯为负)。

航向角计算 ,先计算第一象限的值:

现在判断To点的位置,结合之前视锥的初始姿态,计算出最终的航向弧度。

case 区域1:

case 区域2:

case 区域3:无需 *** 作

case 区域4:

当然,这种方法仅仅适用于短距离和靠近赤道的地区,对于笔者的用途足够了。

其实并不推荐这样的方法来计算视锥(不过好歹笔者调试了半天才试出来,也记录一下),后面将叙述推荐的方法。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/12547963.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-26
下一篇 2023-05-26

发表评论

登录后才能评论

评论列表(0条)

保存