cesium gltf模型

news2024/9/27 15:32:51

cesium 支持的三维模型格式包括GLTF和GLB(二进制glTF文件)。 本文通过使用Entity图元的modelGraphics对象来加载gltf模型,简单对gltf模型的加载进行了封装。通过设置模型的欧拉角,可以计算模型的朝向。

1 3D数学中模型旋转的方式

在3D数学中,有三种方式来旋转模型,即 旋转矩阵、欧拉角、四元数。如何不熟悉,可以先去补一下3D数据基础课程。因为欧拉角比较直观,易与理解,这里简单对其进行介绍。

欧拉角将模型朝向描述为围绕3个垂直轴的3个旋转。在cesium中 定义为“航向-俯仰-滚转”。即模型朝向由航向角(Heading Angle)、俯仰角(PitchAngle)和滚转角(RollAngle)定义。

欧拉角有一个缺陷,即大名鼎鼎的万向节死锁问题。通过四元数可以解决这个问题,所以在实际的计算过程中,一般会将欧拉角转换为四元数后再计算模型朝向。

2 计算模型朝向

cesium 提供了相应的函数来计算模型朝向。

2.1 HeadingPitchRoll 类

该类定义了欧拉角(以弧度表示)来表征旋转。其参数如下:

2.2 Transforms 命名空间

cesium的Transforms命名空间包含一些三维空间的常用的转换函数,其中的headingPitchRollQuaternion函数可以通过模型的位置和设置的欧拉角计算模型的朝向。

2.3 计算模型朝向
calculateOrientation() {
  let hpr = new Cesium.HeadingPitchRoll(
  Cesium.Math.toRadians(this.style.heading),
  Cesium.Math.toRadians(this.style.pitch),
  Cesium.Math.toRadians(this.style.roll)
  );
  const position =  Cesium.Cartesian3.fromDegrees(this.coords[0], this.coords[1], this.baseHeight)
  const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
  return orientation
}

3 GltfModel 类封装

ModelGraphics类的构造函数参数如下:

各参数的具体含义请参考官网API,这里不再进行详细说明。

GltfModel.js

/*
 * @Description:
 * @Author: maizi
 * @Date: 2022-05-27 11:36:22
 * @LastEditTime: 2023-04-24 09:56:21
 * @LastEditors: maizi
 */
const merge = require('deepmerge')

const defaultStyle = {
  modelUrl: "",
  heading: 0,
  pitch: 0,
  roll: 0,
  scale: 1
}

class GltfModel {
  constructor(viewer, coords, options = {}) {
    this.viewer = viewer;
    this.coords = coords;
    this.options = options;
    this.props = this.options.props;
    this.baseHeight = this.coords[2] || 0;
    this.style = merge(defaultStyle, this.options.style || {});

    this.entity = null;
    this.init();
  }

  init() {
    this.entity = new Cesium.Entity({
      id: Math.random().toString(36).substring(2),
      type: "gltf_model",
      position: Cesium.Cartesian3.fromDegrees(this.coords[0], this.coords[1], this.baseHeight),
      props: this.props,
      orientation: this.calculateOrientation(),
      model: {
        uri: this.style.modelUrl,
        scale: this.style.scale,
      }
    });
  }

  // 计算模型四元数
  calculateOrientation() {
     //航向
     let hpr = new Cesium.HeadingPitchRoll(
      Cesium.Math.toRadians(this.style.heading),
      Cesium.Math.toRadians(this.style.pitch),
      Cesium.Math.toRadians(this.style.roll)
    );
    const position =  Cesium.Cartesian3.fromDegrees(this.coords[0], this.coords[1], this.baseHeight)
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
    return orientation
  }

  updateStyle(style) {
    this.style = merge(defaultStyle, style);
    this.entity.orientation = this.calculateOrientation();
    this.entity.model.scale = this.style.scale;
  }

  setSelect(enabled) {
    if (enabled) {
      this.entity.model.silhouetteColor = Cesium.Color.fromAlpha(Cesium.Color.YELLOW, 1);
      this.entity.model.silhouetteSize = 4;
    } else {
      this.entity.model.silhouetteColor = Cesium.Color.fromAlpha(Cesium.Color.YELLOW, 1);
      this.entity.model.silhouetteSize = 0;
    }
  }
}

export {
  GltfModel
}

4 完整示例代码

MapWorks.js

import GUI from 'lil-gui'; 
// 初始视图定位在中国
import { GltfModel } from './GltfModel'

Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(90, -20, 110, 90);
//天地图key
const key = '你申请的key'

let viewer = null;
let modelLayer = null
let eventHandler = null
let gltfModelList = []
let selectGraphic = null
let gui = null


