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

如何获取THREE.js Geometry特定面及面中心的屏幕空间坐标?

我来帮你解决Three.js里获取特定面/组合面屏幕坐标的问题!不管是单个Face的中心,还是像Box顶部这种由两个Face组成的组合面,咱们都能一步步实现:

一、先搞懂核心转换逻辑

要得到DOM屏幕坐标,得走这几步:

  • 计算目标面的世界空间中心坐标(从模型本地坐标转成全局世界坐标)
  • 用相机的project()方法把世界坐标转成标准化设备坐标(NDC)
  • 把NDC转换成屏幕像素坐标,最后调整y轴(因为Three.js的原点在左下角,DOM在左上角)
二、获取单个Face的中心坐标(屏幕空间)

首先,咱们写一个工具函数,输入Mesh和Face索引,返回该Face的屏幕坐标:

function getFaceScreenCenter(mesh, faceIndex, camera, renderer) {
    const geometry = mesh.geometry;
    const face = geometry.faces[faceIndex];
    
    // 1. 获取Face的三个顶点本地坐标
    const v1 = geometry.vertices[face.a];
    const v2 = geometry.vertices[face.b];
    const v3 = geometry.vertices[face.c];
    
    // 2. 计算Face的本地中心
    const localCenter = new THREE.Vector3(
        (v1.x + v2.x + v3.x) / 3,
        (v1.y + v2.y + v3.y) / 3,
        (v1.z + v2.z + v3.z) / 3
    );
    
    // 3. 转成世界坐标
    const worldCenter = localCenter.applyMatrix4(mesh.matrixWorld);
    
    // 4. 转成标准化设备坐标(NDC)
    const ndc = worldCenter.clone().project(camera);
    
    // 5. 转成屏幕像素坐标,调整y轴适配DOM
    const screenWidth = renderer.domElement.clientWidth;
    const screenHeight = renderer.domElement.clientHeight;
    
    return new THREE.Vector2(
        (ndc.x + 1) * screenWidth / 2,
        (1 - ndc.y) * screenHeight / 2
    );
}

使用的时候很简单,比如你有一个BoxMesh,想拿第0个Face的屏幕中心:

const boxMesh = new THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.MeshBasicMaterial());
const face0ScreenCenter = getFaceScreenCenter(boxMesh, 0, camera, renderer);
console.log("Face 0的屏幕坐标:", face0ScreenCenter.x, face0ScreenCenter.y);
三、获取组合面(比如Box顶部)的中心坐标

BoxGeometry的每个侧面由2个Face组成,顶部对应的Face索引是4和5(从0开始计数)。咱们可以把这两个Face的中心取平均,或者直接取顶部四个顶点的平均(结果一致),这里提供两种方式:

方式1:基于组合Face的中心计算

function getCombinedFacesScreenCenter(mesh, faceIndices, camera, renderer) {
    let totalX = 0;
    let totalY = 0;
    
    faceIndices.forEach(index => {
        const center = getFaceScreenCenter(mesh, index, camera, renderer);
        totalX += center.x;
        totalY += center.y;
    });
    
    return new THREE.Vector2(totalX / faceIndices.length, totalY / faceIndices.length);
}

// 使用:获取Box顶部(Face4、5)的屏幕中心
const boxTopCenter = getCombinedFacesScreenCenter(boxMesh, [4,5], camera, renderer);
console.log("Box顶部的屏幕坐标:", boxTopCenter.x, boxTopCenter.y);

方式2:直接取组合面的所有顶点平均(更高效)

如果知道组合面对应的顶点索引,比如Box顶部的四个顶点是4、5、6、7,可以直接计算:

function getCombinedVerticesScreenCenter(mesh, vertexIndices, camera, renderer) {
    const geometry = mesh.geometry;
    const localCenter = new THREE.Vector3(0,0,0);
    
    vertexIndices.forEach(index => {
        localCenter.add(geometry.vertices[index]);
    });
    localCenter.divideScalar(vertexIndices.length);
    
    // 后续转换和单个Face的逻辑一致
    const worldCenter = localCenter.applyMatrix4(mesh.matrixWorld);
    const ndc = worldCenter.clone().project(camera);
    
    const screenWidth = renderer.domElement.clientWidth;
    const screenHeight = renderer.domElement.clientHeight;
    
    return new THREE.Vector2(
        (ndc.x + 1) * screenWidth / 2,
        (1 - ndc.y) * screenHeight / 2
    );
}

// 使用:获取Box顶部的屏幕中心
const boxTopCenter2 = getCombinedVerticesScreenCenter(boxMesh, [4,5,6,7], camera, renderer);
四、注意事项
  • 如果你的Geometry是BufferGeometry(Three.js r125+默认用这个),代码要调整:比如用getAttribute('position')获取顶点数据,用index属性获取Face索引,逻辑是一样的只是数据结构不同。
  • 记得在模型变换(移动、旋转、缩放)后调用这些方法,因为matrixWorld会随模型状态更新。
  • 如果相机或者渲染器尺寸变化,也要重新计算,不然坐标会出现偏差。

内容的提问来源于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 09:32:30