3D散点图中用ExtrudeGeometry包裹选中点的位置校准问题求助
问题分析与解决方案
你的核心问题在于:直接将屏幕坐标转换为归一化设备坐标(NDC)作为THREE.Shape的顶点,再通过ExtrudeGeometry创建的网格,没有匹配3D世界空间的尺度与位置;同时用投影后的NDC z值计算挤出深度,完全不符合世界空间的实际深度范围,导致包裹形状的位置、大小都出现偏差。
关键修正步骤
1. 正确转换屏幕多边形到世界空间
屏幕上绘制的多边形需要转换为3D世界空间中对应平面的点,这里我们基于选中点沿相机视线方向的深度来确定平面位置:
// 屏幕坐标转世界空间点(基于相机视线方向的指定深度) const screenToWorld = (screenX, screenY, depthAlongCamera) => { const cameraDir = this.camera.getWorldDirection(new THREE.Vector3()).negate(); const rayOrigin = this.camera.position.clone(); const rayDir = new THREE.Vector3( (screenX / canvasWidth) * 2 - 1, -(screenY / canvasHeight) * 2 + 1, 0.5 ).unproject(this.camera).sub(rayOrigin).normalize(); return rayOrigin.add(rayDir.multiplyScalar(depthAlongCamera)); };
2. 计算选中点的真实深度范围
放弃使用投影后的NDC z值,改用选中点在相机视线方向上的投影距离计算挤出深度:
// 假设selectedPoints是选中点的世界空间Vector3数组 const cameraDir = this.camera.getWorldDirection(new THREE.Vector3()).negate(); let minDepth = Infinity; let maxDepth = -Infinity; selectedPoints.forEach(point => { const depth = point.clone().sub(this.camera.position).dot(cameraDir); minDepth = Math.min(minDepth, depth); maxDepth = Math.max(maxDepth, depth); }); const extrusionDepth = maxDepth - minDepth;
3. 创建匹配世界空间的挤出形状
将屏幕多边形转换为mesh局部空间的顶点,再设置mesh的正确位置与旋转:
// 把屏幕多边形转成世界空间点(位于选中点的最近深度平面) const worldPolygon = normalizedPolygon.map(pt => screenToWorld(pt[0], pt[1], minDepth)); // 设置mesh的位置与旋转:让mesh局部z轴对齐相机视线方向 const meshPosition = this.camera.position.clone().add(cameraDir.clone().multiplyScalar(minDepth)); const meshRotation = new THREE.Quaternion().setFromUnitVectors( new THREE.Vector3(0, 0, 1), cameraDir ); // 转换世界空间点到mesh局部空间 const localPolygon = worldPolygon.map(point => { return point.clone().sub(meshPosition).applyQuaternion(meshRotation.invert()); }); // 创建Shape与挤出网格 const shape = new THREE.Shape(); const firstPt = localPolygon[0]; shape.moveTo(firstPt.x, firstPt.y); localPolygon.slice(1).forEach(pt => shape.lineTo(pt.x, pt.y)); shape.lineTo(firstPt.x, firstPt.y); const geometry = new THREE.ExtrudeGeometry(shape, { steps: 2, depth: extrusionDepth, bevelEnabled: false, }); const material = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.1, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); mesh.position.copy(meshPosition); mesh.quaternion.copy(meshRotation); this.scene.add(mesh);
核心修正点说明
- 放弃直接使用NDC坐标作为Shape顶点,改为将屏幕点转换到世界空间,再映射到mesh局部空间,确保形状尺度与3D场景匹配。
- 用相机视线方向的深度替代投影后的NDC z值,保证挤出长度符合选中点的实际空间范围。
- 通过旋转对齐相机视线方向,让挤出方向与选中点的深度方向一致,避免形状朝向错误。
内容的提问来源于stack exchange,提问作者igal leikin
相关产品推荐
相关产品推荐