function initMap(container) {
  viewer = new Cesium.Viewer(container, {
    animation: false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    infoBox: false,
    sceneModePicker: false,
    selectionIndicator: false,
    timeline: false,
    navigationHelpButton: false, 
    scene3DOnly: true,
    orderIndependentTranslucency: false,
    contextOptions: {
      webgl: {
        alpha: true
      }
    }
  })
  viewer._cesiumWidget._creditContainer.style.display = 'none'
  viewer.scene.fxaa = true
  viewer.scene.postProcessStages.fxaa.enabled = true
  if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
    // 判断是否支持图像渲染像素化处理
    viewer.resolutionScale = window.devicePixelRatio
  }
  // 移除默认影像
  removeAll()
  // 地形深度测试
  viewer.scene.globe.depthTestAgainstTerrain = true
  // 背景色
  viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.0, 0.0, 0)
  // 太阳光照
  viewer.scene.globe.enableLighting = true;

  // 初始化图层
  initLayer()

  // 初始化鼠标事件
  initClickEvent()


  //调试
  window.viewer = viewer
}

function initGui() {
  let params = {
    ...selectGraphic.style
  }
  gui = new GUI()
  let layerFolder = gui.title('样式设置')
  layerFolder.add(params, 'heading', -180, 180).step(1.0).onChange(function (value) {
    selectGraphic.updateStyle(params)
  })
  layerFolder.add(params, 'pitch', -90, 90).step(1.0).onChange(function (value) {
    selectGraphic.updateStyle(params)
  })
  layerFolder.add(params, 'roll', -180, 180).step(1.0).onChange(function (value) {
    selectGraphic.updateStyle(params)
  })
  layerFolder.add(params, 'scale',0, 100).step(0.1).onChange(function (value) {
    selectGraphic.updateStyle(params)
  })
}

function addTdtLayer(options) {
  let url = `https://t{s}.tianditu.gov.cn/DataServer?T=${options.type}&x={x}&y={y}&l={z}&tk=${key}`
  const layerProvider = new Cesium.UrlTemplateImageryProvider({
    url: url,
    subdomains: ['0','1','2','3','4','5','6','7'],
    tilingScheme: new Cesium.WebMercatorTilingScheme(),
    maximumLevel: 18
  });
  viewer.imageryLayers.addImageryProvider(layerProvider);
}
function initLayer() {
  addTdtLayer({
    type: 'img_w'
  })
  addTdtLayer({
    type: 'cia_w'
  })
  modelLayer = new Cesium.CustomDataSource('gltfModel')
  viewer.dataSources.add(modelLayer)
}

function loadModels(points) {
  points.forEach(item => {
    const gltfModel = new GltfModel(viewer, item.coords, {
      style:item.style
    })
    modelLayer.entities.add(gltfModel.entity)
    gltfModelList.push(gltfModel)
  });
  viewer.flyTo(modelLayer)
}

function initClickEvent() {
  eventHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  initLeftClickEvent()
  initMouseMoveEvent()
}

function initLeftClickEvent() {
  eventHandler.setInputAction((e) => {
    if (selectGraphic) {
      selectGraphic.setSelect(false)
      selectGraphic = null
    }
    if (gui) {
      gui.destroy()
    }
    let pickedObj = viewer.scene.pick(e.position);
    if (pickedObj && pickedObj.id) {
      if (pickedObj.id.type === 'gltf_model') {
        selectGraphic = getGraphicById(pickedObj.id.id)
        if (selectGraphic) {
          selectGraphic.setSelect(true)
          initGui()
        }
      }
    }
  },Cesium.ScreenSpaceEventType.LEFT_CLICK)
}

