【问题标题】:Intersection problems with ray-sphere intersection射线与球体相交的相交问题
【发布时间】:2015-03-12 12:37:20
【问题描述】:

我正在编写一个简单的光线追踪器,为了保持简单,我决定在我的场景中只使用球体。我现在处于一个阶段,我只想确认我的光线是否正确地与场景中的球体相交,仅此而已。我创建了一个 Ray 和 Sphere 类,然后在我的主文件中创建了一个函数,该函数遍历每个像素以查看是否存在交叉点(相关代码将在下面发布)。问题是与球体的整个交叉点表现得相当奇怪。如果我创建一个中心为 (0, 0, -20) 且半径为 1 的球体,那么我只会得到一个交点,该交点始终位于图像的第一个像素处(左上角)。一旦我到达 15 的半径,我会突然在左上角区域看到三个交叉点。 18 的半径给了我六个交叉点,一旦我达到 20+ 的半径,我突然得到每个像素的交叉点,所以有些事情是不应该做的。

我怀疑我的射线球相交代码在这里可能有问题,但是通过查看它并通过网络查看更多信息,大多数解决方案都描述了我使用的相同方法,所以我认为它不应该(!)在这里有错。所以......我不确定我做错了什么,它可能是我的交集代码,也可能是导致问题的其他原因。我似乎无法找到它。给球体和光线赋值时,我想错了吗?下面是相关代码

球体类:

Sphere::Sphere(glm::vec3 center, float radius) 
: m_center(center), m_radius(radius), m_radiusSquared(radius*radius)
{
}

//Sphere-ray intersection. Equation: (P-C)^2 - R^2 = 0, P = o+t*d
//(P-C)^2 - R^2 => (o+t*d-C)^2-R^2 => o^2+(td)^2+C^2+2td(o-C)-2oC-R^2
//=> at^2+bt+c, a = d*d, b = 2d(o-C), c = (o-C)^2-R^2
//o = ray origin, d = ray direction, C = sphere center, R = sphere radius
bool Sphere::intersection(Ray& ray) const
{
    //Squared distance between ray origin and sphere center
    float squaredDist = glm::dot(ray.origin()-m_center, ray.origin()-m_center);

    //If the distance is less than the squared radius of the sphere...
    if(squaredDist <= m_radiusSquared)
    {
        //Point is in sphere, consider as no intersection existing
        //std::cout << "Point inside sphere..." << std::endl;
        return false;
    }

    //Will hold solution to quadratic equation
    float t0, t1;

    //Calculating the coefficients of the quadratic equation
    float a = glm::dot(ray.direction(),ray.direction()); // a = d*d
    float b = 2.0f*glm::dot(ray.direction(),ray.origin()-m_center); // b = 2d(o-C)
    float c = glm::dot(ray.origin()-m_center, ray.origin()-m_center) - m_radiusSquared; // c = (o-C)^2-R^2

    //Calculate discriminant
    float disc = (b*b)-(4.0f*a*c);

    if(disc < 0) //If discriminant is negative no intersection happens
    {
        //std::cout << "No intersection with sphere..." << std::endl;
        return false;
    }
    else //If discriminant is positive one or two intersections (two solutions) exists
    {
        float sqrt_disc = glm::sqrt(disc);
        t0 = (-b - sqrt_disc) / (2.0f * a);
        t1 = (-b + sqrt_disc) / (2.0f * a);
    }

    //If the second intersection has a negative value then the intersections
    //happen behind the ray origin which is not considered. Otherwise t0 is
    //the intersection to be considered
    if(t1<0)
    {
        //std::cout << "No intersection with sphere..." << std::endl;
        return false;
    }
    else
    {
        //std::cout << "Intersection with sphere..." << std::endl;
        return true;
    }
}

计划:

#include "Sphere.h"
#include "Ray.h"

void renderScene(const Sphere& s);

const int imageWidth = 400;
const int imageHeight = 400;

int main()
{
    //Create sphere with center in (0, 0, -20) and with radius 10
    Sphere testSphere(glm::vec3(0.0f, 0.0f, -20.0f), 10.0f);

    renderScene(testSphere);

    return 0;
}

//Shoots rays through each pixel and check if there's an intersection with
//a given sphere. If an intersection exists then the counter is increased.
void renderScene(const Sphere& s)
{
    //Ray r(origin, direction)
    Ray r(glm::vec3(0.0f), glm::vec3(0.0f));

    //Will hold the total amount of intersections
    int counter = 0;

    //Loops through each pixel...
    for(int y=0; y<imageHeight; y++)
    {
        for(int x=0; x<imageWidth; x++)
        {
            //Change ray direction for each pixel being processed
            r.setDirection(glm::vec3(((x-imageWidth/2)/(float)imageWidth), ((imageHeight/2-y)/(float)imageHeight), -1.0f));

            //If current ray intersects sphere...
            if(s.intersection(r))
            {
                //Increase counter
                counter++;
            }
        }
    }

    std::cout << counter << std::endl;
}

