Cesium 地球网格构造

news2024/11/14 3:18:40

Cesium 地球网格构造

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

请添加图片描述

HeightmapTessellator

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

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

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

1、函数定义

// 声明
static computeVertices(options) {}
// 使用
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);

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,
    };
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1013026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gradle的简介、下载、安装、配置及使用流程

Gradle的简介、下载、安装、配置及使用流程 1.Gradle的简介 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置&#xff0c;也增加了基于Kotlin语言的kotlin-based DSL&#xff0c;抛弃了基于X…

AI项目六:基于YOLOV5的CPU版本部署openvino

若该文为原创文章&#xff0c;转载请注明原文出处。 一、CPU版本DEMO测试 1、创建一个新的虚拟环境 conda create -n course_torch_openvino python3.8 2、激活环境 conda activate course_torch_openvino 3、安装pytorch cpu版本 pip install torch torchvision torchau…

vcruntime140_1.dll修复方法分享,教你安全靠谱的修复手段

在使用Windows操作系统的过程中&#xff0c;我们有时会遇到vcruntime140_1.dll文件丢失或损坏的情况。本文将详细介绍vcruntime140_1.dll的作用&#xff0c;以及多种解决方法和修复该文件时需要注意的问题&#xff0c;希望能帮助读者更好地处理这一问题。 一.vcruntime140_1.dl…

数据结构——【堆】

一、堆的相关概念 1.1、堆的概念 1、堆在逻辑上是一颗完全二叉树&#xff08;类似于一颗满二叉树只缺了右下角&#xff09;。 2、堆的实现利用的是数组&#xff0c;我们通常会利用动态数组来存放元素&#xff0c;这样可以快速拓容也不会很浪费空间&#xff0c;我们是将这颗完…

【Java】SpringData JPA快速上手,关联查询,JPQL语句书写

JPA框架 文章目录 JPA框架认识SpringData JPA使用JPA快速上手方法名称拼接自定义SQL关联查询JPQL自定义SQL语句 ​ 在我们之前编写的项目中&#xff0c;我们不难发现&#xff0c;实际上大部分的数据库交互操作&#xff0c;到最后都只会做一个事情&#xff0c;那就是把数据库中的…

电容 stm32

看到stm32电源部分都会和电容配套使用&#xff0c;所以对电容的作用产生了疑惑 电源 负电荷才能在导体内部自由移动&#xff0c;电池内部的化学能驱使着电源正电附近的电子移动向电源负极区域。 电容 将电容接上电池&#xff0c;电容的两端一段被抽走电子&#xff0c;一端蓄积…

【STL容器】vector

文章目录 前言vector1.1 vector的定义1.2 vector的迭代器1.3 vector的元素操作1.3.1 Member function1.3.2 capacity1.3.3 modify 1.4 vector的优缺点 前言 vector是STL的容器&#xff0c;它提供了动态数组的功能。 注&#xff1a;文章出现的代码并非STL库里的源码&#xff0c…

C++ PrimerPlus 复习 第三章 处理数据

第一章 命令编译链接文件 make文件 第二章 进入c 第三章 处理数据 文章目录 C变量的命名规则&#xff1b;C内置的整型——unsigned long、long、unsigned int、int、unsigned short、short、char、unsigned char、signed char和bool&#xff1b;如何知道自己计算机类型宽度获…

Jenkins Maven pom jar打包未拉取最新包解决办法,亲测可行

Jenkins Maven pom jar打包未拉取最新包解决办法&#xff0c;亲测可行 1. 发布新版的snapshots版本的jar包&#xff0c;默认Jenkins打包不拉取snapshots包2. 设置了snapshot拉取后&#xff0c;部分包还未更新&#xff0c;需要把包版本以snapshot结尾3. IDEA无法更新snapshots包…

超炫的开关效果

