C++ 如何做射线平面相交?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23975555/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:36:17  来源:igfitidea点击:

How to do ray plane intersection?

c++raytracing

提问by Pontus Magnusson

How do I calculate the intersection between a ray and a plane?

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

Code

代码

This produces the wrong results.

这会产生错误的结果。

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;
    }
}

Parameters

参数

rayrepresents the ray object.
ray.directionis the direction vector.
ray.originis the origin vector.
recrepresents the result object.
rec.tHitis the value of the hit.
rec.anyHitis a boolean.

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

My function has access to the plane:
centerand normaldefines the plane

我的函数可以访问平面:
centernormal定义平面

回答by Trillian

As wonce commented, you want to also allow the denominator to be negative, otherwise you will miss intersections with the front face of your plane. However, you still want a test to avoid a division by zero, which would indicate the ray being parallel to the plane. You also have a superfluous negation in your computation of t. Overall, it should look like this:

正如 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;

回答by vwvan

First consider the math of the ray-plane intersection:

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

In general one intersects the parametric form of the ray, with the implicit form of the geometry.

通常,光线的参数形式与几何的隐式形式相交。

So given a ray of the form x = a * t + a0, y = b * t + b0, z = c * t + c0;

所以给定形式为 x = a * t + a0, y = b * t + b0, z = c * t + c0;

and a plane of the form: A x * B y * C z + D = 0;

和形式为:A x * B y * C z + D = 0 的平面;

now substitute the x, y and z ray equations into the plane equation and you will get a polynomial in t. you then solve that polynomial for the real values of t. With those values of t you can back substitute into the ray equation to get the real values of x, y and z. Here it is in Maxima:

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

enter image description here

在此处输入图片说明

Note that the answer looks like the quotient of two dot products! The normal to a plane is the first three coefficients of the plane equation A, B, and C. You still need D to uniquely determine the plane. Then you code that up in the language of your choice like so:

请注意,答案看起来像两点乘积的商!平面的法线是平面方程 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);
}

回答by Bas Smit

implementation of vwvan's answer

执行 vwvan 的答案

Vector3 Intersect(Vector3 planeP, Vector3 planeN, Vector3 rayP, Vector3 rayD)
{
    var d = Vector3.Dot(planeP, -planeN);
    var t = -(d + rayP.z * planeN.z + rayP.y * planeN.y + rayP.x * planeN.x) / (rayD.z * planeN.z + rayD.y * planeN.y + rayD.x * planeN.x);
    return rayP + t * rayD;
}

回答by Mateen Ulhaq

Let the ray be given parametrically by p = p0 + t*vfor initial point p0and direction vector vfor t >= 0.

让参射线由下式给出p = p0 + t*v用于初始点p0和方向矢量vt >= 0

Let the plane be given by dot(n, p) + d = 0for normal vector n = (a, b, c)and constant d. If ris a point on the plane, then d = - dot(n, r). Fully expanded, the plane equation may also be written ax + by + cz + d = 0.

让平面由dot(n, p) + d = 0法向量n = (a, b, c)和常数给出d。如果r是平面上的一个点,则d = - dot(n, r)。完全展开后,平面方程也可以写成ax + by + cz + d = 0

Substituting the ray into the plane equation gives:

将射线代入平面方程给出:

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

Example implementation:

示例实现:

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;
}