Three.js的坐标系系统是理解3D场景和对象位置的关键。以下是详细解析:
Three.js使用右手坐标系:
X轴:水平向右(正方向)
Y轴:垂直向上(正方向)
Z轴:垂直于屏幕向外(正方向)
text
Y
↑
|
|
+----→ X
/
/
Z(指向观察者)
场景的绝对坐标系
scene.position 在世界坐标系中
所有对象的最终位置都转换到世界坐标系进行渲染
每个对象都有自己的局部坐标系
相对于父对象的坐标系
对象的position、rotation、scale都是相对于父对象
javascript
// 在父对象的局部坐标系中设置位置
mesh.position.set(1, 2, 3);
// 获取世界坐标
const worldPosition = new THREE.Vector3();
mesh.getWorldPosition(worldPosition);
使用弧度制
顺序:Z → Y → X(默认)
可以使用欧拉角或四元数
javascript
// 方法1:欧拉角(弧度)
mesh.rotation.set(0, Math.PI/2, 0); // 绕Y轴旋转90度
// 方法2:使用度转弧度
mesh.rotation.y = THREE.MathUtils.degToRad(90);
// 方法3:四元数(避免万向节锁)
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/2);
mesh.setRotationFromQuaternion(quaternion);
每个对象都有三个矩阵:
javascript
// 局部变换矩阵
mesh.matrix; // 相对于父对象
mesh.matrixWorld; // 世界变换矩阵
// 手动更新矩阵
mesh.updateMatrix(); // 更新局部矩阵
mesh.updateMatrixWorld(); // 更新世界矩阵
javascript
const localPoint = new THREE.Vector3(1, 0, 0);
// 局部坐标 → 世界坐标
const worldPoint = localPoint.applyMatrix4(mesh.matrixWorld);
// 世界坐标 → 局部坐标
const localPointAgain = worldPoint.applyMatrix4(
new THREE.Matrix4().copy(mesh.matrixWorld).invert()
);
// 使用内置方法
mesh.localToWorld(localPoint);
mesh.worldToLocal(worldPoint);
javascript
// 屏幕坐标 → 世界坐标(射线)
const mouse = new THREE.Vector2(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
// 世界坐标 → 屏幕坐标
const vector = new THREE.Vector3(10, 20, 30);
vector.project(camera); // 归一化设备坐标
javascript
function addCoordinateAxes(scene, size = 1) {
// X轴(红色)
const xAxis = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, 0, 0),
size,
0xff0000
);
// Y轴(绿色)
const yAxis = new THREE.ArrowHelper(
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 0, 0),
size,
0x00ff00
);
// Z轴(蓝色)
const zAxis = new THREE.ArrowHelper(
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(0, 0, 0),
size,
0x0000ff
);
scene.add(xAxis, yAxis, zAxis);
}
javascript
// 创建父对象
const parent = new THREE.Group();
parent.position.set(5, 0, 0);
// 创建子对象(相对于父对象)
const child = new THREE.Mesh(geometry, material);
child.position.set(0, 3, 0); // 在父对象的局部坐标系中
parent.add(child);
// child的世界坐标 = parent的世界坐标 + child的局部坐标
scene.add(parent);
javascript
// 使对象面向某个点
mesh.lookAt(target.position);
// 使对象面向相机
mesh.lookAt(camera.position);
// 对齐到世界坐标系
mesh.up.set(0, 1, 0); // 设置"上"方向为世界Y轴
javascript
// 1. 批量更新矩阵
scene.traverse((obj) => {
if (obj.isMesh) {
obj.updateMatrix();
obj.updateMatrixWorld();
}
});
// 2. 矩阵自动更新(默认true,可关闭优化性能)
mesh.matrixAutoUpdate = false;
// 需要手动调用 updateMatrix() 当变换改变时
javascript
// 显示坐标系
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 显示包围盒
const bboxHelper = new THREE.BoxHelper(mesh, 0xffff00);
scene.add(bboxHelper);
理解坐标系层级:始终清楚当前操作的是局部坐标还是世界坐标
使用正确的转换方法:避免手动计算,使用Three.js提供的转换方法
注意更新顺序:先设置变换属性,再更新矩阵
使用辅助工具:利用AxesHelper、GridHelper等辅助理解坐标系
考虑性能:在需要性能的场景中,合理控制矩阵更新频率
掌握Three.js的坐标系系统是创建复杂3D交互的基础,建议通过实际项目中的坐标转换和对象操作来加深理解。