function initMouseMoveEvent() {
  eventHandler.setInputAction((e) => {
    const pickedObj = viewer.scene.pick(e.endPosition);
    if (pickedObj && pickedObj.id) {
      if (pickedObj.id.type === 'gltf_model') {
        // 改变鼠标状态
        viewer._element.style.cursor = "";
        document.body.style.cursor = "pointer";
      } else {
        viewer._element.style.cursor = "";
        document.body.style.cursor = "default";
      }
    } else {
      viewer._element.style.cursor = "";
      document.body.style.cursor = "default";
    }
  },Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}

function getGraphicById(id) {
  let graphic = null
  for (let i = 0; i < gltfModelList.length; i++) {
    if (gltfModelList[i].entity.id === id) {
      graphic = gltfModelList[i]
      break
    } 
  }
  return graphic
}



function removeAll() {
  viewer.imageryLayers.removeAll();
}

function destroy() {
  viewer.entities.removeAll();
  viewer.imageryLayers.removeAll();
  viewer.destroy();
}

export {
  initMap,
  loadModels,
  destroy
}

GltfModel.vue

<!--
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-07 17:03:50
 * @LastEditTime: 2023-04-24 09:53:33
 * @LastEditors: maizi
-->

<template>
  <div id="container">
  </div>
</template>

<script>
import * as MapWorks from './js/MapWorks'
export default {
  name: 'GltfModel',
  mounted() {
    this.init();
  },
  methods:{
    init(){
      let container = document.getElementById("container");
      MapWorks.initMap(container)
      //创建模型点位
      let points = [
        {
          coords: [104.074822, 30.659807],
          style: {
            modelUrl: '/static/model/xiaofangche.gltf'
          }
        },
        {
          coords: [104.074722, 30.659807],
          style: {
            modelUrl: '/static/model/xiaofangyuan.glb',
          }
        }
      ];
      MapWorks.loadModels(points)
    }
  },

  beforeDestroy(){
    //实例被销毁前调用,页面关闭、路由跳转、v-if和改变key值
    MapWorks.destroy();
  }
}
</script>

<style lang="scss" scoped>
#container{
  width: 100%;
  height: 100%;
  background: rgba(7, 12, 19, 1);
  overflow: hidden;
  background-size: 40px 40px, 40px 40px;
  background-image: linear-gradient(hsla(0, 0%, 100%, 0.05) 1px, transparent 0), linear-gradient(90deg, hsla(0, 0%, 100%, 0.05) 1px, transparent 0);
}


</style>

5 运行结果

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

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

相关文章

数据定义和操作

一、数据定义语言和操作语言 1. 分析需求设计数据库表 根据“优乐网”系统需求&#xff1a; 此系统满足以下需求&#xff1a; 系统支持生成商品的入库和出库。入库之后的商品可以在平台显示 所有用户都可以浏览系统的商品信息&#xff0c;只有注册用户才能订购团购商品和服…

机械学习—零基础学习日志(python编程)

零基础为了学人工智能&#xff0c;正在艰苦的学习 昨天给高等数学的学习按下暂停键&#xff0c;现在开始学习python编程。 我学习的思路是直接去阿里云的AI学习课堂里面学习。 整体感觉&#xff0c;阿里云的AI课堂还是有一些乱&#xff0c;早期课程和新出内容没有更新和归档…

进阶学习------linux运维读写执行权限

进阶学习------linux运维读写执行权限 在UNIX和类UNIX操作系统中&#xff0c;文件权限是通过一组特定的数字来表示的&#xff0c;这些数字分为三组&#xff0c;分别对应于用户&#xff08;文件所有者&#xff09;、组和其他用户的权限。每组权限由三个二进制位表示&#xff0c;…

如何进行硬件调试?

硬件调试是硬件系统设计、开发和制造过程中不可或缺的一环&#xff0c;旨在对可能出现的问题进行分析和解决。以下是进行硬件调试的一般步骤和方法&#xff1a; 一、准备阶段 熟悉设计文档&#xff1a;在开始调试之前&#xff0c;需要详细阅读和理解硬件系统的设计文档、原理图…

【探索数据结构与算法】——深入了解双向链表(图文详解)

目录 一、双向链表的基本概念 ​​​ 二、双向链表的结构 三、双向链表的基本操作实现方法 1.双向链表的初始化 2.双向链表的头插 3.双向链表的尾插 6.查找节点 7.在指定位置之前插入节点 8.删除指定位置节点 9.打印链表数据 10.双向链表销毁 四、完整代码实现 …

html+css+js网页设计 星享咖啡6个页面(带js) ui还原度90%

htmlcssjs网页设计 星享咖啡6个页面&#xff08;带js&#xff09; ui还原度90% 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等…

一个拳打Claude3.5 Artifacts 脚踢软件外包公司的国产AI神器

Claude3.5的Artifacts功能想必大家都有所耳闻吧。 不了解的小伙伴也没关系&#xff0c;看下面这个视频。 更详细的介绍可以看之前我写的文章 关于Claude3.5-Sonnet引以为傲的功能&#xff0c;在半年前就被某国产平台无情碾压的那档事&#xff01;_claude 3.5 sonnet 国内能用…

20240808在飞凌OK3588-C开发板上使用HDMI OUT接口的SONY索尼8530机芯的YUV模式录像

20240808在飞凌OK3588-C开发板上使用HDMI OUT接口的SONY索尼8530机芯的YUV模式录像 2024/8/8 15:26 v4l2-ctl --list-devices v4l2-ctl --list-formats-ext -d /dev/video8 v4l2-ctl -V -d /dev/video8 根据规格书《FCB-CR8550_8530_TM_20190730.pdf》&#xff0c;很容易知道8…

