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

我很怀疑射线球交点代码可能在这里出错,但是仔细检查并在网上查找了更多信息之后,大多数解决方案都描述了我使用的相同方法,因此我认为它不应该是错误的!这里。所以...我不确定自己在做什么错,这可能是我的交集代码,也可能是导致问题的其他原因。我似乎找不到。给出球体和光线的值时,可能是我在想错吗?以下是相关代码

球形类:

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

最佳答案

t1的情况下,您对二次方程式的第二种解决方案(disc > 0)是错误的,您需要这样的东西:

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

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

其他一些小意见:
  • 对于disc重复使用名称sqrt(disc)似乎令人困惑,因此我在上面使用了新的变量名。
  • 您不需要测试t0 > t1,因为您知道asqrt_disc均为正数,因此t1始终大于t0
  • 如果射线原点在球体内,则t0可能为负,而t1可能为正。您似乎无法处理这种情况。
  • 不需要disc == 0的特殊情况,因为一般情况下计算出的值与特殊情况下相同。 (而且,特殊情况越少,检查代码就越容易。)
  • 关于c++ - 射线-球面相交的相交问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12678225/

    10-12 23:58