2016-10-28 68 views
2

我正在實現具有反射的遞歸射線追蹤器。射線追蹤器目前正在反映出處於陰影中的區域,我不知道爲什麼。當反射代碼被註釋掉時,射線跟蹤器的陰影部分按預期工作,所以我不認爲這是問題。遞歸射線追蹤器中的不正確反射

Vec Camera::shade(Vec accumulator, 
        Ray ray, 
        vector<Surface*>surfaces, 
        vector<Light*>lights, 
        int recursion_depth) { 

if (recursion_depth == 0) return Vec(0,0,0); 

double closestIntersection = numeric_limits<double>::max(); 
Surface* cs; 
for(unsigned int i=0; i < surfaces.size(); i++){ 
    Surface* s = surfaces[i]; 

    double intersection = s->intersection(ray); 

    if (intersection > EPSILON && intersection < closestIntersection) { 
     closestIntersection = intersection; 
     cs = s; 
    } 
} 
if (closestIntersection < numeric_limits<double>::max()) { 

    Point intersectionPoint = ray.origin + ray.dir*closestIntersection; 
    Vec intersectionNormal = cs->calculateIntersectionNormal(intersectionPoint); 

    Material materialToUse = cs->material; 

    for (unsigned int j=0; j<lights.size(); j++) { 

     Light* light = lights[j]; 

     Vec dirToLight = (light->origin - intersectionPoint).norm(); 
     Vec dirToCamera = (this->eye - intersectionPoint).norm(); 


     bool visible = true; 
     for (unsigned int k=0; k<surfaces.size(); k++) { 
      Surface* s = surfaces[k]; 

      double t = s->intersection(Ray(intersectionPoint, dirToLight)); 

      if (t > EPSILON && t < closestIntersection) { 
       visible = false; 
       break; 
      } 
     } 

     if (visible) { 
      accumulator = accumulator + this->color(dirToLight, intersectionNormal, 
              intersectionPoint, dirToCamera, light, materialToUse); 
     } 

    } 

    //Reflective ray 
    //Vec r = d − 2(d · n)n 
    if (materialToUse.isReflective()) { 
     Vec d = ray.dir; 
     Vec r_v = d-intersectionNormal*2*intersectionNormal.dot(d); 
     Ray r(intersectionPoint+intersectionNormal*EPSILON, r_v); 
     //km is the ideal specular component of the material, and mult is component-wise multiplication 
     return this->shade(accumulator, r, surfaces, lights, recursion_depth--).mult(materialToUse.km); 
    } 
    else 
     return accumulator; 
} 
else 
    return accumulator; 
} 

Vec Camera::color(Vec dirToLight, 
        Vec intersectionNormal, 
        Point intersectionPoint, 
        Vec dirToCamera, 
        Light* light, 
        Material material) { 

//kd I max(0, n · l) + ks I max(0, n · h)p 

Vec I(light->r, light->g, light->b); 
double dist = (intersectionPoint-light->origin).magnitude(); 
I = I/(dist*dist); 

Vec h = (dirToLight + dirToCamera)/((dirToLight + dirToCamera).magnitude()); 

Vec kd = material.kd; 
Vec ks = material.ks; 

Vec diffuse = kd*I*fmax(0.0, intersectionNormal.dot(dirToLight)); 

Vec specular = ks*I*pow(fmax(0.0, intersectionNormal.dot(h)), material.r); 

return diffuse+specular; 

} 

我已經提供了我的輸出和預期的輸出。照明看起來有點不同b/c我最初是一個.exr文件,另一個是.png,但我在輸出中繪製了箭頭,表面應該反射陰影,但事實並非如此。

My output

Expected output

+0

你可以減少你的例子中的幾何體渲染到兩個或三個對象和一個燈?用圖像的複雜程度來診斷有點困難。 –

+0

另外,就像你說的那樣,圖像應該是「反射代碼註釋掉」的例子之一嗎?我問,因爲兩者都顯示反映。 –

+0

@ DanielA.Thompson這兩個圖像都是用反射代碼創建的。頂部圖像是我的輸出,底部圖像是預期的輸出。 (我提到註釋反射代碼的唯一原因是爲了澄清陰影代碼正常工作,所以它以某種方式反射代碼來引入該錯誤。) – cph2117