【秋招突围】2024届校招-拼多多笔试题-第一套

🍭 大家好这里是 大厂笔试突围,一起备战秋招笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🌻 听说本周PDD的笔…

elasticSearch和ik分词插件安装和使用

elasticSearch 特点&#xff1a;分布式搜索和分析引擎&#xff0c;可以用http以json的方式进行数据索引。 由来&#xff1a;ES全称Elastic Stack&#xff08;ELK Stack&#xff09;&#xff0c;是由三个产品elasticSearch&#xff0c;logstack&#xff08;数据收集&#xff0…

【新手必备】5分钟学会Transformer算法的核心要点

Transformer 是近年来在自然语言处理&#xff08;NLP&#xff09;领域取得显著成果的一种深度学习模型&#xff0c;最初由 Vaswani et al. 在 2017 年提出。 与传统的序列模型&#xff08;如 RNN 和 LSTM&#xff09;相比&#xff0c;Transformer 的主要优势在于其能够更好地处…

零基础5分钟上手亚马逊云科技AWS核心云架构知识-用S3桶托管静态网页

简介&#xff1a; 小李哥从今天开始将开启全新亚马逊云科技AWS云计算知识学习系列&#xff0c;适用于任何无云计算或者亚马逊云科技技术背景的开发者&#xff0c;让大家0基础5分钟通过这篇文章就能完全学会亚马逊云科技一个经典的服务开发架构。 我将每天介绍一个基于亚马逊云…

go之protobuf和grpc

一、Protobuf Protobuf是接口规范的描述语言&#xff0c;可以通过工具生成代码&#xff0c;将结构化数据序列化。 二、grpc gRPC 是 Google 公司基于 Protobuf 开发的跨语言的开源 RPC 框架。 三、使用教程 3.1 student.proto syntax "proto3"; import "go…

软件测试---接口测试

一、接口及接口测试概念 &#xff08;1&#xff09;接口的类型 &#xff08;2&#xff09;接口测试的概念 &#xff08;3&#xff09;接口测试的原理 &#xff08;4&#xff09;接口测试的特点 &#xff08;5&#xff09;接口测试的实现方式 二、HTTP协议 &#xff08;1&#…

【人工智能】AI时代程序员----是缔造AI程序员,还是AI缔造程序员?

是缔造AI程序员&#xff0c;还是AI缔造程序员&#xff1f; 前言 随着AIGC&#xff08;如ChatGPT、MidJourney、Claude等&#xff09;大语言模型的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序员的工作方式正在发生深刻变革。 ​ ChatGPT ​ Midjourney ​ Claude …

【免费测试】人脸身份证比对接口如何用Java对接?(一)

一、什么是人脸身份证比对&#xff1f; 人脸身份证比对又称人证比对&#xff0c;实人比对&#xff0c;人像比对&#xff0c;输入姓名、身份证号码和头像照片&#xff0c;与公安库身份证头像进行权威比对&#xff0c;返回分值作为判断依据。 二、人脸身份证比对接口适用哪些场…

Processing之函数

一.函数基础 函数是processing程序中最基本的结构。经常使用的函数有draw()、line()、size()等。计算机每次运行程序的一行。当一个函数运行时&#xff0c;计算机跳到函数定义的位置&#xff0c;运行到哪里的代码&#xff0c;然后在跳回它离开的位置。 1.1掷骰子 案例代码如图…

离线安装windows应用商店中的应用

以windows notepad为例 1.百度windows应用商店&#xff0c;notepad 进入https://apps.microsoft.com/detail/9msmlrh6lzf3?hlen-aq&glAQ 是一个名为windows notepad的应用&#xff0c;复制地址栏里面detail后面的字符9msmlrh6lzf3 有很多notepad类似的应用&#xff0c;wi…

2024年新能源汽车充电桩建设驶入快车道

2024年新能源汽车市场大爆发&#xff1a;渗透率飙升&#xff0c;保有量创新高&#xff0c;充电桩建设驶入快车道 随着2024年新能源汽车市场的持续繁荣&#xff0c;一场前所未有的绿色革命正在全球范围内加速推进。这一年&#xff0c;新能源汽车的渗透率不仅实现了质的飞跃&…

CSS 实现两边固定宽,中间自适应

0. **Flexbox 实现**&#xff1a; css复制代码.container { display: flex; } ​ .fixed { width: 200px; /* 两边固定宽度 */ } ​ .flexible { flex: 1; /* 中间自适应 */ } html复制代码<div class…