前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >3d Tiles 加载调度原理分析

3d Tiles 加载调度原理分析

作者头像
Jean
发布于 2021-07-16 09:05:04
发布于 2021-07-16 09:05:04
1.9K00
代码可运行
举报
文章被收录于专栏:Web行业观察Web行业观察
运行总次数:0
代码可运行

作者:迷途的小书童 微信公众号:g0415shenweri 转载声明

3dtiles协议具备了超大规模的数据加载调度的能力。本人分析了cesium的源码,结合自己的理解总结了一下加载调度的实现。

3dtiles的数据结构

3dtiles是金字塔状的层次结构,最上层是不太精细的数据,越到下层模型数据越精细,渲染成本越高。一般根据视口离tile的远近来加载不同的层级。

上面的层级关系可以用父子关系描述:

  • roottile:parent:null ,children:tile1,tile2
  • tile1:parent:rootile , children:tile3,tile4
  • tile2:parent:rootile, children:tile5,tile6
  • tile3:parent:tile1, children:null
  • tile4:parent:tile1, children:null
  • tile5:parent:tile2, childrend:null

3dtiles的状态

每个tile用两种不同主线的状态来描述,一种用来描述tile的数据是否已经通过http加载到内容,一种是当前tile是否需要渲染出来。整个渲染流程都是围绕着这两种状态进行业务逻辑的编写。

数据加载状态,我们通过下面几种状态来覆盖全部:只有当LoadState为ContentLoaded的时候,tile才能够被渲染出来。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
LoadState 
{
    Destroying = -3,
    Failed = -2,
    FailedTemporarily = -1,
    Unloaded = 0,
    ContentLoading = 1,
    ContentLoaded = 2,
    Done = 3
  };
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

渲染状态,描述当前tile是否可以渲染出来,我们通过下面几种状态来描述:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
None = 0,
Rendered = 1,
Refined = 2
Unrefined = 3
代码语言:javascript
代码运行次数:0
运行
复制

备注:渲染状态不会管当前tile是否内容已经加载,他只是描述当前tile在当前视口范围是否需要渲染。

状态解释:

None:表示tile刚刚创建

Rendered:表示当前正在被渲染的tile

Regined:表示当前的tile没有被渲染,但是其child有被渲染

Unrefined:表示当前tile没有被渲染,其child也没有被渲染,其parent有被渲染

这里解释下英文:Regined表示精细的,即其有更精细的模型被渲染,我们用来表示当前tile的child有被渲染。UnRegined表示非精细的,即其不精细的模型被渲染,我们用来表示当前tile的parent被渲染出来。

上面4种状态,可以描述任何一个tile当前的状态情况。

3dtiles的调度

整个3dtiles的调度,其实就是不停的去计算当前视口哪些tile可以被渲染的过程。我简化了部分不重要的逻辑,切换到重点,画了下面的流程图:

每次都是从roottile开始计算,调用_visitTile函数,该函数在渲染调度方面,主要干了3个事情,是否是叶子节点、达到sse,达不到sse。

如果是叶子节点就直接将当前tile的状态设置为Rendered,并将其tile丢进渲染的队列。

如果有子节点,就判断sse的值,sse我这里不做过多解释,有点复杂。这个参数用来判断是渲染当前tile,还是子节点的tile。

如果sse的值需要我们渲染当前tile,我们就需要所有的子child里面正在被渲染的tile的状态设置为Unrefined状态,并push到不再渲染的tile队列里面。这里是通过一个嵌套循环来实现。不过我们不需要遍历所有的子child,我们只需要遍历当前tile的状态为Regined的,Regined的状态说明可以看上一节的描述。

如果sse的值需要我们渲染子child,我们会遍历所有的子child,然后即上面的函数vistiVisibleChildrenNearToFar,这个函数内部又会重新调用_visitTile。

通过上面的逻辑,我们可以清楚的得到两个队列:renderlist和norenderlist。

renderlist表示当前需要渲染的tile队列

norenderlist表示原先是渲染状态,现在不需要在状态的队列。

得到这两个队列之后,我们就可以调用渲染的函数进行数据的渲染。不过这里需要渲染的队列tile,有可能有的数据内容还没有请求到。我们需要在渲染的时候进行过滤。

3dtiles的内存回收

我们不可能把所有的tile的数据都存储在内存里面,机器的内存是有限制的,我们也不能这么做。所以我们要把在没有在渲染状态的数据回收掉。cesium的做法是设置一个内存大小阀值来实现的。当超过这个阀值才会进行内存回收的逻辑。我这里想通过超时时间来实现。我们维护一个双向链表的回收队列。为什么是双向链表呢?这是为了可以非常快的删除任意一个节点。

我们开启一个定时器,定期的去计算哪些tile已经超过一个固定值没有被渲染了。那么我们将其从回收队列里面剔除,并将数据内容释放掉。

我们的tile在状态由Rendered变成非Rendered状态时候,我们会将当前tile丢入回收队列。

我们的tile状态变成Rendered的时候,我们又会从回收队列里面删除这个tile。

我们通过延迟回收,来实现当相机快速移动的时候,不会频繁的进行数据请求。又保证了最终内存的能够控制在一定范围之内。

3dtiles的数据请求

前面将的状态都是在说渲染状态,我们的数据何时被请求呢?这里由于比较简单,我就放在最后说了。当我们的tile的状态变成Rendered的时候,会触发数据请求命令。如果数据已经加载到内存,就不会再请求,如果数据状态为非ContentLoaded状态,我们会重新发起数据请求。

整个3dtile的最核心的调度流程大概就这些。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebHub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3dtiles的数据结构
  • 3dtiles的状态
  • 3dtiles的调度
  • 3dtiles的内存回收
  • 3dtiles的数据请求
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档