3D中射线与正方形/矩形相交

5 投票
3 回答
17743 浏览
提问于 2025-04-17 09:57

我正在制作一个游戏,想要在3D空间中找到光线与正方形或矩形的交点。我在网上搜索了很多解决方案,但没有找到我能理解的。虽然有一些关于2D中线和线段交点的脚本,但我不知道怎么把它们改成3D的。

交点从哪个方向进入正方形或矩形并不重要,但我需要能够获取交点的向量,这样我就可以测试它与其他交点的距离,看看这个交点是发生在其他交点之前还是之后。

如果有用Python或其他类似脚本语言的例子,我会非常感激。

编辑:我不知道怎么修改2D的例子来展示,但我做了一个新的,并且同时发布了两个。

// This is the example it test a ray onto a plane then look to se if that point is in the rectangle and saves it to test for distance later
list Faces; // Triangle faces
list Points;

vector FindPoint(){
    // Calculate the point of intersection onto the plane and returns it
    // If it can intersect
    // Else return ZERO_VECTOR
}

integer point-in-quadrilateral(){
    // Return 1 if the point is in the rectangular on the plane
    // Else return 0
}

default{
    state_entry(){
        integer n = (Faces != []); // Return number of elements
        integer x = 0;
        while(x < n){
            vector intersection = FindPoint( FromList(Faces, x) ); // Take out a element and runs it trough the function
            if(intersection != ZERO_VECTOR){
                integer test = point-in-quadrilateral( FromList(Faces,     x) ); // Find out if the point is in rectangular
                if(test == 1){ // If so
                    Points += intersection; // Save the point
                }
            }
            ++x;
        }

        float first; // The distance to the box intersection
        integer l = (Points != []);
        integer d;
        while(d < l){
            if(Dist( FromList(Points, d) ) < first) // If the new distance is less then first
                return 0; // Then end script
            ++d;
        }
    }
}

// This is the 2D version
vector lineIntersection(vector one, vector two, vector three, vector four){
float bx = two.x - one.x;
float by = two.y - one.y;
float dx = four.x - three.x;
float dy = four.y - three.y; 
float b_dot_d_perp = bx*dy - by*dx;
if(b_dot_d_perp == 0.0) {
    return ZERO_VECTOR;
}
float cx = three.x-one.x; 
float cy = three.y-one.y;
float t = (cx*dy - cy*dx) / b_dot_d_perp; 
if(LineSeg){ //if true tests for line segment
    if((t < 0.0) || (t > 1.0)){
        return ZERO_VECTOR;
    }
    float u = (cx * by - cy * bx) / b_dot_d_perp;
    if((u < 0.0) || (u > 1.0)) {
        return ZERO_VECTOR;
    }
}

return <one.x+t*bx, one.y+t*by, 0.0>;

3 个回答

0

你没有说明这个三维的正方形/矩形是否和坐标轴对齐。假设这个三维矩形 R 在空间中是随意放置的,这里有一种方法。首先,你需要把你的光线 r 和包含 R 的平面相交。你可以通过给 r 乘上一个缩放因子 s,让它落在 R 的平面上,然后求出这个 s 的值来实现。这会给你一个在平面上的点 p。接下来,把这个平面、Rp 投影到某一个坐标平面上,比如 {xy, yz, zx}。你只需要避免沿着平面的法向量进行垂直投影,这总是可以做到的。然后在投影平面上解决点是否在四边形内的问题。

在开始之前,检查一下你的线段是否在 R 的三维平面内,如果是的话,单独处理这个情况。

17

这个解决方案其实很简单。你可以用一个点(也就是一个向量)和一个方向向量来定义一条射线,用一个点(同样是一个向量)和两个表示边的向量来定义一个矩形。

假设这条射线可以表示为 R0 + t * D,其中 R0 是射线的起点,D 是一个单位向量,表示射线的方向,t 是射线的长度。

矩形可以用一个角点 P0 和两个向量 S1S2 来表示,这两个向量分别代表矩形的两条边(它们的长度等于边的长度)。你还需要一个向量 N,它是矩形表面的法向量,这个向量等于 S1S2 的叉积的单位向量。

现在,假设射线与矩形相交于点 P。那么,射线的方向 D 必须与法向量 N 形成一个非零角度。你可以通过检查 D.N < 0 来验证这一点。

为了找到交点,假设 P = R0 + a * D(这个点必须在射线上)。现在你需要找到 a 的值。找出向量 P0P。这个向量必须与 N 垂直,这意味着 P0P.N = 0,这可以简化为 a = ((P0 - R0).N) / (D.N)

接下来,你需要检查这个点是否在矩形内部。为此,计算 P0PS1 方向上的投影 Q1,以及在 S2 方向上的投影 Q2。点在矩形内部的条件是 0 <= length(Q1) <= length(S1)0 <= length(Q2) <= length(S2)

这个方法适用于任何类型的平行四边形,不仅仅是矩形。[更新:看起来关于平行四边形的说法让人有些困惑。在平行四边形(也包括矩形)的情况下,投影应该沿着另一条边进行,而不仅仅是垂直于它(引用最后一段:“计算 Q1 的投影沿 S1Q2 的投影沿 S2”)。这就像在一个坐标系统中,坐标轴之间不是垂直的。]

3

首先,你需要为三维空间中的一条直线创建一个向量方程,然后找出这条直线与一个矩形所在平面的交点。接下来,只需简单地检查这个交点是否在矩形的范围内。

你可以通过以下方式找到解中的参数 t:

t = (a * (x0 - rx) + b * (y0 - ry) + c * (x0 - rz)) / (a * vx + b * vy + c * vz)

这里:

a(x - x0) + b(y - y0) + c(z - z0) = 0

是你矩形所在平面的方程。

而:

<x, y, z> = <rx + vx * t, ry + vy * t, rz + vz * t>

是你所提到的那条直线的向量方程。

需要注意的是:

<rx, ry, rz>

是向量方程的起始点,

<vx, vy, vz>

是上述方程的方向向量。

之后,把参数 t 代入你的向量方程,就能得到一个点,用来测试距离。

enter image description here

撰写回答