Cesium设置模型朝向速度矢量方向

news2024/12/25 14:24:14

Cesium设置模型朝向速度矢量方向

文章目录

  • Cesium设置模型朝向速度矢量方向
    • 1. 需求场景
    • 2. 技术路线
      • 2.1 VelocityOrientationProperty
      • 2.2 VelocityVectorProperty
    • 3. 参考链接

1. 需求场景

现有一段飞机起飞、爬升的轨迹数据,需要在Cesium中模拟出飞行过程动画,要求飞机模型的姿态随着速度矢量方向变化,而不是一直保持飞机模型的原始状态。

2. 技术路线

Cesium.Entity类中有属性orientation 可以用来控制实体模型model的朝向,当不设置该属性时,模型就保持原始状态。如下图所示:

原始状态

根据需求,飞机模型应该向上仰起来,有两种方式可以达到目标。

2.1 VelocityOrientationProperty

Cesium提供了VelocityOrientationProperty类,通过该类可以直接设置实体的orientation属性,其内部会自动计算速度矢量,设置后飞机模型就会沿着速度矢量的方向,官方文档示例代码:

// Create an entity with position and orientation.
var position = new Cesium.SampledProperty();
position.addSamples(...);
var entity = viewer.entities.add({
  position : position,
  orientation : new Cesium.VelocityOrientationProperty(position)
}));

实际应用时示例代码:

entity.orientation = new Cesium.VelocityOrientationProperty(entity.position);

效果如下图:

朝向调整后

2.2 VelocityVectorProperty

第一种方式基本就可以解决问题,但是有一种情况:三维模型本身有问题,有些三维模型从其他格式转换过来,在导入到Cesium后会发现有翻转、角度偏移等现象,需要在上一步的基础上(先将模型变换到速度矢量方向),再进行一些模型旋转变换。

通过VelocityVectorProperty可以计算出速度矢量,通过速度矢量、要沿参考轴旋转的角度(heading、pitch、rool)就可以计算出最终的朝向四元数(quaternion),将该四元数设置给实体的orientation属性即可。

核心代码如下:

    /**
     * 计算朝向四元数
     * X轴正向指向运动方向;Y轴在水平面内垂直于X轴,正向指向右侧;Z轴通过右手法则确定
     * @param {Cartesian3} position 位置
     * @param {Cartesian3} velocity 速度向量
     * @param {*} rotateX 绕X轴旋转的角度(roll)
     * @param {*} rotateY 绕Y轴旋转的角度(pitch)
     * @param {*} rotateZ 绕Z轴旋转的角度(heading)
     * @returns 
     */
    function getQuaternion(position, velocity, rotateX, rotateY, rotateZ) {
      // 1、计算站心到模型坐标系的旋转平移矩阵
      // 速度归一化
      let normal = Cesium.Cartesian3.normalize(velocity, new Cesium.Cartesian3());
      // 计算模型坐标系的旋转矩阵
      let satRotationMatrix = Cesium.Transforms.rotationMatrixFromPositionVelocity(position, normal, Cesium.Ellipsoid.WGS84);
      // 模型坐标系到地固坐标系旋转平移矩阵
      let m = Cesium.Matrix4.fromRotationTranslation(satRotationMatrix, position);
      // 站心坐标系(东北天坐标系)到地固坐标系旋转平移矩阵
      var m1 = Cesium.Transforms.eastNorthUpToFixedFrame(position, Cesium.Ellipsoid.WGS84, new Cesium.Matrix4());
      // 站心到模型坐标系的旋转平移矩阵
      let m3 = Cesium.Matrix4.multiply(Cesium.Matrix4.inverse(m1, new Cesium.Matrix4()), m, new Cesium.Matrix4());

      // 2、模型姿态旋转矩阵
      rotateX = rotateX || 0;
      rotateY = rotateY || 0;
      rotateZ = rotateZ || 0;
      let heading = rotateZ, pitch = rotateY, roll = rotateX;
      let postureHpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading), Cesium.Math.toRadians(pitch), Cesium.Math.toRadians(roll));
      let postureMatrix = Cesium.Matrix3.fromHeadingPitchRoll(postureHpr);

      // 3、最终的旋转矩阵
      let mat3 = Cesium.Matrix4.getMatrix3(m3, new Cesium.Matrix3());
      let finalMatrix = Cesium.Matrix3.multiply(mat3, postureMatrix, new Cesium.Matrix3());
      let quaternion1 = Cesium.Quaternion.fromRotationMatrix(finalMatrix);
      let hpr = Cesium.HeadingPitchRoll.fromQuaternion(quaternion1);
      let q2 = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
      return q2;
    }

