Cesium源码解析三(metadataAvailability的含义)

快速导航

Cesium源码解析一(搭建开发环境)
Cesium源码解析二(terrain文件的加载、解析与渲染全过程梳理)
Cesium源码解析三(metadataAvailability的含义)
Cesium源码解析四(metadata元数据拓展中行列号的分块规则解析)
Cesium源码解析五(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)

1.前言

  上一篇中我们梳理了 Cesium 加载、解析、渲染 terrain 的整个流程,这一篇我们就深入细节,看一下 layer.json 中的 metadataAvailability 参数的含义。一般使用 Cesium 的同学可能也注意不到这个参数,之所以要单独拿出来说这个,是因为我们遇到了一个问题,使用 GeoServer 发布的 terrain 服务,在 UE 中加载没问题,在 Cesium 中加载却一直报错: TypeError: Cannot read properties of undefined (reading ‘addAvailableTileRange’) ,由此引发了我们不断深入,终于发现了这个参数的真正含义,本篇我们就来讲一讲。

2.json文件的结构

  layer.json这个文件,相信使用过 Cesium 的同学应该都不陌生了,我们来看下它长什么样子。
在这里插入图片描述

  如图,这是一个 CesiumLab 处理数据以后,给出的 layer.json ,我们可以看到里面有个参数 metadataAvailability ,它的值是10。这是什么意思?我这里可以直接给出结论,后面我们再详细看:

metadataAvailability 翻译过来叫做:元数据可用性,它的真正作用是,计算当前层级的父层级中,哪一级的 terrain 数据中包括了元数据的信息。

3.元数据的解析过程

  元数据的解析过程是在 CesiumTerrainProvider.createQuantizedMeshTerrainData 函数中完成的,详细的解析解析过程我们在第一篇中进行了介绍,不清楚的同学可以移步 Cesium源码解析一(terrain文件的加载、解析与渲染全过程)。这里我们只把里面核心的一段放出来,这是一个 while 循环:

while (pos < view.byteLength) {
    var extensionId = view.getUint8(pos, true);
    pos += Uint8Array.BYTES_PER_ELEMENT;
    var extensionLength = view.getUint32(pos, littleEndianExtensionSize);
    pos += Uint32Array.BYTES_PER_ELEMENT;
    console.log( level, x, y,"extensionid="+extensionId+",extensionLength="+extensionLength+",byteLength="+view.byteLength);
    if (
      extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS &&
      provider._requestVertexNormals
    ) {
      //数据中有光照并且也请求了光照
      encodedNormalBuffer = new Uint8Array(buffer, pos, vertexCount * 2);
    } else if (
      extensionId === QuantizedMeshExtensionIds.WATER_MASK &&
      provider._requestWaterMask
    ) {
      //数据中有水面并且也请求了水面
      waterMaskBuffer = new Uint8Array(buffer, pos, extensionLength);
    } else if (
      extensionId === QuantizedMeshExtensionIds.METADATA &&
      provider._requestMetadata
    ) {
      //数据中有元数据并且也请求了元数据
      var stringLength = view.getUint32(pos, true);
      console.log("metadata_length="+stringLength);
      if (stringLength > 0) {
        var metadata = getJsonFromTypedArray(
          new Uint8Array(buffer),
          pos + Uint32Array.BYTES_PER_ELEMENT,
          stringLength
        );
        var availableTiles = metadata.available;
        if (defined(availableTiles)) {
          for (var offset = 0; offset < availableTiles.length; ++offset) {
            var availableLevel = level + offset + 1;
            var rangesAtLevel = availableTiles[offset];
            var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(
              availableLevel
            );

            for (
              var rangeIndex = 0;
              rangeIndex < rangesAtLevel.length;
              ++rangeIndex
            ) {
              var range = rangesAtLevel[rangeIndex];
              var yStart = yTiles - range.endY - 1;
              var yEnd = yTiles - range.startY - 1;
              provider.availability.addAvailableTileRange(
                availableLevel,
                range.startX,
                yStart,
                range.endX,
                yEnd
              );
              layer.availability.addAvailableTileRange(
                availableLevel,
                range.startX,
                yStart,
                range.endX,
                yEnd
              );
            }
          }
        }
      }
      layer.availabilityTilesLoaded.addAvailableTileRange(level, x, y, x, y);
    }
    pos += extensionLength;
  }

  在第0级和10级的时候会进行元数据的解析来构建四叉树。我们以加载 Cesium ion 提供的 terrain 数据为例来看下解析到的元数据数据,当 zxy=0,0,0是这样的:
在这里插入图片描述
当 zxy=0,1,0是这样的:
在这里插入图片描述
  有没有发现解析出来的数组的长度都是10。但这只是表象,千万不要以为这个10就是 metadataAvailability 的值。因为 metadataAvailability 是用来计算的,我们接着往下跟。

4.计算父层级中哪一级包含了元数据信息以构建四叉树

  这个功能是在 CesiumTerrainProvider.getAvailabilityTile 函数中实现的。我们来看下代码:

function getAvailabilityTile(layer, x, y, level) {
  if (level === 0) {
    return;
  }
  //layer.availabilityLevels就是layer.json中的metadataAvailability的值
  var availabilityLevels = layer.availabilityLevels;
  var parentLevel =
    level % availabilityLevels === 0
      ? level - availabilityLevels
      : ((level / availabilityLevels) | 0) * availabilityLevels;
  var divisor = 1 << (level - parentLevel);
  var parentX = (x / divisor) | 0;
  var parentY = (y / divisor) | 0;
  return {
    level: parentLevel,
    x: parentX,
    y: parentY,
  };
}

  观察上述代码,我们发现,调用过程中,是用当前层级 levellayer.availabilityLevels,也就是 metadataAvailability 进行取余,来计算出一个父层级。
如果当前 level 的值是0-9,那么计算出来就是0,如果当前 level 是10-20,计算出来的值就是10,也就是说,只有第0级和第10的 terrain 文件中才存储了元数据的信息。当我们跟踪解析元数据的代码时,也能发现这一规律。
在这里插入图片描述
  然后我们再来看下此时的调用栈:
在这里插入图片描述
  发现原来是递归调用,注意,Scene.updateAndExecuteCommands ,这是一个重要过程,我们在上一篇中分析过,这个过程是执行渲染命令,它使用的数据是上一帧准备好的数据,因为现在的浏览器一般都是每秒60帧,所以,人的是感觉就是实时渲染的。

3.总结

  本篇博文中,我们对 metadataAvailability 进行了深入解析,并结合 Cesium 的整个渲染过程进行了分析,使我们的认识更加深刻了。那么本次就讲到这里了,回见~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AIGIS.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值