2012-10-01 62 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