如何计算光线平面交点

问题描述 投票:0回答:4

如何计算射线和平面之间的交点?

以下代码会产生错误的结果。

float denom = normal.dot(ray.direction);

if (denom > 0)
{
    float t = -((center - ray.origin).dot(normal)) / denom;

    if (t >= 0)
    {
        rec.tHit = t;
        rec.anyHit = true;
        computeSurfaceHitFields(ray, rec);
        return true;
    }
}

输入变量说明如下:

  • ray
    代表射线对象。
  • ray.direction
    是方向向量。
  • ray.origin
    是原点向量。
  • rec
    表示结果对象。
  • rec.tHit
    是命中值。
  • rec.anyHit
    是一个布尔值。

我的函数可以访问飞机:

  • center
    normal
    定义平面
c++ raycasting raytracing
4个回答
40
投票

正如 wonce 所评论的,您还希望允许分母为负数,否则您将错过与飞机正面的交叉点。但是,您仍然希望进行测试以避免被零除,这将表明光线平行于平面。您在计算

t
时也有多余的否定。总的来说,它应该看起来像这样:

float denom = normal.dot(ray.direction);
if (abs(denom) > 0.0001f) // your favorite epsilon
{
    float t = (center - ray.origin).dot(normal) / denom;
    if (t >= 0) return true; // you might want to allow an epsilon here too
}
return false;

8
投票

首先考虑射线平面相交的数学:

一般来说,光线的参数形式与几何的隐式形式相交。

因此,给定一条形式为 x = a * t + a0, y = b * t + b0, z = c * t + c0; 的射线;

以及以下形式的平面:A x * B y * C z + D = 0;

现在将 x、y 和 z 射线方程代入平面方程,您将得到 t 的多项式。然后求解该多项式以获得 t 的实际值。利用 t 的这些值,您可以代入射线方程以获得 x、y 和 z 的实际值。 这是Maxima:

enter image description here

请注意,答案看起来像是两个点积的商! 平面法线是平面方程 A、B 和 C 的前三个系数。 您仍然需要 D 来唯一确定平面。 然后用您选择的语言对其进行编码,如下所示:

Point3D intersectRayPlane(Ray ray, Plane plane)
{
    Point3D point3D;

    //  Do the dot products and find t > epsilon that provides intersection.


    return (point3D);
}

7
投票

数学

定义:

  • 让射线由

    q = p + t*v
    参数化给出初始点
    p
    和方向向量
    v
    t >= 0

  • 令平面为点集

    r
    ,满足法向量
    dot(n, r) + d = 0
    和常数
    n = (a, b, c)
    的方程
    d
    。完全展开后,平面方程也可以写成熟悉的形式
    ax + by + cz + d = 0

q
满足平面方程时,光线与平面相交。代入,我们有:

d = -dot(n, q)
  = -dot(n, p + t * v)
  = -dot(n, p) + t * dot(n, v)

重新排列:

t = -(dot(n, p) + d) / dot(n, v)

t
的值插回
p + t*v
中即可用于确定交点。

实施示例

std::optional<vec3> intersectRayWithPlane(
    vec3 p, vec3 v,  // ray
    vec3 n, float d  // plane
) {
    float denom = dot(n, v);

    // Prevent divide by zero:
    if (abs(denom) <= 1e-4f)
        return std::nullopt;

    // If you want to ensure the ray reflects off only
    // the "top" half of the plane, use this instead:
    //
    // if (-denom <= 1e-4f)
    //     return std::nullopt;

    float t = -(dot(n, p) + d) / dot(n, v);

    // Use pointy end of the ray.
    // It is technically correct to compare t < 0,
    // but that may be undesirable in a raytracer.
    if (t <= 1e-4)
        return std::nullopt;

    return p + t * v;
}

4
投票

实施vwvan的答案

Vector3 Intersect(Vector3 planeP, Vector3 planeN, Vector3 rayP, Vector3 rayD)
{
    var d = Vector3.Dot(planeP, -planeN);
    var t = -(d + Vector3.Dot(rayP, planeN)) / Vector3.Dot(rayD, planeN);
    return rayP + t * rayD;
}
© www.soinside.com 2019 - 2024. All rights reserved.