目录
- 说在前面
- 移动
-
- [World Space](#World Space)
- [Local Space](#Local Space)
- 旋转
-
- [World Space](#World Space)
- [Local Space](#Local Space)
- 代码
说在前面
- 测试环境:Microsoft Edge 120.0.2210.91
- three.js版本:0.160.0
- 其他:本篇文章中只探讨了无父对象下的移动与旋转,有父对象的情况将在下篇文章中讨论
- 新年快乐!
移动
World Space
-
在世界坐标系中移动时,直接对position进行运算即可
javascriptconst 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
方法:javascripttranslateOnAxis( 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 ); }
所以我们可以这样写:
javascriptconst 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中,旋转提供了方法供我们使用:
jsrotateOnWorldAxis( 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
注意公式里变化量在前
故而,世界坐标系下的旋转代码如下:jsconst 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中,旋转提供了方法供我们局部坐标轴旋转:
jsrotateOnAxis( 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
注意公式里变化量在后因此,局部坐标系下的旋转代码可以是:
jsconst 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);
}