Cesium 高性能扩展之DrawCommand(三):显隐和点
欢迎关注微信公号【三维网格3D】,第一时间获取最新文章
DrawCommand 是 Cesium 渲染器的核心类,常用的接口 Entity、Primitive、Cesium3DTileSet,以及地形和影像的渲染等等,底层都是一个个 DrawCommand 完成的。在进行扩展开发、视觉特效提升、性能优化、渲染到纹理(RTT),甚至基于 Cesium 封装自己的开发框架,定义独家数据格式等等,都需要开发人员对 DrawCommand 熟练掌握。而这部分接口,Cesium 官方文档没有公开,网上的相关资料也比较少,学习起来比较困难,所以接下来我们用几期文章,由浅入深,实用为主,力求全面地介绍 DrawCommand 及相关类的运用。
第一次被关注我们的小伙伴催更,还是挺开心的😂,公司事务略忙,只有深夜码字来赶稿了~那么本篇我们就按照计划往下写,介绍自定义Primitive的显隐控制,以及如何支持scene.pick,实现要素拾取查询。
![](https://img.haomeiwen.com/i24256791/ff671d8c5cc529ca.png)
1、显隐控制
从前面两篇的代码看,显隐控制是非常简单的,我们只需要在update方法的开头加入显隐判断,如果隐藏则直接退出,否则继续。
![](https://img.haomeiwen.com/i24256791/33c6c2bddab9f9a0.png)
考虑一下,如果我们将多个要素的几何体合并成一个几何体,以达到减少绘图批次(DrawCommand的数量)提升性能的目的,这种情况下,上述方法将会同时显示或者隐藏这些要素,那么该如何控制其中任一要素的显隐呢?
参考Cesium的其他Primitive类,会发现他们为每一个顶点创建了一个batchId或者a_batchId属性,这个属性非常重要,除了可以用于控制显隐外,在3D Tiles样式引擎、要素拾取查询(pick)等都起到关键作用。
这里简单介绍基于batchId的显隐控制实现思路:
为每一个要素创建batchId,从0开始编号;
为要素几何体创建batchId顶点属性,并将batchId值逐个写入顶点属性数组;
合并所有要素几何体;
创建一个二维数组类型的纹理贴图,宽度等于要素数量,高度为1,用0和1表示要素隐藏或者显示,写入纹理数据;
在shader中根据batchId并计算uv,从显隐纹理获取要素的显隐标记,如果小于1则discard;
当某个要素显隐发生变化时,更新显隐纹理。
目前我们先不要想那么复杂,等掌握了更多技能,需要设计这么复杂的自定义Primitve的时候,自然能理解并参考Cesium的代码来实现这一显隐控制方法。
2、支持pick实现点选
要素拾取查询(Pick),是3D交互的重要基础,想象一下一个3D应用,所有图形要素怎么点击都没有反应,那它跟一个视频播放器就没有什么两样了~
Cesium提供了统一的要素拾取查询机制,拾取API封装在Cesium.Scene中,前面我们介绍3D描边等后期处理时就曾使用过,非常简单。
![](https://img.haomeiwen.com/i24256791/c237148c5bb0af1b.png)
那么如果让我们的自定义Primitive能够被scene.pick点击拾取到呢?
2.1、context创建pickId
![](https://img.haomeiwen.com/i24256791/fa3e93512b68b31b.png)
2.2、shader声明pickId变量
这里可以pickId变量类型有两种:uniform和varying。
uinform:如果DrawCommand绘制的是单一要素,所绘制的内容就是一个整体,则使用uniform,所有顶点的pickId颜色是一样的,通过uniformMap来传递;
varying:如果绘制的是多个要素,绘制内容可能分为多个部件,只是合并为一个批次来渲染,可能每个部件需要能够被单独点击拾取,则使用varying,需要将每一个部件要素的pickId颜色写入对应的几何顶点属性,顶点着色器接收颜色值,并通过varying传递到偏远着色器。
2.2.1、uniform类pickId
![](https://img.haomeiwen.com/i24256791/0218bb538d87ea86.png)
2.2.2、 varying类pickId
![](https://img.haomeiwen.com/i24256791/e7f846afb922f1b8.png)
2.2.3、Cesium对DrawCommand的后续加工
大家可能很好奇,为什么pickId变量,只是声明,却没有见到具体的使用,怎么能实现点击拾取呢?
这个问题,大家不妨在绘制出结果后(延迟一段时间)打印出DrawCommand对象看看:
![](https://img.haomeiwen.com/i24256791/b933774c08eba6af.png)
我们接着展开看看pickCommand._shaderProgram._vertexShaderText:
![](https://img.haomeiwen.com/i24256791/00518bcc43a61203.png)
Cesium为pick操作创建了一个新的DrawCommand副本pickCommand,并且基于我们的shader进行加工处理,在新的main函数使用了我们声明的pickId变量。
同时大家可以看到,不止多了一个pickCommand,还增加了好几个其他DrawCommand副本,说明我们创建好的DrawCommand并没有直接进入渲染阶段,而是要经过Cesium渲染器进行加工处理,才最终交付执行渲染。
想一下,如果我们开启了阴影那么会不会又增加一个副本呢?这个问题我们留到下一篇来探讨。
2.3、传递pickId颜色
与2.2节相对应,如果pickId变量为uniform类,则通过unformMap传递其颜色,如果为varying则需要修改几何体,增加pickId颜色相关的顶点属性。
2.3.1、uniformMap传递
![](https://img.haomeiwen.com/i24256791/b6b5733694f18c50.png)
2.3.2、顶点属性传递
这种情况下,2.1创建的pickId就不够用了,我们需要为每一个要素的几何体创建一个pickId。我们定一个方法,来处理每一个要素的几何体。
![](https://img.haomeiwen.com/i24256791/a9a24b6764def470.png)
2.4、drawCommand指定pickId变量
![](https://img.haomeiwen.com/i24256791/75265830ef7ae928.png)
2.5、释放pickId
需要注意,当我们不需要pickId或者drawCommand释放或者整个Primitive释放时,一定要释放pickId,否则将会出现内存泄漏,因为我们会将primitive对象绑定到pickId,这个pickId不释放的话,会一直保存在context的缓存里,直到整个程序消亡。
下面是完整的drawCommandd释放方法,包含了pickId的释放。
![](https://img.haomeiwen.com/i24256791/c66c251f078426f6.png)
3、点选示例及效果视频
![](https://img.haomeiwen.com/i24256791/9e2b883d557f3ee9.png)
uniform
有没有发现,当我们加入pickId之后,不但点击拾取查询功能实现了,包括后期处理高亮也自然支持了,这是因为Cesium后期处理的要素选择也是基于pickId实现的。
本篇到此结束,如果觉得有用,不妨点赞+分享,让更多小伙伴一起来交流学习吧!
下一篇我们将介绍阴影(shadows)和实例化(instancing),敬请期待!
欢迎关注微信公号【三维网格3D】,第一时间获取最新文章