递归光线跟踪器中的反射不正确

时间:2016-10-28 16:01:56

标签: c++ graphics 3d raytracing

我正在使用反射实现递归光线跟踪器。光线追踪器目前正在反射阴影区域,我不知道为什么。当反射代码被注释掉时,光线跟踪器的阴影方面按预期工作,所以我不认为这是问题。

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

2 个答案:

答案 0 :(得分:1)

要检查的几件事情:

  1. 内部for循环中的可见性检查可能会返回误报(即,它计算的所有surfaces[k]都不会比您的交叉点更接近lights[j],一些j)。这会导致它错误地将light[j]的贡献添加到您的accumulator。这会导致阴影丢失,但它应该发生在任何地方,包括你的顶级递归级别,而你只看到反射中缺少阴影。

  2. color()方法中可能存在错误,该错误会返回一些错误的值,然后递增到accumulator。虽然没有看到该代码,但很难确定。

  3. 您在recursion_depth检查中使用materialToUse.IsReflective()上的postfix减量。您能否验证recursion_depth的递减值是否实际传递给shade()方法调用? (如果没有,请尝试更改前缀减量)。

    return this->shade(... recursion_depth--)...
    
  4. 编辑:您还可以验证recursion_depth只是shade()方法的参数,即任何地方都没有全局/静态recursion_depth。假设没有(并且不应该),您可以将上面的调用更改为

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

    编辑2:还有其他几件事要看:

    1. color()中,我不明白为什么要在计算中包含相机的方向。每个像素的除第一个之外的交叉点的颜色应该与相机的位置无关。但我怀疑这是导致这个问题的原因。

    2. 验证return this->shade(accumulator, r, surfaces, lights, recursion_depth--).mult(materialToUse.km);使用该矩阵乘法做正确的事情。为什么要乘以materialToUse.km

    3. 验证materialToUse.km每个曲面是否恒定(即它不会改变曲面的几何形状,迭代深度或其他任何内容)。

    4. 将语句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;
      
    5. 确定一个有问题的像素的图像(x,y)坐标。设置在渲染该像素时触发的条件断点,然后逐步执行shade()方法。假设您选择示例图像中右下箭头指向的像素,您应该看到一个递归shade()。单步执行第一次递归,你会看到你的代码错误地添加了地板上的光线贡献,当它应该在阴影中时。

答案 1 :(得分: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规范化之前