超炫的开关动画 代码如下 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Switch</title><style>/* 谁家好人 一个按钮写三百多行样式代码 &#x1f622;&#x1f622;*/*,*:after,*:before {box-sizing: bor…

代码随想录--栈与队列-用队列实现栈

使用队列实现栈的下列操作&#xff1a; push(x) -- 元素 x 入栈pop() -- 移除栈顶元素top() -- 获取栈顶元素empty() -- 返回栈是否为空 &#xff08;这里要强调是单向队列&#xff09; 用两个队列que1和que2实现队列的功能&#xff0c;que2其实完全就是一个备份的作用 impo…

产教融合 | 力软联合重庆科技学院开展低代码应用开发培训

近日&#xff0c;力软与重庆科技学院联合推出了为期两周的低代码应用开发培训课程&#xff0c;来自重庆科技学院相关专业的近百名师生参加了此次培训。 融合研学与实践&#xff0c;方能成为当代数字英才。本次培训全程采用线下模式&#xff0c;以“力软低代码平台”为软件开发…

聚焦真实用例,重仓亚洲,孙宇晨畅谈全球加密新格局下的主动变革

「如果能全面监管&#xff0c;加密行业仍有非常大的增长空间。目前加密用户只有 1 亿左右&#xff0c;如果监管明朗&#xff0c;我们可以在 3-5 年内获得 20-30 亿用户。」—— 孙宇晨 9 月 14 日&#xff0c;波场 TRON 创始人、火币 HTX 全球顾问委员会成员孙宇晨受邀出席 20…

RHCSA的一些简单操作命令

目录 1、查看Linux版本信息 2、ssh远程登陆 3、解析[zxlocalhost ~]$ 与 [rootlocalhost ~]# 4、退出命令exit 5、su——switch user 6、打印用户所处的路径信息pwd 7、修改路径 8、输出文件\目录信息 9、重置root账号密码 10、修改主机名称 1&#xff09;临时修改…

SkyWalking入门之Agent原理初步分析

一、简介 当前稍微上点体量的互联网公司已经逐渐采用微服务的开发模式&#xff0c;将之前早期的单体架构系统拆分为很多的子系统&#xff0c;子系统封装为微服务&#xff0c;彼此间通过HTTP协议RESET API的方式进行相互调用或者gRPC协议进行数据协作。 早期微服务只有几个的情况…

三种方式部署单机版Minio,10行命令干就完了~

必要步骤&#xff1a;安装MinIO 拉取MinIO镜像 docker pull quay.io/minio/minio 创建文件挂载点 mkdir /home/docker/MinIO/data &#xff08;文件挂载点映射&#xff0c;默认是/mydata/minio/data&#xff0c;修改为/home/docker/MinIO&#xff0c;文件存储位置自行修改&…

随笔-嗨,中奖了

好久没有动笔了&#xff0c;都懒惰了。 前段时间&#xff0c;老妹凑着暑假带着双胞胎的一个和老妈来了北京&#xff0c;听着小家伙叫舅舅&#xff0c;还是挺稀奇的。周末带着他们去了北戴河&#xff0c;全家人都是第一次见大海&#xff0c;感觉&#xff0c;&#xff0c;&#…

qiankun 乾坤主应用访问微应用css静态图片资源报404

发现static前没有加我指定的前缀 只有加了后才会出来 解决方案: env定义前缀 .env.development文件中 # static前缀 VUE_APP_PUBLIC_PREFIX"" .env.production文件中 # static前缀 VUE_APP_PUBLIC_PREFIX"/szgl" settings文件是封了一下src\settings…

测试平台前端部署

这里写目录标题 一、前端代码打包1、打包命令2、打包完成后,将dist文件夹拷贝到nginx文件夹中3、重新编写default.conf4、将之前启动的容器进行停止并且删除,再重新创建容器5、制作Dockerfile二、编写Dockerfile一、前端代码打包 1、打包命令 npm run build2、打包完成后,…

Kubernetes学习篇之组件

Kubernetes学习篇之组件 文章目录 Kubernetes学习篇之组件前言概述控制平面组件(Control Plane Components)kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-manager Node 组件kubeletkube-proxy容器运行时(Container Runtime) 插件(Addons)DNSWeb界…