控制台测试代码:

        // 当前时刻速度向量、位置
        let curVelocityVector = entity.velocityVector.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
        let curPosition = entity.position.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
        // 计算朝向四元数
        var quaternion = getQuaternion(curPosition, curVelocityVector);
        // 设置实体朝向,验证是否指向速度矢量方向
        entity.orientation = quaternion;

实际应用代码:

var viewer, entity;
    function startup(Cesium) {
      "use strict";
      //Sandcastle_Begin
      viewer = new Cesium.Viewer("cesiumContainer");
      var scene = viewer.scene;

      // Cesium查看器
      viewer.extend(Cesium.viewerCesiumInspectorMixin);

      // CZML中的orientation并不考虑速度矢量方向
      let dataSourcePromise = Cesium.CzmlDataSource.load("../../SampleData/CZML/Aircraft2.czml");
      dataSourcePromise.then(function (dataSource) {
        viewer.dataSources.add(dataSource);

        // 获取实体
        entity = viewer.dataSources.getByName("1610994859816914946")[0].entities.getById("1610994859816914946");

        // 添加属性:速度向量
        entity.velocityVector = new Cesium.VelocityVectorProperty(entity.position, true);

        /* // 当前时刻速度向量、位置
        let curVelocityVector = entity.velocityVector.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
        let curPosition = entity.position.getValue(viewer.clock.currentTime, new Cesium.Cartesian3());
        // 计算朝向四元数
        var quaternion = getQuaternion(curPosition, curVelocityVector);
        // 设置实体朝向,验证是否指向速度矢量方向
        entity.orientation = quaternion; */


        let rotateX = 0;
        let rotateY = 0;
        let rotateZ = 0;
        var property = new Cesium.SampledProperty(Cesium.Quaternion);
        if (entity.position instanceof Cesium.CompositePositionProperty) {
          let intervals = entity.position.intervals;
          for (let i = 0; i < intervals.length; i++) {
            const interval = intervals.get(i);
            let positions = interval.data._property._values;
            interval.data._property._times.forEach((time, index) => {

              let curVelocityVector = entity.velocityVector.getValue(time, new Cesium.Cartesian3());
              let curPosition = entity.position.getValue(time, new Cesium.Cartesian3());
              // 计算朝向四元数
              var quaternion = getQuaternion(curPosition, curVelocityVector, rotateX, rotateY, rotateZ);
              // 添加采样值
              property.addSample(time, quaternion);
            });
          }
        }

        // 将转换后的四元数设置给实体
        entity.orientation = property;
      })


      Sandcastle.finishedLoading();
    }
    if (typeof Cesium !== "undefined") {
      window.startupCalled = true;
      startup(Cesium);
    }

    /**
     * 计算朝向四元数
     * X轴正向指向运动方向;Y轴在水平面内垂直于X轴,正向指向右侧;Z轴通过右手法则确定
     * @param {Cartesian3} position 位置
     * @param {Cartesian3} velocity 速度向量
     * @param {*} rotateX 绕X轴旋转的角度(roll)
     * @param {*} rotateY 绕Y轴旋转的角度(pitch)
     * @param {*} rotateZ 绕Z轴旋转的角度(heading)
     * @returns 
     */
    function getQuaternion(position, velocity, rotateX, rotateY, rotateZ) {
      // 1、计算站心到模型坐标系的旋转平移矩阵
      // 速度归一化
      let normal = Cesium.Cartesian3.normalize(velocity, new Cesium.Cartesian3());
      // 计算模型坐标系的旋转矩阵
      let satRotationMatrix = Cesium.Transforms.rotationMatrixFromPositionVelocity(position, normal, Cesium.Ellipsoid.WGS84);
      // 模型坐标系到地固坐标系旋转平移矩阵
      let m = Cesium.Matrix4.fromRotationTranslation(satRotationMatrix, position);
      // 站心坐标系(东北天坐标系)到地固坐标系旋转平移矩阵
      var m1 = Cesium.Transforms.eastNorthUpToFixedFrame(position, Cesium.Ellipsoid.WGS84, new Cesium.Matrix4());
      // 站心到模型坐标系的旋转平移矩阵
      let m3 = Cesium.Matrix4.multiply(Cesium.Matrix4.inverse(m1, new Cesium.Matrix4()), m, new Cesium.Matrix4());

      // 2、模型姿态旋转矩阵
      rotateX = rotateX || 0;
      rotateY = rotateY || 0;
      rotateZ = rotateZ || 0;
      let heading = rotateZ, pitch = rotateY, roll = rotateX;
      let postureHpr = new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(heading), Cesium.Math.toRadians(pitch), Cesium.Math.toRadians(roll));
      let postureMatrix = Cesium.Matrix3.fromHeadingPitchRoll(postureHpr);

      // 3、最终的旋转矩阵
      let mat3 = Cesium.Matrix4.getMatrix3(m3, new Cesium.Matrix3());
      let finalMatrix = Cesium.Matrix3.multiply(mat3, postureMatrix, new Cesium.Matrix3());
      let quaternion1 = Cesium.Quaternion.fromRotationMatrix(finalMatrix);
      let hpr = Cesium.HeadingPitchRoll.fromQuaternion(quaternion1);
      let q2 = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
      return q2;
    }

