光线追踪 - 几何球体相交 - 尽管没有相交,相交函数对所有光线返回true

3

我正在用C++和OpenGL编写一份光线追踪项目,并在我的球体相交函数中遇到了一些问题:我检查了多个来源,数学看起来没问题,但出现了每条光线都返回相交的情况。以下是球体相交函数的代码以及其他一些说明性代码:

bool intersect(Vertex & origin, Vertex & rayDirection, float intersection)
{
    bool insideSphere = false;
    Vertex oc = position - origin;
    float tca = 0.0;
    float thcSquared = 0.0;

    if (oc.length() < radius)
        insideSphere = true;

    tca = oc.dot(rayDirection);

    if (tca < 0 && !insideSphere)
        return false;

    thcSquared = pow(radius, 2) - pow(oc.length(), 2) + pow(tca, 2);

    if (thcSquared < 0)
        return false;

    insideSphere ? intersection = tca + sqrt(thcSquared) : intersection = tca - sqrt(thcSquared);

    return true;
}

以下是调用交点函数的光线追踪函数中的一些上下文信息。请注意,我的相机位于 (0, 0, 0) ,这也是光线追踪函数中“origin”变量的值:

#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

#define WINDOW_METERS_WIDTH 30
#define WINDOW_METERS_HEIGHT 20
#define FOCAL_LENGTH 25


rayDirection.z = FOCAL_LENGTH * -1;
for (int r = 0; r < WINDOW_HEIGHT; r++)
{
    rayDirection.y = (WINDOW_METERS_HEIGHT / 2 * -1) + (r * ((float)WINDOW_METERS_HEIGHT / (float)WINDOW_HEIGHT));
    for (int c = 0; c < WINDOW_WIDTH; c++)
    {
        intersection = false;
        t = 0.0;

        rayDirection.x = (WINDOW_METERS_WIDTH / 2 * -1) + (c * ((float)WINDOW_METERS_WIDTH / (float)WINDOW_WIDTH));

        rayDirection = rayDirection - origin;

        for (int i = 0; i < NUM_SPHERES; i++)
        {
            if (spheres[i].intersect(CAM_POS, rayDirection, t))
            {
                intersection = true;
            }
        }

感谢您的查看,请让我知道是否有其他代码可以帮助您!

嗯,intersect()函数里面有两个return true语句,你的调试器显示哪一个一直被触发了? - genpfault
1
应该只有一个"return true"语句;另一个仅将"insideSphere"赋为true值。 - user1333890
2
在intersect函数的末尾分配intersection的目的是什么? - JWWalker
2
但是,在函数末尾修改局部变量在函数返回后没有任何影响,那么为什么要这样做呢? - JWWalker
强烈推荐查看此源代码 https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection - user18490
显示剩余4条评论
1个回答

2
看起来你的数学有点混淆了。函数的第一部分,也就是直到第一个return false为止,没问题,如果光线起点在球体外且不朝向它,则返回false。然而,我认为你把相机放在所有球体外面,以这种方式使得所有球体都可见,这就是为什么这部分从未返回false的原因。 thcSquared真的很错误,我不知道它应该代表什么。
让我们用数学方法来做交点计算。我们有:
- origin:射线的起点,我们称之为A - rayDirection:无限射线的方向,我们称之为d - position:球体的中心,称为P - radius:不言自明,称为r 你想要的是一个同时在球体和直线上的点,我们称之为M
- M = A + t * d,因为它在直线上 - |M - P| = r,因为它在球体上
第二个方程可以改写成|A + t * d - P|² = r²,这给出了(A - P)² + 2 * t * (A - P).dot(d) + t²d² = r²。这是一个简单的二次方程。解决后,你会得到0、1或2个解,选择离射线起点最近的一个(但是必须是正数)。 编辑:你被迫使用另一种方法,我在这里详细说明:
  1. 计算球心到直线(称之为l)的距离。这可以通过将球心“投影”在直线上来完成。因此:

    tca = ( (P - A) dot d ) / |d|,或者使用您的变量名称,tca = (OC dot rd) / |rd|。投影是H = A + tca * d,并且l = |H - P|

  2. 如果l > R,则返回false,没有交点。
  3. 假设M是一个交点。三角形MHP有一个直角,因此MH² + HP² = MP²,换句话说thc² + l² = r²,因此我们现在有了从H到球体的距离thc
  4. 有了所有这些,t = tca +- thc,只需取两者中最小的非负值。

您链接的论文解释了这一点,但没有说明它假定射线方向的范数为1。我没有在您的代码中看到归一化,这可能是您的代码失败的原因(未经验证)。

附注:将3D向量命名为Vertex真的不好,像Vector3vec3这样的名称会更好。


我们在这个项目中使用的算法基于这个演示文稿中第13页以后的算法:http://www.vis.uky.edu/~ryang/teaching/cs535-2012spr/Lectures/13-RayTracing-II.pdf。你提供的解决方案似乎很有道理,我会尝试一下看看它是否能解决我的问题,但最终而不幸的是,我必须使用几何算法。 - user1333890
也非常同意关于Vertex名称的评论;它被使用的唯一原因是因为它是从早期项目中回收的。 - user1333890
好的,我会看一下你的算法 - 可能需要一些时间,因为我同时还得吃饭 ;) - Synxis
完成。这篇论文的质量并不是很好(错别字、错误定义、过度复杂且无用的问题定义等)。另外,非常重要的是,它假定“rd”的范数为1。 - Synxis
嗯...尽管如此,这篇论文仍然有漂亮的图表。顺便问一下,“MIA”是什么? - Synxis
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接