You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在THREE.js中点击BoxGeometry的特定面?

获取Three.js BoxGeometry特定面坐标用于编程点击

嘿,我来帮你搞定这个问题!要获取BoxGeometry特定面(比如顶面、左右面)的坐标来实现编程点击,其实有两种靠谱的思路,我给你详细拆解下:

方法一:手动提取并识别特定面坐标(适合需要直接操作面数据的场景)

现代Three.js里的BoxGeometry是BufferGeometry类型,顶点数据存在attributes.position里,面的索引存在index属性中。我们可以一步步提取并转换坐标:

  1. 先把几何体的局部顶点数据提取出来
    先从BufferGeometry里把所有顶点的局部坐标拿出来,方便后续处理:

    const boxGeo = yourMesh.geometry;
    const positionAttr = boxGeo.attributes.position;
    const localVertices = [];
    for (let i = 0; i < positionAttr.count; i++) {
      localVertices.push(new THREE.Vector3(
        positionAttr.getX(i),
        positionAttr.getY(i),
        positionAttr.getZ(i)
      ));
    }
    
  2. 转换为世界坐标
    你已经会做世界坐标转换了,直接用mesh.matrixWorld把局部顶点转成世界坐标就行:

    const worldVertices = localVertices.map(v => v.clone().applyMatrix4(yourMesh.matrixWorld));
    
  3. 筛选特定面的顶点
    BoxGeometry的局部坐标中心在原点,所以每个大面的顶点都有一个坐标是固定的极值。比如顶面的所有顶点,局部y值等于boxGeo.parameters.height / 2(半高)。我们可以用这个特征筛选出目标面的顶点索引:

    const halfHeight = boxGeo.parameters.height / 2;
    const topVertexIndices = [];
    for (let i = 0; i < localVertices.length; i++) {
      // 加个小容错,避免浮点精度坑
      if (Math.abs(localVertices[i].y - halfHeight) < 0.001) {
        topVertexIndices.push(i);
      }
    }
    
  4. 找到对应的三角形面
    BoxGeometry的每个大面是由两个三角形组成的,我们遍历index数组里的面(每个面3个顶点索引),找出所有顶点都属于目标顶点组的面:

    const indexArr = boxGeo.index.array;
    const topFaces = [];
    for (let i = 0; i < indexArr.length; i += 3) {
      const idx1 = indexArr[i];
      const idx2 = indexArr[i+1];
      const idx3 = indexArr[i+2];
      if (topVertexIndices.includes(idx1) && topVertexIndices.includes(idx2) && topVertexIndices.includes(idx3)) {
        topFaces.push({
          vertices: [worldVertices[idx1], worldVertices[idx2], worldVertices[idx3]],
          // 可以顺便算出面中心,方便点击检测
          center: new THREE.Vector3().addVectors(worldVertices[idx1], worldVertices[idx2])
            .add(worldVertices[idx3]).divideScalar(3)
        });
      }
    }
    

    现在topFaces里就是顶面的两个三角形面的世界坐标和中心了,你可以用这些数据来做编程点击判断。

方法二:用Raycaster直接检测(更高效,推荐)

其实没必要手动提取面坐标,Three.js的Raycaster已经帮我们做好了面检测,返回的结果里直接包含命中的面,我们只需要判断这个面属于哪个大面就行:

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// 绑定鼠标点击事件
window.addEventListener('click', (e) => {
  // 把鼠标坐标转成Three.js需要的标准化设备坐标
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

  // 从相机发射射线
  raycaster.setFromCamera(mouse, yourCamera);
  const hits = raycaster.intersectObject(yourMesh);

  if (hits.length > 0) {
    const hitFace = hits[0].face;
    // 取面的第一个顶点的局部坐标来判断所属大面
    const vertex = new THREE.Vector3(
      yourMesh.geometry.attributes.position.getX(hitFace.a),
      yourMesh.geometry.attributes.position.getY(hitFace.a),
      yourMesh.geometry.attributes.position.getZ(hitFace.a)
    );
    const { width, height, depth } = yourMesh.geometry.parameters;
    const halfWidth = width / 2;
    const halfHeight = height / 2;
    const halfDepth = depth / 2;

    // 判断是哪个面
    if (Math.abs(vertex.y - halfHeight) < 0.001) {
      console.log('触发顶面点击!');
      // 这里写你的点击逻辑
    } else if (Math.abs(vertex.y + halfHeight) < 0.001) {
      console.log('触发底面点击!');
    } else if (Math.abs(vertex.x + halfWidth) < 0.001) {
      console.log('触发左面点击!');
    } else if (Math.abs(vertex.x - halfWidth) < 0.001) {
      console.log('触发右面点击!');
    }
    // 前后面对应z坐标的正负半深,逻辑同理
  }
});

一些要注意的坑

  • 浮点精度问题:比较坐标的时候一定要加个小的容错值(比如0.001),不然可能因为浮点计算的微小误差导致判断失败。
  • 变换兼容:如果你的Mesh有缩放、旋转,mesh.matrixWorld已经包含了所有变换,所以转换坐标的时候直接用它就没问题。
  • 旧版Geometry:如果你用的是很老的Three.js版本(r124及以前),BoxGeometry是Geometry类型,那更简单,直接遍历mesh.geometry.faces数组,每个Face3对象里有a/b/c三个顶点索引,判断逻辑和上面一致。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:25:43