许多文章都说到GeometricErorr,跟官方说的神乎其神,总之是一个几何误差阈值,渲染窗口在渲染的循环里使用相机参数来计算该瓦片的几何误差,当计算的几何误差超过tileset的maximumScreenSpaceError(最大可接受的几何误差)则进入下一个精度瓦片REPLACE或ADD来补充几何误差,官方文档也描述了几何误差和像素的关联
不过重点是并没有给出几何误差的计算方式,平时我们认识的LOD都是通过相机到瓦片中心的距离或者瓦片包围盒占屏幕的像素来控制瓦片节点的加载卸载,这里我通过经验简单给新手一种计算方式
从Cesium源码可以看出,瓦片的error会跟着视角变化
var sseDenominator = camera.frustum.sseDenominator;
error = (geometricError * height) / (distance * sseDenominator);
当瓦片的error小于tileset的最大误差则可以渲染
function canTraverse(tileset, tile) {
if (tile.children.length === 0) {
return false;
}
if (tile.hasTilesetContent) {
// Traverse external tileset to visit its root tile
// Don't traverse if the subtree is expired because it will be destroyed
return !tile.contentExpired;
}
return tile._screenSpaceError > tileset._maximumScreenSpaceError;
}
那我们在生产tileset.json的时候怎么计算geometricError呢,使用模型包围盒的直径?
在调试过程发现
在模型很大的时候,直径很大,那tile一直在渲染
在不同精度的模型LOD时,包围盒直径是几乎一样大的,简单的除以2?
要是跟其他LOD一样相机到瓦片的距离小于多少则切换LOD就好了。
分析代码
error = (geometricError * height) / (distance * sseDenominator);
瓦片的渲染时error = geometricError * 渲染窗体高度 / (瓦片中心到相机距离 * sseD)
sseD视锥体的垂直角度系数,一般根据屏幕宽高比计算赋值,我们可以暴力固定为0.5
height是窗体高度,现在屏幕一般是1920 * 1080,高分是2560 * 1440,实际上chrome里不是全屏,我们可以先固定为1024,或者1000吧
error > _maximumScreenSpaceError 则渲染,否则进入下一层级
_maximumScreenSpaceError默认为16,是可传参数
最终
error = (geometricError * height) / (distance * sseDenominator);
当
(geometricError * height) / (distance * sseDenominator) < 16
则进入下一层级
geometricError可以与distance做方程
geometricError = 16 * distance * sseDenominator / height
= 16 * distance * 0.5 / 1024
≈ distance * 8 / 1000
≈ distance * 0.008
≈ distance / 125.0
即125.0是一个geometricError与相机到瓦片中心距离的系数
即我们如果使用574.4880608119186瓦片的包围直径,则在
distance = 574 * 125 = 71,750 的距离就显示该瓦片了(7万米高空看600米还是多了)
当两千米我们想进入下一个精度,则tileset的root的geometricError设置为
geometricError = 2000 / 125.0 = 16.0
children为叶子不分裂则geometricError = 0.0,
{
"asset" :
{
"gltfUpAxis" : "Z",
"tilesetVersion" : "1.0.1-OC23dtiles",
"version" : "1.0"
},
"geometricError" : 574.4880608119186,
"root" :
{
"boundingVolume" :
{
"box" :
[
-0.01602935791015625,
1.579261779785156,
445.0204315185547,
168.9166793823242,
0.0,
0.0,
0.0,
228.6613388061523,
0.0,
0.0,
0.0,
41.11302185058594
]
},
"children" :
[
{
"boundingVolume" :
{
"box" :
[
0.11706542968750,
0.1052780151367188,
446.6288299560547,
168.7835845947266,
0.0,
0.0,
0.0,
225.6792221069336,
0.0,
0.0,
0.0,
42.54774475097656
]
},
"content" :
{
"uri" : "1/0.b3dm"
},
"geometricError" : 0
}
],
"content" :
{
"uri" : "0.b3dm"
},
"geometricError" : 16.0,
"transform" :
[
-0.9457490369896568,
-0.3248980748375352,
0.0,
0.0,
0.1827117205659617,
-0.5318573643700093,
0.8268882458550326,
0.0,
-0.2686543991840867,
0.7820287622154638,
0.5623662764309280,
0.0,
-1715223.859419574,
4992862.189407891,
3566389.983635076,
1.0
]
}
}
大雁搭的3dtiles例子,做了瓦片合并,只有顶层和次顶层两个b3dm,包围盒大小相近
https://download.youkuaiyun.com/download/rpgpp55/19791152
这里只是给了一种简单粗暴的计算方式,在调试过程还是比较有用,当然我们是固定了maximumScreenSpaceError来计算的距离系数为125,maximumScreenSpaceError这个值本来就是传参,默认为16,大家使用瓦片的包围半径来作为几何误差,然后改这个也可以值也可以,瓦片越大则需要“忍受”的最大屏幕空间误差也得响应变大