• Cesium 地球网格构造


    Cesium 地球网格构造

    Cesium原理篇:3最长的一帧之地形(2:高度图)

    请添加图片描述

    HeightmapTessellator

    用于从高程图像创建网格。提供了一个函数 computeVertices,可以根据高程图像创建顶点数组。

    该函数的参数包括高程图像、高度数据的结构、网格宽高、边缘裙板高度、矩形范围、相机中心点等。函数的实现使用了许多性能优化技巧,如将函数内常量化、内联等。

    该模块的输出为一个对象,包括创建好的顶点数组、最大与最小高度、该网格的边界球、边界方向盒等信息。

    1、函数定义
    // 声明
    static computeVertices(options) {}
    
    • 1
    • 2
    // 使用
    const width = 5;
    const height = 5;
    const statistics = Cesium.HeightmapTessellator.computeVertices({
        heightmap : [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
        width : width,
        height : height,
        skirtHeight : 0.0,
        nativeRectangle : {
            west : 10.0,
            east : 20.0,
            south : 30.0,
            north : 40.0
        }
    });
    
    const encoding = statistics.encoding;
    const position = encoding.decodePosition(statistics.vertices, index);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    options 参数:

    参数类型描述
    heightmapArray要镶嵌的高度图。
    widthnumber高度图的宽度(以高度样本计)。
    heightnumber高度图的高度(以高度样本计)。
    skirtHeightnumber要悬垂在高度图边缘的裙子的高度。
    nativeRectangleRectangle高度贴图投影的原始坐标中的矩形。对于具有地理投影的高度图,这是度数。对于 Web 墨卡托投影,这是米。
    exaggerationnumber用于夸大地形的比例尺。默认为1.
    exaggerationRelativeHeightnumber地形夸大的高度,以米为单位。默认为0.
    rectangleRectangle高度图覆盖的矩形,大地坐标为北、南、东和西属性(弧度)。必须提供矩形或本机矩形。如果同时提供两者,则假定它们是一致的。
    isGeographicboolean如果高度图使用{@link GeographicProjection},则为true;如果使用{@link WebMercatorProjection},则为false。默认为true。
    relativeToCenterCartesian3将计算出的位置作为Cartesian3.subtract(worldPosition, relativeToCenter)。默认为Cartesian3.ZERO。
    ellipsoidEllipsoid高度贴图适用的椭球体。默认为Ellipsoid.WGS84。
    structureobject描述高度数据结构的对象。
    裙边(skirt)

    防止不同层级mesh加载时出现裂缝

    实现
      static computeVertices(options) {
        const cos = Math.cos;
        const sin = Math.sin;
        const sqrt = Math.sqrt;
        const toRadians = CesiumMath.toRadians;
    
        const heightmap = options.heightmap;
        const width = options.width;
        const height = options.height;
        const skirtHeight = options.skirtHeight;
        const hasSkirts = skirtHeight > 0.0;
    
        const ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
    
        const nativeRectangle = Rectangle.clone(options.nativeRectangle);
        const rectangle = Rectangle.clone(options.rectangle);
    
        const geographicWest = rectangle.west;
        const geographicSouth = rectangle.south;
        const geographicEast = rectangle.east;
        const geographicNorth = rectangle.north;
    
        const relativeToCenter = options.relativeToCenter;
        const includeWebMercatorT = defaultValue(options.includeWebMercatorT, false);
    
        const exaggeration = defaultValue(options.exaggeration, 1.0);
        const exaggerationRelativeHeight = defaultValue(
          options.exaggerationRelativeHeight,
          0.0
        );
        const hasExaggeration = exaggeration !== 1.0;
        const includeGeodeticSurfaceNormals = hasExaggeration;
    
        const rectangleWidth = Rectangle.computeWidth(nativeRectangle);
        const rectangleHeight = Rectangle.computeHeight(nativeRectangle);
    
        // 每个网格的长宽
        const granularityX = rectangleWidth / (width - 1);
        const granularityY = rectangleHeight / (height - 1);
    
        const radiiSquared = ellipsoid.radiiSquared;
        const radiiSquaredX = radiiSquared.x;
        const radiiSquaredY = radiiSquared.y;
        const radiiSquaredZ = radiiSquared.z;
    
        let minimumHeight = 65536.0;
        let maximumHeight = -65536.0;
    
        const fromENU = Transforms.eastNorthUpToFixedFrame(
          relativeToCenter,
          ellipsoid
        );
    
        const minimum = minimumScratch;
        minimum.x = Number.POSITIVE_INFINITY;
        minimum.y = Number.POSITIVE_INFINITY;
        minimum.z = Number.POSITIVE_INFINITY;
    
        const maximum = maximumScratch;
        maximum.x = Number.NEGATIVE_INFINITY;
        maximum.y = Number.NEGATIVE_INFINITY;
        maximum.z = Number.NEGATIVE_INFINITY;
    
        let hMin = Number.POSITIVE_INFINITY;
    
        const gridVertexCount = width * height;
        const edgeVertexCount = skirtHeight > 0.0 ? width * 2 + height * 2 : 0;
        const vertexCount = gridVertexCount + edgeVertexCount;
    
        const positions = new Array(vertexCount);
        const heights = new Array(vertexCount);
        const uvs = new Array(vertexCount);
    
        let startRow = 0;
        let endRow = height;
        let startCol = 0;
        let endCol = width;
    
        if (hasSkirts) {
          --startRow;
          ++endRow;
          --startCol;
          ++endCol;
        }
    
        for (let rowIndex = startRow; rowIndex < endRow; ++rowIndex) {
          let row = rowIndex;
          if (row < 0) {
            row = 0;
          }
          if (row >= height) {
            row = height - 1;
          }
    
          //
          //  ^ latitude(纬度)
          //  |
          //  |              North(90)
          //  |              ---------
          //  |             |         |
          //  |  West(-180) |         | East(0)
          //  |             |         |
          //  |              ---------
          //  |              South(-90)
          //  -----------------------------> longitude(经度)
          // 地理坐标系下
    
          // 当前纬度(latitude) 距离最北头(North) 的距离
          // 这个值是越来越小的, 随着行数越来越大
          let latitude = nativeRectangle.north - granularityY * row;
          latitude = toRadians(latitude);
    
          // 当前纬度(latitude) 距离最南头(South) 的百分比(0~1)
          let v = (latitude - geographicSouth) / (geographicNorth - geographicSouth);
          v = CesiumMath.clamp(v, 0.0, 1.0);
    
          const isNorthEdge = rowIndex === startRow;
          const isSouthEdge = rowIndex === endRow - 1;
    
          const cosLatitude = cos(latitude);
          const nZ = sin(latitude);
          const kZ = radiiSquaredZ * nZ;
    
          for (let colIndex = startCol; colIndex < endCol; ++colIndex) {
            let col = colIndex;
            if (col < 0) {
              col = 0;
            }
            if (col >= width) {
              col = width - 1;
            }
    
            const terrainOffset = row * width + col;
    
            let heightSample = heightmap[terrainOffset]
    
            let longitude = nativeRectangle.west + granularityX * col;
            longitude = toRadians(longitude);
    
            let u = (longitude - geographicWest) / (geographicEast - geographicWest);
            u = CesiumMath.clamp(u, 0.0, 1.0);
    
            let index = row * width + col;
    
            if (skirtHeight > 0.0) {
              const isWestEdge = colIndex === startCol;
              const isEastEdge = colIndex === endCol - 1;
              const isEdge = isNorthEdge || isSouthEdge || isWestEdge || isEastEdge;
              const isCorner = (isNorthEdge || isSouthEdge) && (isWestEdge || isEastEdge);
    
              if (isCorner) {
                // Don't generate skirts on the corners.
                continue;
              } else if (isEdge) {
                heightSample -= skirtHeight;
    
                if (isWestEdge) {
                  // The outer loop iterates north to south but the indices are ordered south to north, hence the index flip below
                  // 外循环从北到南迭代,但索引按从南到北的顺序排列,因此索引在下面翻转
                  index = gridVertexCount + (height - row - 1);
                } else if (isSouthEdge) {
                  // Add after west indices. South indices are ordered east to west.
                  // 加在西方指数之后。南方指数是从东向西排列的。
                  index = gridVertexCount + height + (width - col - 1);
                } else if (isEastEdge) {
                  // Add after west and south indices. East indices are ordered north to south. The index is flipped like above.
                  // 在西部和南部指数后加上。东部指数是从北向南排列的。索引如上所述翻转。
                  index = gridVertexCount + height + width + row;
                } else if (isNorthEdge) {
                  // Add after west, south, and east indices. North indices are ordered west to east.
                  // 在西部、南部和东部指数后添加。北方指数是从西向东排列的。
                  index = gridVertexCount + height + width + height + col;
                }
              }
            }
    
            // 经纬度转笛卡尔坐标系
            const nX = cosLatitude * cos(longitude);
            const nY = cosLatitude * sin(longitude);
    
            const kX = radiiSquaredX * nX;
            const kY = radiiSquaredY * nY;
    
            const gamma = sqrt(kX * nX + kY * nY + kZ * nZ);
            const oneOverGamma = 1.0 / gamma;
    
            const rSurfaceX = kX * oneOverGamma;
            const rSurfaceY = kY * oneOverGamma;
            const rSurfaceZ = kZ * oneOverGamma;
    
            const position = new Cartesian3();
            position.x = rSurfaceX + nX * heightSample;
            position.y = rSurfaceY + nY * heightSample;
            position.z = rSurfaceZ + nZ * heightSample;
    
            hMin = Math.min(hMin, heightSample);
    
            positions[index] = position;
            uvs[index] = new Cartesian2(u, v);
            heights[index] = heightSample;
          }
        }
    
        const aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter);
        const encoding = new TerrainEncoding(
          relativeToCenter,
          aaBox,
          hMin,
          maximumHeight,
          fromENU,
          false,
          includeWebMercatorT,
          includeGeodeticSurfaceNormals,
          exaggeration,
          exaggerationRelativeHeight
        );
        const vertices = new Float32Array(vertexCount * encoding.stride);
    
        let bufferIndex = 0;
        for (let j = 0; j < vertexCount; ++j) {
          bufferIndex = encoding.encode(
            vertices,
            bufferIndex,
            positions[j],
            uvs[j],
            heights[j],
            undefined,
            undefined,
            undefined
          );
        }
    
        return {
          vertices: vertices,
        };
      }
    
    • 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
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
  • 相关阅读:
    tailscale自建headscale和derp中继
    另辟蹊径者 PoseiSwap:背靠潜力叙事,构建 DeFi 理想国
    linux下doc转docx
    RDP方式连接服务器上传文件方法
    webrtc
    TCP协议的三次握手和四次挥手
    Visual Studio 2022 正式支持 .NET MAUI 开发
    线上诊断神器-arthas基本应用
    百度SEO优化策略与经验分享(提升百度排名的8大步骤)
    Vue3中的refs使用
  • 原文地址:https://blog.csdn.net/qq_21476953/article/details/132910534