近期要深度研究Cesium。关于Cesium的用法、渲染流程等方面我看很多人都写过。我就写写其中一些可能平时用不到但是比较有趣的内容。因为边研究边写,所以会陆续出几集,然后合并在一起,欢迎大家跟踪。
我的这些文章不打算把一些基本概念展开解释,因此读者在必要时可能需要自己寻找源码进行匹配阅读。如有不对,欢迎指出。
1、多棱台表现
我们一般会认为,一个三维视图就只有一个裁剪体(ViewFrustum)。但是Cesium可以有多个,可以从View.updateFrustums函数名称看的出来。比如下面图中存在4个裁剪体,分别用不同的颜色表示。
多棱台表现会把视野的划分逻辑,有点类似如下图的效果,它把一个椎体切法为多个长度不一的棱台。由于每个棱台可以有自己的深度缓冲阈值,因此可以针对比较远的对象一个独立的棱台,这样其Z值冲突会少一些,效果更好一点。可以用在陡峭的山峰和山谷、非常长的管道。不过经初步测试,效果并不明显,不需要针对一般情况(如500M高建筑等)调整该设置。
2、 剖切
剖切是三维平台最基本的需求,但难度系数也比较大。在Cesium中,剖切的渲染排序是COMPUTE,是第二组渲染对象(PASS.COMPUTE),可见它的统筹性多强(意思是后面对象的渲染需要考虑它的存在)。
三维模型剖切的基本流程是:a) 根据模型包围盒判断是否被剖切干掉(在函数Cesium3DTile.visibility中),b)在模型片元着色器阶段,逐个像素判断是否被剖切干掉(在着色器ModelClippingPlanesStageFS中)。
在片元着色器的剖切方法中(getClippingFunction.Clip)。先反推像素对应的的世界坐标(代码czm_windowToEyeCoordinates(fragCoord)),然后在通过计算点与平面的关系(正面、反面,距离),得到这个片元是否被干掉。
剖切是一个平面,但是Cesium的剖切却构建了一个材质,这是为什么呢?这是因为Cesium把剖切平面存储在一个图片中(材质中),如第0号像素存储第1个剖切面的参数。
3、Cesium如何控制瓦片在内存的容量
Tileset有个参数,叫maximumScreenSpaceError,默认是16。如果瓦片的平面几何误差超过这个值,就会被加载。
如果屏幕范围有非常多的瓦片都达到了这个值,那么内存(含显存)一定会爆,这就好像每年都固定的高考录取分数线肯定会“旱的旱死、涝的涝死”。
因此Cesium对Tileset定义了一个并行于maximumScreenSpaceError的屏幕几何误差memoryAdjustedScreenSpaceError。这个参数会基于maximumScreenSpaceError上下浮动,当内存够,则变小,反正变大。从而实现瓦片内存的控制。
if (tileset.totalMemoryUsageInBytes < cacheBytes) {
decreaseScreenSpaceError(tileset);
} else if (memoryExceeded && tiles.length > 0) {
increaseScreenSpaceError(tileset);
}
4、Tileset的兄弟谁先加载?
我们经常会遇到远处的先加载、小的先加载等等“倒转天罡”的事情。这其中有一定的玄学,要分析的原因比较多。但有几个是可以留意的。
a)“太子的优先级大于其他皇子”。意思是排在Tileset.json前面的会比后面的占优势。
b)眼睛看到的比余光看到的有优势。详见参数“tile._foveatedFactor”,这个参数为0表示眼睛射线射穿了这个瓦片的包围球,其他正值表示眼睛射线到这个瓦片包围球的扭曲度。
c) 瓦片在屏幕正中心比屏幕周边有优势。会给屏幕中心一定范围的瓦片几何误差宽松值(sseRelaxation)。当然这个屏幕中心的角度是需要设置的,详见(foveatedConeSize )参数。