3. 参考链接

[1]. 【Cesium】计算模型的朝向四元数,实现模型运动中调整朝向

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

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

相关文章

Lesson 3. 线性回归的手动实现(3.3 线性回归手动实现与模型局限 3.4 机器学习模型结果可信度理论与交叉验证基础)

文章目录一、线性回归手动实现与模型局限1. 线性回归的手动实现2. 线性回归模型局限3. 线性回归的决定系数二、机器学习模型结果可信度理论与交叉验证基础1. 机器学习模型结果可信度理论基础与数据集划分1.1 机器学习模型结果可信度基础理论1.2 数据集切分方法1.3 线性回归手动…

图的关键路径(AOE网络)

文章目录AOE网概念性质研究的问题关键路径概念求解的方法注意事项AOE网 概念 用顶点表示事件, 边弧表示活动, 边弧上的权值表示活动持续的时间, 这样的带权有向无环图叫AOE网. AOE网常用于估算工程完成时间. AOE网和AOV网都是有向无环图, 不同之处在于它们的边和顶点所代表的…

【青训营】Go的高质量编程

Go的高质量编程 本文内容总结自字节跳动青年训练营 第五届 后端组 什么是高质量&#xff1f; 各种边界条件是否完备异常情况能正常处理&#xff0c;稳定性有保障易读易维护 Go语言开发者Dave Cheney指出&#xff0c;编程需要遵循以下原则&#xff1a; 简单性 消除多余的复…

Docker安装和卸载教程

1、安装前先卸载系统上原有的Docker sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine2、安装需要的yum-utils包 需要安装 yum-utils包&#xff08;该包提供了 yum-c…

研一寒假C++复习笔记--程序的内存模型

目录 1--内存分区模型 2--代码区 3--全局区 4--栈区 5--堆区 6--new操作符 1--内存分区模型 执行C程序时&#xff0c;内存可划分为4个区域&#xff0c;不同区域存放的数据&#xff0c;具有不同的生命周期&#xff1b; ① 代码区&#xff1a;存放函数的二进制代码&#x…

nvcc 编译并行程序时报错gcc: error trying to exec ‘cc1plus‘: execvp: 没有那个文件或目录

一、nvcc 编译程序时报错 gcc: error trying to exec cc1plus: execvp: 没有那个文件或目录 cc1plus 是gcc编译工具链中用到的一个程序 报错非常直白&#xff0c;就是说gcc在执行时找不到这个程序 二、问题解决方法 1.确定是否正确安装gcc、g 如果系统中没有正确安装gcc…

elementUI组件下拉框属性的一些配合使用

文章目录1、单选带搜索功能&#xff08;自定义搜索&#xff09;2、下拉多选远程搜索3、下拉多选有默认选项4、下拉多选默认禁用只提供大致思路&#xff0c;大量玩法等你开发【doge】 1、单选带搜索功能&#xff08;自定义搜索&#xff09; 例如&#xff0c;我需要实现一个功能…

关于学习的一些建议

本文的主要讲的内容如图&#x1f447; 文末附有整个Java的学习路线、能力提升和学习建议的思维导图。 快过新年了&#xff01;在过去的一年里&#xff0c;你们收获了什么呢&#xff1f;在未来的一年里&#xff0c;你们有什么新目标吗&#xff1f; 1.定目标 过去的我往往喜欢…

初识 Django

初识 Django参考描述优劣 Django&#xff08;部分&#xff09;优势功能完备&#xff0c;开箱即用开发效率高Admin 管理后台安全ORM可扩展劣势性能模板过渡封装获取长期支持版&#xff08;Long Time Support&#xff0c;LTS&#xff09;PIP官网获取检测MVC 与 MVTMVC 架构MTV 架…

