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

基于Three.js的3D物体闭合环区域创建与点击检测方案咨询

3D物体表面闭合环存储与点击检测方案优化

存储方案优化

你当前考虑的线段集合存储方式冗余且不利于后续计算,更高效的方式是直接存储闭合多边形的顶点数组,同时附加关键信息:

  • 每个顶点记录两个坐标:3D世界坐标,以及对应物体表面的UV坐标(如果物体UV映射合理)。
  • 缓存该多边形所在的近似平面方程(通过任意三个顶点计算),用于后续的空间投影。
    这种存储方式不仅占用空间更小,还能直接为点击检测提供基础数据,避免线段拆分带来的冗余计算。

点击检测高效算法与实现流程

核心思路:3D转2D的多边形内点判断

直接在3D空间做射线与多边形的交点统计效率极低,最优方案是将3D交点和多边形顶点投影到同一2D平面后,用经典的2D内点判断算法:

1. 第一步:用Three.js Raycaster获取物体交点

先通过Raycaster检测点击是否命中目标物体,拿到交点的世界坐标和UV坐标:

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

function getClickIntersection(event, camera, targetMesh) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObject(targetMesh);
  return intersects.length > 0 ? intersects[0] : null;
}

2. 第二步:2D内点判断(推荐两种算法)

射线法(Ray Casting)

计算量小,适合大多数场景,核心逻辑是统计点向右发出的射线与多边形边的交点数,奇数则在内部,偶数在外部:

function isPointInPolygon2D(point, polygonVertices) {
  let isInside = false;
  const { x: px, y: py } = point;
  let prevVertex = polygonVertices[polygonVertices.length - 1];
  
  for (const currVertex of polygonVertices) {
    const { x: cx, y: cy } = currVertex;
    const { x: pxv, y: pyv } = prevVertex;
    
    // 判断点是否在边的y轴范围内,且射线与边相交
    const isYCross = (cy > py) !== (pyv > py);
    if (isYCross) {
      const xIntersect = ((py - cy) * (pxv - cx)) / (pyv - cy) + cx;
      if (px < xIntersect) isInside = !isInside;
    }
    prevVertex = currVertex;
  }
  return isInside;
}
环绕数算法(Winding Number)

精度更高,能处理点落在边上的特殊场景,但计算量略大:

function calculateWindingNumber(point, polygonVertices) {
  let windingNumber = 0;
  const { x: px, y: py } = point;
  let prevVertex = polygonVertices[polygonVertices.length - 1];
  
  for (const currVertex of polygonVertices) {
    const { x: cx, y: cy } = currVertex;
    const { x: pxv, y: pyv } = prevVertex;
    
    if (pyv <= py) {
      if (cy > py && isPointOnLeftSide({x: px, y: py}, prevVertex, currVertex)) {
        windingNumber++;
      }
    } else {
      if (cy <= py && isPointOnLeftSide({x: px, y: py}, prevVertex, currVertex)) {
        windingNumber--;
      }
    }
    prevVertex = currVertex;
  }
  return windingNumber !== 0;
}

// 辅助函数:判断点是否在边的左侧
function isPointOnLeftSide(point, v1, v2) {
  return (v2.x - v1.x) * (point.y - v1.y) - (v2.y - v1.y) * (point.x - v1.x) > 0;
}

3. 投影选择:UV坐标优先

如果目标物体的UV映射是连续、无重叠的,直接用交点的UV坐标和多边形的UV顶点数组做2D判断,计算量最小;若UV映射不合理,则将交点和多边形顶点投影到多边形所在平面的局部2D坐标系(利用之前缓存的平面方程生成正交轴)。

性能优化策略

  • 空间索引:当物体上存在大量闭合环时,用BVH树或网格划分将多边形按空间位置分组,点击时先筛选出与射线相交的分组,再逐个判断,减少计算量。
  • 缓存预计算:提前缓存多边形的2D投影顶点、平面方程,避免每次点击重复计算。
  • 早期退出:在射线法判断时,一旦交点数变为奇数,可提前终止循环(后续边不会改变结果)。

对比线段集合方案的优势

  • 存储效率更高:顶点数组比线段集合少一半的数据量。
  • 检测速度更快:直接基于顶点做2D判断,避免了3D线段与射线的多次求交计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:40:21