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

目录

说在前面

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

移动

World Space

  • 在世界坐标系中移动时,直接对position进行运算即可

    javascript 复制代码
    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方法:

    javascript 复制代码
    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 );
    }

    所以我们可以这样写:

    javascript 复制代码
    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中,旋转提供了方法供我们使用:

    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
    注意公式里变化量在前
    故而,世界坐标系下的旋转代码如下:

    js 复制代码
    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中,旋转提供了方法供我们局部坐标轴旋转:

    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
    注意公式里变化量在后

    因此,局部坐标系下的旋转代码可以是:

    js 复制代码
    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旋转

代码

js 复制代码
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);

}
相关推荐
Hao_Harrision1 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| ThreeDBackgroundBoxes(3D背景盒子组件)
前端·3d·typescript·react·tailwindcss·vite7
AI浩1 天前
LabelAny3D: Label Any Object 3D in the Wild
3d
学無芷境1 天前
vesselFM: A Foundation Model for Universal 3D Blood Vessel Segmentation
3d
CG_MAGIC2 天前
云渲染时能否关机或断网?
3d·云渲染·建模教程·渲云渲染·3d软件
拾荒的小海螺2 天前
开源项目:Three.js 构建 3D 世界的工具库
javascript·3d·开源
gihigo19982 天前
使用MATLAB绘制3D心形图和玫瑰花图案
开发语言·matlab·3d
zl_vslam2 天前
SLAM中的非线性优-3D图优化之地平面约束(十五)
人工智能·算法·计算机视觉·3d
STCNXPARM2 天前
Android14显示系统 - 开源图形库Mesa3d
3d·开源·mesa3d·android图形库·opengl-es
杀生丸学AI2 天前
【平面重建】3D高斯平面:混合2D/3D光场重建(NeurIPS2025)
人工智能·平面·3d·大模型·aigc·高斯泼溅·空间智能
threelab2 天前
Merge3D 三维引擎中 GeoJSON 数据加载的整体设计
android·3d