力扣sql基础篇(十一)

力扣sql基础篇(十一) 1 每件商品的最新订单 1.1 题目内容 1.1.1 基本题目信息1 1.1.2 基本题目信息2 1.1.3 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 SELECT p.product_name,o1.product_id,o1.order_id,o1.order_date FROM Orders o1 INNER JOIN ( SELECT pro…

Android自定义绘制1-1 Plus

上一个文章是看着扔物线的视频写的。写玩之后&#xff0c;发现他的文章还有很多内容。尴尬。 接着写呗。 自定义绘制知识的4个级别 1.Canvas的drawXXX()系列方法以及Paint类的一些常见方法。 canvas的drawXXX()是自定义绘制的最基本操作。掌握了这些方法&#xff0c;才知道…

JNPF 3.4.5 java+.Net6 旗舰版企业版 简搭-敏捷业务低代码开发平台

JNPF 低代码通常是指APaaS产品&#xff0c;通过为开发者提供可视化的应用开发环境&#xff0c;降低或去除应用开发对原生代码编写的需求量&#xff0c;进而实现便捷构建应用程序的一种解决方案。广义上低代码概念涵盖所有能够完成代码的集成&#xff0c;减少代码开发的应用过程…

在哔站黑马程序员学习Spring—Spring Framework—(二)spring的注解开发学习笔记

一、注解开发spring的强项—>简化开发spring从2.0开始逐步提供了各种各样的注解&#xff0c;到了2.5注解比较完善&#xff0c;到了3.0推出了纯注解开发,使用java类替代配置文件&#xff0c;开启了spring快速开发通道。二、注解开发定义bean不再需要在spring配置文件中定义be…

拷贝实体的工具类---BeanObjectCopyUtils

目录 前言&#xff1a; 第一步&#xff1a;引用的核心类&#xff1a; 第一种&#xff1a;单个实体的拷贝方法 第二种&#xff1a;列表类的实体拷贝方法 第二步&#xff1a;核心方法的介绍&#xff1a; 核心方法一&#xff0c;介绍了实例化的操作&#xff1a; 核心方法二…

CMMI之项目管理类

项目管理类过程域涵盖了与项目的计划、监督和控制相关的项目管理活动。CMMI-DEV 中的七个项目管理类过程域是&#xff1a;• 集成项目管理&#xff08;Integrated Project Management&#xff0c; IPM&#xff09;• 项目监督与控制&#xff08;Project Monitoring and Control…

蓝桥杯:城邦

题目链接 问题描述 答案提交 本题答案是&#xff1a;4046。 运行限制 思路分析 代码&#xff08;Java&#xff09; 问题描述 小蓝国是一个水上王国, 有 2021 个城邦, 依次编号 1 到 2021。在任意两 个城邦之间, 都有一座桥直接连接。 为了庆祝小蓝国的传统节日, 小蓝国政府…

登录授权方案:JSON Web Tokens (JWT)

登录授权方案&#xff1a;JSON Web Tokens (JWT) JWT官方文档&#xff1a;https://jwt.io/introduction 1.简介&#xff1a; JWT 即 json web tokens&#xff0c;通过JSON形式作为Web应用中的令牌&#xff0c;用于在各方之间安全地将信息作为JSON对象传输&#xff0c;在数据传…

【博客598】从netfilter hook执行原理分析iptables为什么自定义链无法主动调用只能从其它链跳转过来

从netfilter hook执行原理分析iptables为什么自定义链无法主动调用只能从其它链跳转过来 1、netfilter hook执行原理 netfilter 框架是 Linux 网络子系统里的一个核心模块&#xff0c;iptables 就是基于 netfilter 框架实现的一个网络包处理工具。 netfilter hook被调用后执行…

2023年“科学探索奖”申报启动及指南

2023年“科学探索奖”资助不超过50位青年科学家&#xff0c;每位获奖人5年内将获得总计300万元奖金。今年的申报时间为2023年1月1日至3月15日。获奖名单拟定于2023年8月揭晓。本文知识人网小编整理该奖项的概要及申报指南&#xff0c;以飨读者。“科学探索奖”是一项由科学家主…

【高并发内存池】第二弹之threadcache 线程缓存

1.为什么线程向threadcache申请内存不需要加锁&#xff1f; 因为没有把cache设置成全局变量&#xff0c;而是使用了TLS(线程局部存储)&#xff0c;作用是当前内存只可当前线程见到&#xff0c;其他线程没有使用权&#xff0c;避免了加锁的操作&#xff0c;保证了线程数据的独立…