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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:54:37