2012-10-01 138 views
2

我正在写一个简单的射线追踪器,并且现在保持简单我已经决定在我的场景中只有球体。我现在正处于一个舞台,我只想确认我的光线正确地与场景中的球体相交,没有别的。我创建了一个Ray和Sphere类,然后在我的主文件中创建了一个函数,通过每个像素检查是否有交点(相关代码将在下面发布)。问题是与球体的整个交点表现得相当奇怪。如果我用中心(0,0,-20)和半径1创建一个球体,那么我只能得到一个总是在我的图像(左上角)的第一个像素处的交点。一旦我达到半径15,我突然在左上区域得到三个交点。 18的半径给我六个交点,一旦我达到20+的半径,我突然为每个像素得到一个交点,所以某些行为是不应该做的。射线球相交的相交问题

我很怀疑我的ray-sphere十字路口代码可能在这里出错,但通过它查看并通过网络查看更多信息大多数解决方案描述了我使用的非常相同的方法,所以我认为它不应该(! )在这里有过错。所以......我不完全确定我做错了什么,它可能是我的交叉码或者可能是其他问题导致的问题。我似乎无法找到它。难道当我为球体和射线赋予价值时我在想错吗?下面是相关的代码

球类:

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

回答

2

你的第二个解决方案(t1)的二次方程是错误的情况下disc > 0,在那里你需要类似:

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

我认为最好写出这种形式的公式,而不是将除法乘以2乘以0.5,因为代码越接近数学越容易检查。

其他一些次要的意见:

  1. 这似乎令人困惑的重新使用的名称为discsqrt(disc),所以我上面使用了新的变量名。

  2. 你并不需要测试t0 > t1,因为你知道,无论asqrt_disc是积极的,所以t1总是比t0更大。

  3. 如果射线原点位于球体内部,则可能是t0为负值,t1为正值。你似乎无法处理这种情况。

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

+0

我已经根据你所说的一些事情更新了我的代码。我使用了一个新的变量名称以获得更好的清晰度,并且还删除了光盘== 0的情况,毕竟这不是必要的,正如您所说的。我还修正了第二个解决方案的错误,我错过了 - 正如指出的那样。对于第三种情况你是对的,我假设检查这个最简单的方法是简单地看看射线原点和球体中心之间的距离是否小于半径,如果它没有发生(我感兴趣的)交点。 – Suen

+0

我同意写出尽可能接近公式的代码,但是通过网络浏览时,我使用当前方法的原因是因为建议避免浮点格式计算出现问题。所以我有点不确定是否应该改变那部分。无论哪种方式,更新是在上面,但不幸的是同样的问题仍然存在 – Suen

+0

您对't0'和't1'的计算仍然是错误的!请尝试我在我的回答中给出的代码。 –

1

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

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

现在,你已经位于相机一个单元远离屏幕,但光线可以为拍摄多达400个单位的权利和下降。这是一个非常广阔的视野。另外,你的光线只能扫过一个空间。这就是为什么你只能在屏幕左上角获得一些像素。我上面写的代码应该纠正这一点。

+0

谢谢你,作品像一个魅力。我不得不承认,尽管我对发生的事情感到困惑。这是一段时间,但首先我想知道如何确定相机和投影/图像平面之间的距离。你说这是一个单位,我唯一可以得出结论的方法是我的相机(“开始”射线位置)在原点,我的方向矢量碰巧指向一个XY平面,其中z = - 1.0。不知道这是否是正确的想法 – Suen

+0

但是,如果它是一个单位,你是对的,它会有一个广阔的视野(这是不好的)。这难道不会通过将我的射线的开始位置进一步移回来解决吗?无论如何,对于更重要的部分,您提供的代码解决了问题,但我不清楚究竟发生了什么。如果你不介意,你能详细说明我的光线是什么意思,只扫过一个空间,当然还有为什么(!)他们这样做,然后他们如何才能在左上角找到像素?最后,你给我的代码是如何解决的? – Suen

+0

我一直在看着它,但我无法将我的头围绕发生的事情。我很抱歉有很多问题,但目前,当我有一个工作解决方案时,我并不真正知道我的代码正在做什么。所以更详细的解释将非常感谢。再次感谢。 – Suen