回答

1

幾件事情要檢查:

  1. 在內for循環的清晰視野檢查可能會返回一個假陽性(即它的計算,所有surfaces[k]是不比你的交叉點更接近lights[j],對於一些j)。這會導致它錯誤地添加light[j]accumulator的貢獻。這會導致缺少陰影,但它應該到處發生,包括您的頂級遞歸級別,而您只在反射中看到缺少陰影。

  2. color()方法中可能會出現錯誤,該方法返回一些錯誤值,然後遞增到accumulator。雖然沒有看到該代碼,但很難確定。

  3. 您在materialToUse.IsReflective()檢查中使用recursion_depth後綴減量。你能證實recursion_depth的遞減值實際上被傳遞給shade()方法調用嗎? (如果沒有,請嘗試更改爲前綴遞減)。

    return this->shade(... recursion_depth--)... 
    

編輯:您能也驗證recursion_depth只是一個參數來shade()方法,即不存在一個全局/靜態recursion_depth任何地方。假設不存在(並且不應該有),就可以改變到以上

return this->shade(... recursion_depth - 1)... 

EDIT 2的呼叫:一對夫婦的其他事情查看:

  • color(),我不明白你爲什麼在計算中包含相機的方向。每個像素的第一個交點的顏色應該與攝像機的位置無關。但我懷疑這是造成這個問題的原因。

  • 驗證return this->shade(accumulator, r, surfaces, lights, recursion_depth--).mult(materialToUse.km);是否在矩陣乘法中做了正確的事情。你爲什麼乘以materialToUse.km

  • 驗證materialToUse.km每個表面的常量(即它不會改變表面幾何形狀,迭代深度或其他)。

  • 分手聲明return this->shade(accumulator, r, surfaces, lights, recursion_depth--).mult(materialToUse.km);到其組件對象,所以你可以看到中間結果在調試器:

    Vec reflectedColor = this->shade(accumulator, r, surfaces, lights, recursion_depth - 1); 
    Vec multipliedColor = reflectedColor.mult(materialToUse.km); 
    return multipliedColor; 
    
  • 確定圖像(X,Y)你的問題的一個像素的座標。設置渲染該像素時觸發的條件斷點,然後逐步執行shade()方法。假設您選擇示例圖像中右下箭頭指向的像素,應該看到一個遞歸到shade()。通過第一次遞歸,你會發現你的代碼不正確地添加了來自地板的光照,當它應該在陰影中時。

  • +0

    我很懷疑它會是#1,因爲正如你所提到的那樣,陰影也會出現問題。另外,當'break'語句發現一個比'closestIntersection'更接近的表面時,馬上退出'for'循環。後綴遞減不正確,所以我切換到'recursion_depth-1'。但'recursion_depth'從來沒有達到0,只有19。難道是因爲反射光線撞擊了一個不反射的表面?這個可以嗎? – cph2117

    +0

    我也加了'color()'函數好好衡量 – cph2117

    +0

    「但是recursion_depth從來沒有命中0,只有19。」如果你擊中反射面,'recursion_depth'只會減少。如果它是18,那意味着你的調用堆棧是三深的(當前射線已經連續擊中了2個反射面,並且你正在考慮第三個面,等等)。此外,您可能會發現將迭代次數計爲上而不是下降次數更直觀,即從「0」開始,每次遞歸都遞增,並更改基本情況以檢查「recursion_limit」(您預先定義爲全球,= 20或其他)。 –

    1

    回答我自己的問題:我沒有檢查t應該小於從交叉點到燈光位置的距離。

    相反的:

    if (t > EPSILON && t < closestIntersection) { 
        visible = false; 
        break; 
    } 
    

    它應該是:

    if (t > EPSILON && t < max_t) { 
        visible = false; 
        break; 
    } 
    

    其中max_t

    double max_t = dirToLight.magnitude(); 
    

    之前dirToLight已經常態化。

    +0

    您是否能夠在頂級遞歸級別觀察到這種情況?如果沒有,我認爲這只是幾何圖形在你的例子中分佈的一個神器? –

    +0

    我不是,所以你可能是對的。 – cph2117