如何获取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
相关产品推荐
相关产品推荐

