• Cesium源码解析二(metadataAvailability的含义)


    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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

      在第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,
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    3.总结

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

  • 相关阅读:
    亚马逊是如何铺设多个IP账号实现销量大卖的?
    Critical:Apache Superset Remote Code Execution Vulnerability
    µC/OS-II---两个系统任务
    Java面向对象进阶3——多态的概述及特点
    2023年25个Java8面试问题和答案
    【java】实现sse调用websocket接口,忽略wss证书并控制sse吐字速度
    Java开始
    Vue复选框批量删除示例
    ViewModel
    MUMPS+Caché学习记录
  • 原文地址:https://blog.csdn.net/xiangshangdemayi/article/details/125506145