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

THREEJS:如何计算两条THREE.Ray射线间的彼此最近点

找到THREE.js中两条射线上彼此最近的点

嘿,这个需求其实是空间几何里经典的两条射线最短距离对应点计算问题,我结合THREE.js的API给你写一套可直接用的实现,还会解释关键逻辑:

核心思路

两条射线可以表示为:

  • 射线1:P₁(t) = origin₁ + t × direction₁,其中 t ≥ 0(因为射线是从原点向方向延伸的半无限线)
  • 射线2:P₂(s) = origin₂ + s × direction₂,其中 s ≥ 0

我们的目标是找到非负的t和s,使得P₁(t)与P₂(s)之间的距离最小——这个时候两点的连线是两条射线的公垂线(如果射线不平行且最短点不在射线端点的话)。

完整实现代码

function getClosestPointsOnRays(ray1, ray2) {
    const o1 = ray1.origin;
    const d1 = ray1.direction;
    const o2 = ray2.origin;
    const d2 = ray2.direction;

    // 计算原点之间的向量
    const v = o2.clone().sub(o1);
    // 预计算各方向向量的点积
    const dotD1D1 = d1.dot(d1);
    const dotD1D2 = d1.dot(d2);
    const dotD2D2 = d2.dot(d2);
    const dotD1V = d1.dot(v);
    const dotD2V = d2.dot(v);

    // 处理平行射线的情况(分母趋近于0)
    const denominator = dotD1D1 * dotD2D2 - dotD1D2 * dotD1D2;
    if (Math.abs(denominator) < 1e-10) {
        // 射线1上离射线2原点最近的点
        let t = dotD1V / dotD1D1;
        t = Math.max(t, 0); // 确保t非负(射线约束)
        const p1 = o1.clone().add(d1.clone().multiplyScalar(t));
        
        // 射线2上离射线1原点最近的点
        let s = dotD2V / dotD2D2;
        s = Math.max(s, 0); // 确保s非负
        const p2 = o2.clone().add(d2.clone().multiplyScalar(s));

        // 比较两种端点组合的距离,取更近的一组
        const distO1P2 = o1.distanceTo(p2);
        const distO2P1 = o2.distanceTo(p1);
        if (distO1P2 <= distO2P1) {
            return [o1.clone(), p2];
        } else {
            return [p1, o2.clone()];
        }
    }

    // 非平行射线,计算t和s的候选值
    let t = (dotD1V * dotD2D2 - dotD2V * dotD1D2) / denominator;
    let s = (dotD2V * dotD1D1 - dotD1V * dotD1D2) / denominator;

    // 应用射线的半无限约束(t和s不能为负)
    if (t < 0) {
        t = 0;
        s = dotD2V / dotD2D2;
        s = Math.max(s, 0); // 再次确保s非负
    } else if (s < 0) {
        s = 0;
        t = dotD1V / dotD1D1;
        t = Math.max(t, 0); // 再次确保t非负
    }

    // 计算最终的最近点
    const closestPoint1 = o1.clone().add(d1.clone().multiplyScalar(t));
    const closestPoint2 = o2.clone().add(d2.clone().multiplyScalar(s));

    return [closestPoint1, closestPoint2];
}

代码说明

  • 平行射线处理:当两条射线方向平行时,我们分别计算每条射线离另一条射线原点最近的点,再取距离更小的那组结果。
  • 非平行射线处理:通过解线性方程组得到t和s的候选值,再修正为符合射线约束的非负值(因为射线不能往原点反方向延伸)。
  • THREE.js API使用:全程用clone()避免修改原射线的原点和方向向量,保证数据安全性。

使用示例

// 创建两条测试射线
const ray1 = new THREE.Ray(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 1, 0).normalize());
const ray2 = new THREE.Ray(new THREE.Vector3(5, 0, 0), new THREE.Vector3(-1, 1, 0).normalize());

// 获取最近点
const [point1, point2] = getClosestPointsOnRays(ray1, ray2);

console.log("射线1上的最近点:", point1);
console.log("射线2上的最近点:", point2);

内容的提问来源于stack exchange,提问作者Marek Krzeminski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:06:57