简体   繁体   English

如何做射线平面相交?

[英]How to do ray plane intersection?

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参数

ray represents the ray object. ray表示射线对象。
ray.direction is the direction vector. ray.direction是方向向量。
ray.origin is the origin vector. ray.origin是原点向量。
rec represents the result object. rec表示结果对象。
rec.tHit is the value of the hit. rec.tHit是命中的值。
rec.anyHit is a boolean. rec.anyHit是一个布尔值。

My function has access to the plane:我的函数可以访问飞机:
center and normal defines the plane centernormal定义了平面

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.正如 wonce 评论的那样,您还希望分母为负数,否则您将错过与飞机正面的交叉点。 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 .您在t的计算中也有一个多余的否定。 Overall, it should look like this:总的来说,它应该是这样的:

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;

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.现在将 x、y 和 z 射线方程代入平面方程,您将得到 t 中的多项式。 you then solve that polynomial for the real values of t.然后,您可以为 t 的实际值求解该多项式。 With those values of t you can back substitute into the ray equation to get the real values of x, y and z.使用这些 t 值,您可以代入射线方程以获得 x、y 和 z 的实际值。 Here it is in Maxima:这是在千里马:

在此处输入图片说明

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.平面的法线是平面方程 A、B 和 C 的前三个系数。您仍然需要 D 来唯一地确定平面。 Then you code that up in the language of your choice like so:然后你用你选择的语言编写代码,如下所示:

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

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


    return (point3D);
}

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

Let the ray be given parametrically by p = p0 + t*v for initial point p0 and direction vector v for t >= 0 .让初始点p0p = p0 + t*vt >= 0方向向量v参数化地给出射线。

Let the plane be given by dot(n, p) + d = 0 for normal vector n = (a, b, c) and constant d .对于法向量n = (a, b, c)和常数d n = (a, b, c)让平面由dot(n, p) + d = 0给出。 If r is a point on the plane, then d = - dot(n, r) .如果r是平面上的一个点,则d = - dot(n, r) Fully expanded, the plane equation may also be written ax + by + cz + d = 0 .完全展开后,平面方程也可以写成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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM