Three.js的坐标系系统是理解3D场景和对象位置的关键。以下是详细解析:

1. 坐标系基础

 右手坐标系

Three.js使用右手坐标系

text

     Y
     ↑
     |
     |
     +----→ X
    /
   /
 Z(指向观察者)

2. 坐标系层级

世界坐标系(World Space)

局部坐标系(Local Space)

3. 核心概念

位置(Position)

javascript

// 在父对象的局部坐标系中设置位置
mesh.position.set(1, 2, 3);

// 获取世界坐标
const worldPosition = new THREE.Vector3();
mesh.getWorldPosition(worldPosition);

旋转(Rotation)

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();   // 更新世界矩阵

4. 坐标系转换

坐标转换方法

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); // 归一化设备坐标

5. 实用示例

坐标系可视化

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);

6. 常见问题与技巧

坐标系对齐

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);

7. 最佳实践

  1. 理解坐标系层级:始终清楚当前操作的是局部坐标还是世界坐标

  2. 使用正确的转换方法:避免手动计算,使用Three.js提供的转换方法

  3. 注意更新顺序:先设置变换属性,再更新矩阵

  4. 使用辅助工具:利用AxesHelper、GridHelper等辅助理解坐标系

  5. 考虑性能:在需要性能的场景中,合理控制矩阵更新频率

掌握Three.js的坐标系系统是创建复杂3D交互的基础,建议通过实际项目中的坐标转换和对象操作来加深理解。