【瞎折腾/3D】无父物体下物体的旋转与移动

目录

  • 说在前面
  • 移动
    • World Space
    • Local Space
  • 旋转
    • World Space
    • Local Space
  • 代码

说在前面

  • 测试环境:Microsoft Edge 120.0.2210.91
  • three.js版本:0.160.0
  • 其他:本篇文章中只探讨了无父对象下的移动与旋转,有父对象的情况将在下篇文章中讨论
  • 新年快乐!

移动

World Space

  • 在世界坐标系中移动时,直接对position进行运算即可
    const translationSpeed = new THREE.Vector3(0.5, 0, 0);
    // delta 为时间间隔
    cubeB.position.x += translationSpeed.x*delta;
    cubeB.position.y += translationSpeed.y*delta;
    cubeB.position.z += translationSpeed.z*delta;
    
    在three.js中,position为局部坐标(local position)。当物体没有父物体时,局部坐标与世界坐标相等,所以此时可以直接操作position,达到在世界坐标系中移动的效果。
  • 在下面的例子中,我们让物体先让Z轴旋转60°,再沿着世界坐标轴X移动
    在这里插入图片描述

Local Space

  • 在局部坐标系中移动时,由于物体本身可能会旋转,所以需要进行一定的转换,在three.js中,我们可以使用translateX方法:

    translateOnAxis( axis, distance ) {// translate object by distance along axis in object space// axis is assumed to be normalized// 也就是应用了物体本身的旋转角度(local space)_v1.copy( axis ).applyQuaternion( this.quaternion );this.position.add( _v1.multiplyScalar( distance ) );return this;
    }translateX( distance ) {return this.translateOnAxis( _xAxis, distance );
    }
    

    所以我们可以这样写:

    const translationSpeed = new THREE.Vector3(0.5, 0, 0);cubeB.translateX(translationSpeed.x*delta);
    cubeB.translateY(translationSpeed.y*delta);
    cubeB.translateZ(translationSpeed.z*delta);
    
  • 在下面的例子中,我们让物体先让Z轴旋转60°,再沿着局部坐标轴X移动在这里插入图片描述

旋转

World Space

  • 在three.js中,旋转提供了方法供我们使用:
    rotateOnWorldAxis( axis, angle ) {// rotate object on axis in world space// axis is assumed to be normalized// method assumes no rotated parent_q1.setFromAxisAngle( axis, angle );this.quaternion.premultiply( _q1 );return this;
    }
    
    我们可以使用四元数来表示旋转,那么世界坐标系中的旋转公式为:
    Q n e w = Q d e l t a Q o l d Q_{new}=Q_{delta}Q_{old} Qnew=QdeltaQold
    注意公式里变化量在前
    故而,世界坐标系下的旋转代码如下:
    const rotationSpeed = new THREE.Vector3(0, Math.PI/3, 0);cubeB.rotateOnWorldAxis(_xAxis, rotationSpeed.x*delta);
    cubeB.rotateOnWorldAxis(_yAxis, rotationSpeed.y*delta);
    cubeB.rotateOnWorldAxis(_zAxis, rotationSpeed.z*delta);
    
  • 在下面的例子中,我们让物体先让Z轴旋转60°,再绕着世界坐标轴Y旋转
    在这里插入图片描述

Local Space

  • 在three.js中,旋转提供了方法供我们局部坐标轴旋转:

    rotateOnAxis( axis, angle ) {// rotate object on axis in object space// axis is assumed to be normalized_q1.setFromAxisAngle( axis, angle );this.quaternion.multiply( _q1 );return this;
    }
    

    同样,我们可以使用四元数来表示旋转,局部坐标系中的旋转公式为:
    Q n e w = Q o l d Q d e l t a Q_{new}=Q_{old}Q_{delta} Qnew=QoldQdelta
    注意公式里变化量在后
    因此,局部坐标系下的旋转代码可以是:

    const rotationSpeed = new THREE.Vector3(0, Math.PI/3, 0);cubeB.rotateX(rotationSpeed.x*delta);
    cubeB.rotateY(rotationSpeed.y*delta);
    cubeB.rotateZ(rotationSpeed.z*delta);
    
  • 在下面的例子中,我们让物体先让Z轴旋转60°,再绕着局部坐标轴Y旋转
    在这里插入图片描述

