web

Cesium 高性能扩展之DrawCommand(三):显隐和点

2022-01-19  本文已影响0人  三维网格

欢迎关注微信公号【三维网格3D】,第一时间获取最新文章

DrawCommand 是 Cesium 渲染器的核心类,常用的接口 Entity、Primitive、Cesium3DTileSet,以及地形和影像的渲染等等,底层都是一个个 DrawCommand 完成的。在进行扩展开发、视觉特效提升、性能优化、渲染到纹理(RTT),甚至基于 Cesium 封装自己的开发框架,定义独家数据格式等等,都需要开发人员对 DrawCommand 熟练掌握。而这部分接口,Cesium 官方文档没有公开,网上的相关资料也比较少,学习起来比较困难,所以接下来我们用几期文章,由浅入深,实用为主,力求全面地介绍 DrawCommand 及相关类的运用。

第一次被关注我们的小伙伴催更,还是挺开心的😂,公司事务略忙,只有深夜码字来赶稿了~那么本篇我们就按照计划往下写,介绍自定义Primitive的显隐控制,以及如何支持scene.pick,实现要素拾取查询。

1、显隐控制

从前面两篇的代码看,显隐控制是非常简单的,我们只需要在update方法的开头加入显隐判断,如果隐藏则直接退出,否则继续。

考虑一下,如果我们将多个要素的几何体合并成一个几何体,以达到减少绘图批次(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描边等后期处理时就曾使用过,非常简单。

那么如果让我们的自定义Primitive能够被scene.pick点击拾取到呢?

2.1、context创建pickId

2.2、shader声明pickId变量

这里可以pickId变量类型有两种:uniform和varying。

uinform:如果DrawCommand绘制的是单一要素,所绘制的内容就是一个整体,则使用uniform,所有顶点的pickId颜色是一样的,通过uniformMap来传递;

varying:如果绘制的是多个要素,绘制内容可能分为多个部件,只是合并为一个批次来渲染,可能每个部件需要能够被单独点击拾取,则使用varying,需要将每一个部件要素的pickId颜色写入对应的几何顶点属性,顶点着色器接收颜色值,并通过varying传递到偏远着色器。

2.2.1、uniform类pickId

2.2.2、 varying类pickId

2.2.3、Cesium对DrawCommand的后续加工

大家可能很好奇,为什么pickId变量,只是声明,却没有见到具体的使用,怎么能实现点击拾取呢?

这个问题,大家不妨在绘制出结果后(延迟一段时间)打印出DrawCommand对象看看:

我们接着展开看看pickCommand._shaderProgram._vertexShaderText:

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传递

2.3.2、顶点属性传递

这种情况下,2.1创建的pickId就不够用了,我们需要为每一个要素的几何体创建一个pickId。我们定一个方法,来处理每一个要素的几何体。

2.4、drawCommand指定pickId变量

2.5、释放pickId

需要注意,当我们不需要pickId或者drawCommand释放或者整个Primitive释放时,一定要释放pickId,否则将会出现内存泄漏,因为我们会将primitive对象绑定到pickId,这个pickId不释放的话,会一直保存在context的缓存里,直到整个程序消亡。

下面是完整的drawCommandd释放方法,包含了pickId的释放。

3、点选示例及效果视频


uniform

有没有发现,当我们加入pickId之后,不但点击拾取查询功能实现了,包括后期处理高亮也自然支持了,这是因为Cesium后期处理的要素选择也是基于pickId实现的。

本篇到此结束,如果觉得有用,不妨点赞+分享,让更多小伙伴一起来交流学习吧!

下一篇我们将介绍阴影(shadows)和实例化(instancing),敬请期待!

欢迎关注微信公号【三维网格3D】,第一时间获取最新文章

上一篇 下一篇

猜你喜欢

热点阅读