【问题标题】:Improper reflection in recursive ray tracer递归光线追踪器中的不正确反射
【发布时间】:2017-03-11 13:47:48
【问题描述】:

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

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,但我在输出中绘制了箭头,其中表面应该反射阴影,但事实并非如此。

【问题讨论】:

  • 您能否将示例中的几何渲染减少到两个或三个对象和一个灯光?图像的复杂程度有点难以诊断。
  • 另外,如您所说,其中一个示例图像应该带有“反射代码注释掉”吗?我问是因为两者似乎都有反射。
  • @DanielA.Thompson 这两个图像都是使用反射代码创建的。上图是我的输出,下图是预期输出。 (我提到注释掉反射代码的唯一原因是为了澄清影子代码可以正常工作,因此在某种程度上是反射代码引入了错误。)
  • 好的。 recursion_depth 使用什么初始值?如果你增加它,问题会消失吗?
  • recursion_depth 是 20。增加到 100,并没有消失。

标签: c++ graphics 3d raytracing


【解决方案1】:

需要检查的几件事:

    1234563这会导致它错误地将light[j] 的贡献添加到您的accumulator。这会导致阴影丢失,但它应该在任何地方发生,包括您的顶级递归级别,而您只会在反射中看到丢失的阴影。
  1. color() 方法中可能存在错误,该方法返回了一些错误值,然后将其递增到 accumulator。虽然没有看到该代码,但很难确定。

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

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

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

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

编辑 2:需要注意的其他几件事:

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

  2. 验证return this-&gt;shade(accumulator, r, surfaces, lights, recursion_depth--).mult(materialToUse.km); 使用该矩阵乘法是否正确。为什么要乘以materialToUse.km

  3. 验证 materialToUse.km 在每个表面上是恒定的(即它不会改变表面的几何形状、迭代深度或其他任何东西)。

  4. return this-&gt;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,因为正如你提到的,阴影也会有问题。另外,break 语句在找到比closestIntersection 更近的表面时会立即退出for 循环。后缀减量不正确,所以我切换到recursion_depth-1。但是recursion_depth 从来没有达到 0,只有 19。可能是因为反射光线击中了不反射的表面吗?这样可以吗?
  • "但是 recursion_depth 永远不会达到 0,只有 19。" recursion_depth 只有在碰到反射面时才会减少。如果它是 18,则意味着您的调用堆栈深度为 3(当前光线已连续击中 2 个反射表面,并且您正在考虑第三个表面等)。此外,您可能会发现将迭代计数向上而不是向下计数更直观,即从 0 开始,每次递归递增,并更改基本情况以检查 recursion_limit(您将其预定义为全局,= 20 或其他)。
  • 这是有道理的。既然我已经包含了color(),对可能导致问题的原因有什么最后的想法吗?
【解决方案2】:

回答我自己的问题:我没有检查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 标准化之前。

【讨论】:

  • 您是否能够在顶级递归级别观察到这种情况?如果不是,我认为这只是您的示例中几何分布方式的产物?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-10
  • 2016-06-14
相关资源
最近更新 更多