目录
- 说在前面
- 移动
- World Space
- Local Space
- 旋转
- World Space
- Local Space
- 代码
说在前面
- 测试环境:Microsoft Edge 120.0.2210.91
- three.js版本:0.160.0
- 其他:本篇文章中只探讨了无父对象下的移动与旋转,有父对象的情况将在下篇文章中讨论
- 新年快乐!
移动
World Space
- 在世界坐标系中移动时,直接对position进行运算即可
在three.js中,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;
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);}