- 测试环境:Microsoft Edge 120.0.2210.91
- three.js版本:0.160.0
- 其他:本篇文章中只探讨了无父对象下的移动与旋转,有父对象的情况将在下篇文章中讨论
- 新年快乐!
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
,达到在世界坐标系中移动的效果。在局部坐标系中移动时,由于物体本身可能会旋转,所以需要进行一定的转换,在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移动
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;
}
我们可以使用四元数来表示旋转,那么世界坐标系中的旋转公式为: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);
在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?=Qold?Qdelta?
注意公式里变化量在后
因此,局部坐标系下的旋转代码可以是:
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);
}