射线平面交叉怎么办?

时间:2014-05-31 23:59:23

标签: c++ raytracing

如何计算光线与平面之间的交点? 我一直在访问我能找到的每个可能的网站,这是我迄今取得的成就:

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

这不起作用:s
我的功能输入是:
ray:包含原点和方向 rec:用于存储命中信息的容器类(bool,t等)

我的功能可以访问飞机:
point:定义平面的点
normal:定义平面的法线

4 个答案:

答案 0 :(得分:15)

如你所评论的那样,你想让分母为负数,否则你会错过与飞机正面的交叉点。但是,您仍然希望测试避免被零除,这表示光线与平面平行。在计算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;

答案 1 :(得分:7)

首先考虑光线平面交点的数学运算:

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

所以给定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);
}

答案 2 :(得分:2)

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

答案 3 :(得分:1)

p = p0 + t*v对起点p0进行参数化赋予光线,对v进行方向矢量t >= 0赋予光线。

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

将射线代入平面方程可得出:

t = - (dot(n, p) + d) / dot(n, 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;
}