iModel Tile 格式

iTwin.js 完全能够以任何标准 3d 瓦片格式流式处理和渲染瓦片,这就是它显示实景模型和地形的方式。然而,iTwin.js 后端生成的瓦片用于可视化几何模型内容的使用称为“iMdl”(用于“iModel”)的非标准格式,该格式针对 iTwin.js 提供的独特功能进行了优化。(iTwin 中的瓦片为 iMdl 格式)

1. 细节层次(Level of detail)

每个瓦片都包含仅适用于屏幕上特定大小的细节级别的图形。例如,表示房屋的简单 iModel 的根节点(最低分辨率)可以如下所示:

"root-tile"

只要瓦片大致以屏幕像素的预期大小显示,则将以适当的细节程度显示。但如果我们放大,使其占据更多的屏幕,我们会注意到看似圆形的窗户和门把手不再是圆形的:

"root-tile-enlarge"

瓦片的详细程度表示为瓦片的“弦长阈值”,即可用于近似曲线的最大弦长。通常,我们希望弦长(以米为单位)大致相当于屏幕空间中一个像素的大小。当我们放大时,和弦变得比像素大(可被测量感知),因此我们必须要求更高细节级别的新瓦片(即更小的和弦阈值)。在下图中,绿色和蓝色矩形表示更高分辨率的瓦片,它们是根瓦片的直接子级:

"child-tile"

这些瓦片中的圆比根瓦片中的圆看起来更圆:

"child-tile-enlarge"

在某些情况下,整个元素可能小于一个像素,在这种情况下,可以从该瓦片中完全忽略这些元素。为了说明这一点,让我们创建一个由相同房屋组成的社区,其根瓦片如下所示:

"neighborhood-root-tile"

如果我们放大这块瓦片上的一扇门,我们会注意到,除了一些明显的非圆形圆圈外,门把手完全没有了!

"neighborhood-root-tile-zoom"

这没有问题,只要我们只以适当的屏幕大小显示此瓦片,事实上,限制每个瓦片中三角形的数量是一个重要的优化。但如果我们放大,渲染器将要求更高分辨率的子图块,其中包括门把手:

"neighborhood-child-tiles"

细节层次的关键是,包含较大空间的瓦片提供的细节级别较低,因此每个瓦片应包含大致相同数量的三角形。而且,由于屏幕上显示的瓦片数量应保持相对恒定,因此无论当前正在查看模型的哪个部分,在任何给定时间显示的三角形数量也应保持相对恒定。当用户缩小视图以查看整个模型时,可以忽略或简化微小的细节,因为这些细节无法识别。限制三角形的数量允许我们可视化任何比例的模型,而不会使 GPU 负担过重。

2. 批处理(Batching)

除了限制提交给 GPU 的三角形数量外,瓦片还可以最小化“绘制调用”的数量。draw 调用是发送到 GPU 的离散命令,例如,“绘制这组三角形”。例如,假设我们要绘制一个由 1000 个三角形组成的场景。我们可以提交 10 个由 100 个三角形组成的请求,或 1 个由 1000 个三角形组成的请求。在后一种情况下,现代图形硬件的性能要求要高得多。

iModel 由元素组成。在上面的示例房子 iModel 中,每个窗、门、墙、门把手和屋顶部分都是一个单独的图元。传统的渲染器会为每个元素生成一个三角形网格,并将每个元素作为单独的绘制调用提交。在由数千或数百万个元素组成的 iModel 中,这将产生太多的 draw 调用,因此无法维持交互式帧率。

iTwin.js 渲染器不渲染元素-它渲染瓦片。给定与瓦片体积相交的元素集,我们努力将它们全部批处理为尽可能少的网格,理想情况下为一个网格。这是通过合并元素网格,然后通过几种策略区分合并网格中的顶点来实现的,包括:

  • 颜色表:生成网格中每个唯一颜色的查找表。每个顶点只需在表中指定其 8 位或 16 位整数索引,而不是指定其 RGBA 颜色。
  • 材质图集:生成网格中每个唯一渲染材质的查找表。每个顶点指定表中相应材质的 8 位整数索引,而不是为每个材质生成单独的网格。

不幸的是,并非总是可以为每个瓦片生成一个网格(Mesh)。例如,无法将使用不同 RenderTexture 的网格批处理在一起;不透明网格不能与透明网格一起批处理,因为它们必须在单独的过程中渲染。

2.1 特征表(Feature Table)

如果 iTwin.js 不渲染元素,如何通过鼠标单击来选择单个元素?每个瓦片嵌入一个 FeatureTable,描述瓦片中存在的每个唯一特征,即元素 Id、子类别 Id 和几何体类的组合。瓦片中的每个顶点指定其起源要素的 24 位整数索引。当渲染器想要查询在给定区域(例如,在鼠标光标位置)中哪些特征可见时,它会渲染与该区域相交的瓦片,输出特征 ID,然后查询特征表以获取相应的特征。

特征表还支持高效的重新符号化。查找表是根据当前 FeatureOverrides 构建的。顶点着色器使用其特征 Id 索引到此特征表中,并应用相应的覆盖。

3. TileTree 结构

标准 3D 瓦片集通常是静态的:它们由一组由已知层次结构组织的有限预发布瓦片组成。例如,Bentley ContextCapture 发布了一组 tile 和一个描述其结构的 JSON 文件;显示这样的 tileset 只需要查看 JSON 文件并下载适当的 Tile 即可。

iModel 瓦片不同。由于用户可以在视口中任意近距离放大,因此瓦片集理论上是无限的。为模型生成一组详尽的瓦片是完全不切实际的。相反,直到有人请求 iModel 瓦片,iModel 瓦片才存在,此时它由 iTwin.js 后端生成的(然后可选地缓存下来供后续重用)。

此外,无法完全了解瓦片树的结构-它只能在瓦片生成期间动态发现。例如,某个特定瓦片的空间体积可能完全没有元素-因此该瓦片是空的,并且没有子瓦片。或者,瓦片的体积可能只包括未弯曲的几何体,在这种情况下,可能没有必要生成子瓦片来细化它,因为无法获得更高级别的细节。瓦片树结构的这些和其他特征只能通过实际生成瓦片来确定。

3.1 细分策略

给定包含某个体积的瓦片,我们希望获得与该体积相交的几何体的更高分辨率表示。这被称为“精炼”瓦片。瓦片遵循“空间一致性”——也就是说,瓦片的子瓦片的体积必须完全包含在父瓦片的体积内。iTwin.js 采用了两种瓦片细化策略:

  • 细分:这是迄今为止 3D 瓦片最常用的策略,有多种方法,包括四叉树、八叉树和 k-d 树。它们都将父瓦片的体积细分为更小的子体积。对于 3D 模型,iTwin.js 应用细分生成 8 个子空间。父瓦片的体积沿其最长轴一分为二;然后将得到的两半沿其最长轴拆分;最后,将 4 个子体积再次沿其最长轴拆分。该过程如下所示。(对于 2D 模型,只生成四个子空间,而不是八个子空间)。

"subdivision"

  • 放大:与细分一个瓦片的体积以生成多个更小的子瓦片不同,放大指生成一个与父瓦片大小相同但细节级别为两倍的子瓦片。此策略有助于降低深度复杂性,当用户放大时,必须请求更多的瓦片来显示延伸到屏幕中的几何图形(通过减少瓦片的层级来优化)。但是,必须按情形地应用它,因为在不减少瓦片体积的情况下过度增加细节级别可能会引入浮点精度错误,从而导致图形瑕疵。

3.2 分层

要生成瓦片,请使用 iTwin.js 后端查询其边界体积与瓦片体积相交的所有几何元素。然后,它遍历每个元素的几何流。任何相对于瓦片弦阈值足够小的图元或几何图元都会被忽略。否则,几何体将转换为三角形网格(或多段线或点字符串),并添加到瓦片的批处理(Bathing)网格中。最后将特征表和一些元数据嵌入到图块中。(注意:这是对瓦片生成过程的极其简化的描述-省略了许多细节和细微差别)。

瓦片生成过程中的元数据是发现瓦片树结构的关键。除其他信息外,每个瓦片的元数据记录:

  • 瓦片的图形中是否省略了任何足够小的元素或几何图形;
  • 瓦片体积内是否遇到任何弯曲或切割几何体;
  • 包含瓦片体积内所有几何体的紧密边界框;
  • 一个位字段,指示已确定瓦片体积的哪些子空间完全为空。

在可行的情况下,测试子空间是否与单个面和线段相交,而不是与边界框相交;这提高了检测空子空间的准确性。

如果忽略了任何几何体,或者瓦片空间内存在任何弯曲或抽取的几何体,我们知道瓦片需要细化(即它有子瓦片);否则,细化将不会在细节级别上产生实际的改进。

内容体积允许我们对视锥执行更严格的相交测试,以避免绘制实际上不可见的瓦片。在下图中,绿色和蓝色矩形表示每个瓦片的内容体积,通常比瓦片的边界体积小(且永远不会大):

"content-volumes"

有关空子卷的信息使我们能够避免对子图块的请求,因为我们知道这些子图块不会生成任何图形。在下图中,绿色矩形表示需要优化的瓦片,蓝色矩形表示不需要优化的瓦片。丢失的矩形表示被标识为完全为空的卷,因此不会导致对这些卷的瓦片请求。请注意,所有绿色矩形都包含弯曲的几何体,而所有蓝色矩形都只包含未弯曲的几何体:

"depth1"

第一层级

"depth2"

第二层级

4. 优化

iModel 瓦片采用了许多优化,旨在减少瓦片大小(并进一步减少 GPU 内存使用和下载时间)和提高帧率,包括:

  • 广泛使用查找表:通常,顶点数据(如位置、颜色、法向量等)作为顶点属性提交给 GPU。iModel 瓦片将所有顶点数据嵌入到用作查找表的单个纹理中;唯一的顶点属性是每个三角形顶点表中的 24 位索引。类似地,另一个查找表包含有关网格中每个可见边的信息。这大大减少了冗余,从而产生更小的分幅。
  • 特定瓦片:iTwin.js 支持显示网格的边。这需要有关每个瓦片中要包含的边的信息,这可以大大增加每个瓦片的大小。如果当前未显示边,iTwin.js 将请求忽略边缘信息的磁贴,避免了不必要的开销,但如果以后启用边缘显示,则需要请求新的磁贴。类似地,为剖面切割图形、时间轴动画和其他目的生成特殊的瓦片。
  • 压缩:采用各种策略来最小化磁贴的内存占用,包括位置量化、法向量 oct 编码以及使用 24 位整数作为索引。

results matching ""

    No results matching ""