You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Three.js中旋转立方体并追踪各面颜色位置实现方案咨询

当然可以用Three.js实现这个需求!

我帮你梳理下具体的实现思路,顺便解答你提到的几个属性疑问:

1. 先给立方体的每个面标记身份与颜色

要追踪面的位置,首先得给每个面一个唯一标识(比如“top”“front”“left”),并绑定对应的颜色代码(比如“g”“b”“r”)。

  • 如果你用的是新版Three.js(r125+),推荐用BufferGeometry(现在的标准几何体)。BoxGeometry默认会把每个面分成独立的group,你可以直接给每个groupuserData添加标识和颜色代码,这样就能把面和颜色关联起来。
  • 如果你用的是旧版Three.js(r124及以前)Geometry类的faces属性是可用的,每个Face3对象可以直接添加userData属性来存标识和颜色。

2. 追踪旋转后各面的位置

立方体旋转后,我们需要判断哪个面现在处于“上”“前”“左”的视角方向。核心思路是利用面的法向量
每个面都有一个默认的法向量(比如顶面的法向量是(0,1,0)),当立方体旋转后,我们把这个法向量转换到世界坐标系下,然后和目标方向(上:(0,1,0)、前:(0,0,-1)、左:(-1,0,0))做点积,点积最接近1的面就是当前处于该位置的面。

3. 实现getPosition()函数

这个函数的逻辑就是遍历所有面,通过上面的法向量判断方法,找到当前对应“上”“前”“左”的面,然后拼接它们的颜色代码即可。如果某个位置没有匹配的面(比如你的例子里的“左侧无对应颜色”),就返回约定的占位符(比如“n”)。

属性可用性解答

针对你提到的几个属性:

  • cube.material:完全可用!如果是数组形式的材质(比如MeshBasicMaterial数组),每个元素对应立方体的一个面(和BoxGeometrygroups顺序一一对应),你可以通过它获取或修改面的颜色、纹理等。
  • cube.geometry.vertices:仅在旧版Three.js的Geometry类中存在,新版已经移除了Geometry,改用BufferGeometryattributes.position来访问顶点数据(通过cube.geometry.attributes.position.array可以获取顶点的浮点数数组)。
  • cube.geometry.faces:同样只存在于旧版Geometry类中,新版BufferGeometryindex属性存储面的顶点索引,每个面由3个连续的索引值组成。不过如果只是为了标记面的信息,用groupsuserData会更方便。

简单代码示例

这里给你一个基于新版Three.js的实现片段:

// 创建立方体几何体与材质
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 材质顺序对应:右、左、上、下、前、后
const materials = [
  new THREE.MeshBasicMaterial({ color: 0xff0000 }), // 红色(右)
  new THREE.MeshBasicMaterial({ color: 0xff0000 }), // 红色(左)
  new THREE.MeshBasicMaterial({ color: 0x00ff00 }), // 绿色(上)
  new THREE.MeshBasicMaterial({ color: 0x0000ff }), // 蓝色(下)
  new THREE.MeshBasicMaterial({ color: 0x0000ff }), // 蓝色(前)
  new THREE.MeshBasicMaterial({ color: 0xffffff }), // 白色(后)
];

// 给每个面组添加标识和颜色代码
const faceInfos = [
  { id: 'right', code: 'r' },
  { id: 'left', code: 'r' },
  { id: 'top', code: 'g' },
  { id: 'bottom', code: 'b' },
  { id: 'front', code: 'b' },
  { id: 'back', code: 'w' },
];
geometry.groups.forEach((group, index) => {
  group.userData = faceInfos[index];
});

const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);

// 旋转立方体的函数
function rotateCube() {
  // 绕Y轴旋转90度(自身坐标系)
  cube.rotation.y += Math.PI / 2;
}

// 获取位置的函数
function getPosition() {
  const result = { top: 'n', front: 'n', left: 'n' };
  const topDir = new THREE.Vector3(0, 1, 0);
  const frontDir = new THREE.Vector3(0, 0, -1);
  const leftDir = new THREE.Vector3(-1, 0, 0);
  const tempNormal = new THREE.Vector3();

  geometry.groups.forEach(group => {
    const info = group.userData;
    // 初始化对应面的默认法向量
    switch(info.id) {
      case 'top': tempNormal.set(0,1,0); break;
      case 'front': tempNormal.set(0,0,-1); break;
      case 'left': tempNormal.set(-1,0,0); break;
      case 'right': tempNormal.set(1,0,0); break;
      case 'bottom': tempNormal.set(0,-1,0); break;
      case 'back': tempNormal.set(0,0,1); break;
    }
    // 将法向量转换到世界坐标系
    tempNormal.applyMatrix4(cube.matrixWorld).normalize();

    // 判断是否匹配目标方向
    if (tempNormal.dot(topDir) > 0.99) result.top = info.code;
    if (tempNormal.dot(frontDir) > 0.99) result.front = info.code;
    if (tempNormal.dot(leftDir) > 0.99) result.left = info.code;
  });

  return `${result.top}${result.front}${result.left}`;
}

内容的提问来源于stack exchange,提问作者JeffProd

火山引擎 最新活动