2016-10-28 45 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