代码

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';let camera, scene, renderer;scene = new THREE.Scene();renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(15, 20, 30);
scene.add(camera);// controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 20;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;// ambient light
scene.add(new THREE.AmbientLight(0x666666));// point light
const light = new THREE.PointLight(0xffffff, 3, 0, 0);
camera.add(light);// helper
scene.add(new THREE.AxesHelper(20));// textures
const loader = new THREE.TextureLoader();
const texture = loader.load('textures/sprites/disc.png');
texture.colorSpace = THREE.SRGBColorSpace;// CubeA
const meshA = new THREE.BoxGeometry(1, 1, 1);
const mateA = new THREE.MeshNormalMaterial();
const cubeA = new THREE.Mesh(meshA, mateA);
scene.add(cubeA);// CubeB
const meshB = new THREE.BoxGeometry(1, 1, 1);
const mateB = new THREE.MeshNormalMaterial();
const cubeB = new THREE.Mesh(meshA, mateA);
scene.add(cubeB);cubeB.position.x = 2;
cubeB.rotateZ(Math.PI/3);
cubeB.add(new THREE.AxesHelper(4));window.addEventListener('resize', onWindowResize);const translationSpeed = new THREE.Vector3(0.5, 0, 0);
const rotationSpeed = new THREE.Vector3(0, Math.PI/3, 0);let preTime = Date.now();
let curTime = preTime;
let delta;const _xAxis = /*@__PURE__*/ new THREE.Vector3( 1, 0, 0 );
const _yAxis = /*@__PURE__*/ new THREE.Vector3( 0, 1, 0 );
const _zAxis = /*@__PURE__*/ new THREE.Vector3( 0, 0, 1 );animate();function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}function animate() {requestAnimationFrame(animate);curTime = Date.now();delta = (curTime - preTime)/1000;preTime = curTime;// cubeB.position.x += translationSpeed.x*delta;// cubeB.position.y += translationSpeed.y*delta;// cubeB.position.z += translationSpeed.z*delta;cubeB.translateX(translationSpeed.x*delta);cubeB.translateY(translationSpeed.y*delta);cubeB.translateZ(translationSpeed.z*delta);// cubeB.rotateX(rotationSpeed.x*delta);// cubeB.rotateY(rotationSpeed.y*delta);// cubeB.rotateZ(rotationSpeed.z*delta);cubeB.rotateOnWorldAxis(_xAxis, rotationSpeed.x*delta);cubeB.rotateOnWorldAxis(_yAxis, rotationSpeed.y*delta);cubeB.rotateOnWorldAxis(_zAxis, rotationSpeed.z*delta);render();}function render() {renderer.render(scene, camera);}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打破数据孤岛:ChatGPT如何打通金融大数据的任督二脉?

文章目录 一、引言二、ChatGPT与金融大数据分析的融合三、实践应用:ChatGPT在金融大数据分析中的优势与挑战四、案例分析:ChatGPT在金融大数据分析中的应用案例五、前景展望:ChatGPT在金融大数据分析领域的未来发展《AI时代Python金融大数据分…

ShuffleNet V2:高效CNN架构设计实用指南

摘要 目前,神经网络架构设计主要以计算复杂度的间接指标(即 FLOPs)为指导。然而,直接指标(如速度)还取决于其他因素,如内存访问成本和平台特性。因此,这项工作建议在目标平台上评估…

浅析锂电池保护板(BMS)系统设计思路(四)SOC算法-扩展Kalman滤波算法

1 SOC估算方法介绍 电池SOC的估算是电池管理系统的核心,自从动力电池出现以来,各种各样的电池SOC估算方法不断出现。随着电池管理系统的逐渐升级,电池SOC估算方法的效率与精度不断提高,下面将介绍常用几种电池SOC估算方法[1]&…

代码随想录-刷题第四十三天

1049. 最后一块石头的重量 II 题目链接:1049. 最后一块石头的重量 II 思路:本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成0-1背包问题了。与416. 分割等和子集非常相似。 动态规划五步曲&…

域传送漏洞

DNS解析 当用户访问域名时浏览器解析首先会查看浏览器缓存是否有对应的ip,如果没有则会到本地host文件中查看是否有对应的ip,如果没用则会将域名发送给本地区的DNS服务器. DNS服务器分为递归服务器,根服务器,权威服务器 首先是递…

C++初阶——基础知识(内联函数)

目录 1.内联函数 内联函数的示例代码 1.内联函数 是一种 C 中的函数定义方式,它告诉编译器在每个调用点上插入函数体的副本,而不是像普通函数那样在调用时跳转到函数体所在的地址执行。这样可以减少函数调用的开销,提高程序的执行效率。 …

0101包冲突导致安装docker失败-docker-云原生

文章目录 1 前言2 报错3 解决结语 1 前言 最近在学习k8s,前置条件就是要安装指定版本的docker,命令如下 yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.62 报错 file /usr/libexec/docker/cli-plugins/docker-buildx fr…

git解决冲突场景

文章目录 git解决冲突场景 git解决冲突场景 假设我们在公司开发了一个功能修改了一个文件 我们现在模拟修改文件之后提交一个版本到本地,但是不上传到远程仓库 假设我们现在回到家开发代码,需要拉去最新的代码 提示已经更新。根本没有最新的代码改动&am…

【超图】SuperMap iClient3D for WebGL/WebGPU —— 坐标系位置 —— Cartesian2

作者:taco 说到关于地理必然逃不开位置的关系。借用百度百科的内容来说地理学(geography),是研究地球表层空间地理要素或者地理综合体空间分布规律、时间演变过程和区域特征的一门学科。所以位置&坐标系必然逃不掉了。那么在S…

Cisco模拟器-OSPF路由协议

设计要求用两台双口路由器连接不同IP网段的计算机,并使用OSFP协议发现路由表使不同IP网段的计算机可以相互通信。 通过设计,可以连通IP地址网段不同的局域网,可应用在园区网的互连和互通的实现上。 主要配置步骤 路由器0: Router…

C# WPF上位机开发(以始为终,寻找真实的上位机需求)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 c# wpf、qt、mfc这些上位机的需求是真实存在的,在现实中有很多应用的地方,这一点大家都很清楚。而程序员本身呢&#xff0c…

OpenGauss 之索引查找和匹配

一. 前言 本文主要通过走读OpenGuass的代码,来了解查询的时候OpenGuass是如何查找表的索引信息以及根据谓词条件过滤掉无用的索引信息的。 二. 索引路径匹配流程 1. 首先OpenGuass在build_simple_rel的时候,首先将一个表以及与他相关的索引都加到rel-&g…