【问题讨论】:

    标签: c++ geometry intersection raytracing


    【解决方案1】:

    disc &gt; 0 的情况下,您对二次方程的第二个解 (t1) 是错误的,您需要类似以下内容:

    float sqrt_disc = glm::sqrt(disc);
    t0 = (-b - sqrt_disc) / (2 * a);
    t1 = (-b + sqrt_disc) / (2 * a);
    

    我认为最好以这种形式写出方程式,而不是将除以 2 变成乘以 0.5,因为代码越类似于数学,就越容易检查。

    其他一些次要的cmets:

    1. 将名称 disc 重用于 sqrt(disc) 似乎令人困惑,因此我在上面使用了一个新的变量名称。

    2. 你不需要测试t0 &gt; t1,因为你知道asqrt_disc都是正数,所以t1总是大于t0

    3. 如果光线原点在球体内,t0 可能为负,t1 可能为正。你好像没有处理这个案子。

    4. disc == 0 不需要特殊情况,因为一般情况计算的值与特殊情况相同。 (而且你的特殊情况越少,检查你的代码就越容易。)

    【讨论】:

    • 我已经根据你所说的一些事情更新了我上面的代码。为了更清楚起见,我使用了一个新的变量名称,并删除了 disc == 0 的情况,毕竟正如你所说,这不是必需的。我还修复了第二个解决方案的错误,其中我错过了 - 所指出的标志。你对第三种情况是正确的,我认为检查这一点的最简单方法是简单地查看射线原点和球体中心之间的距离是否小于半径,如果它是没有交叉点(我感兴趣的)发生。
    • 我同意写出尽可能接近公式的代码,但是当通过网络查看时,我采用当前方法的原因是因为建议避免浮点计算出现问题格式。所以我有点不确定我是否应该改变那部分。无论哪种方式,更新都在上面,但不幸的是同样的问题仍然存在
    • 您对t0t1 的计算仍然是错误的!试试我在答案中给出的代码。
    • 已更新。这么晚才回复很抱歉。我使用了您提供的代码。我还添加了您提到的第三种情况。感谢您的建议,我应该想到的非常有用的东西!只是想提一下,它并没有改变我遇到的根本问题,而 Benny Smith 的回答解决了这个问题。一旦我让它与你的(和 Benny Smith 的)代码一起工作,我也尝试了我在当前更新之前的代码,它确实给出了相同的工作结果,所以除了你指出的减号丢失之外,我不完全确定如果我的那部分代码实际上是错误的。
    【解决方案2】:

    如果我正确理解您的代码,您可能想尝试:

    r.setDirection(glm::vec3(((x-imageWidth/2)/(float)imageWidth),
                             ((imageHeight/2-y)/(float)imageHeight),
                             -1.0f));
    

    现在,您已将相机放置在距屏幕一单位的位置,但光线可以向右侧和向下最多射出 400 个单位。这是一个非常广阔的视野。而且,你的光线只扫过一个八分之一的空间。这就是为什么您只能在屏幕的左上角获得少量像素的原因。我上面写的代码应该可以纠正这个问题。

    【讨论】:

    • 谢谢你,就像一个魅力。我不得不承认,虽然我对正在发生的事情感到困惑。已经有一段时间了,但首先我想知道你是如何确定相机和我的投影/图像平面之间的距离的。你说它是一个单位,我可以得出这个结论的唯一方法是我的相机(射线的“开始”位置)在原点,我的方向矢量恰好指向一个 XY 平面,其中 z = - 1.0。不确定这是否是正确的思考方式
    • 但是如果它是一个单位,你是对的,它会有一个广阔的视野(这不好)。这难道不是通过将我的光线的起始位置向后移动来解决吗?无论如何,对于更重要的部分,您提供的代码解决了问题,但我不知道到底发生了什么。如果您不介意,您能否详细说明我的光线仅扫过一个八分之一空间的含义,当然,为什么(!)他们这样做,然后它们与仅在左上角获取像素有何关系?最后你给我的代码是怎么解决的?
    • 我一直在看它,但我就是无法理解正在发生的事情。对于许多问题,我很抱歉,但目前,虽然我有一个可行的解决方案,但我真的不知道我的代码到底在做什么。因此,将不胜感激更详细的解释。再次感谢。
    • 抱歉这么久才回复。您在上面提供的矢量是穿过空间的光线(因此是“光线追踪”)。在您的原始代码中,x 和 y 的值范围为 0 到 400,而 z 保持为 -1 的常数。因此,它们只会在 x 和 y 为正的空间部分(第一个八分位)中传播,在极端情况下,光线每向前移动一个单位,就会向右步进 400 个单位。在我编写的代码中,x 和 y 的范围从 -0.5 到 0.5,而 z 保持恒定为 1 - 这为您提供了 45 度的视野。
    • 我发现这个网站有一些关于光线追踪视野的有用信息:unknownroad.com/rtfm/graphics/rt_eyerays.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 2011-01-04
    相关资源
    最近更新 更多