c++ - 递归光线追踪器中的不正确反射

标签 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;

}

我已经提供了我的输出和预期的输出。照明看起来有点不同,因为我的最初是一个 .exr 文件,另一个是 .png 文件,但我在输出中绘制了箭头,其中表面应该反射阴影,但事实并非如此。

My output

Expected output

最佳答案

需要检查的几件事:

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

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

  3. 您正在 recursion_depth 上使用后缀递减里面materialToUse.IsReflective()查看。您能否验证 recursion_depth 的递减值实际上被传递到shade()方法调用? (如果没有,请尝试更改为前缀递减)。

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

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

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

编辑 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() 。逐步完成第一个递归,您会发现您的代码错误地添加了地板的光贡献,而它应该处于阴影中。

  • 关于c++ - 递归光线追踪器中的不正确反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40308953/

    相关文章:

    c++ - 本地餐厅自动早餐计费的问题 (c++)

    换行文本算法(换行文本以适应 'page' )

    c++ - 如何在 Q3DSurface 中为每个 QSurfaceDataRow 设置不同的颜色?

    java - 动态图形对象绘画

    matlab - 删除切片中的小值

    algorithm - 如何检查一个点是否在椭圆体内?

    c++ - OpenCv 实现高斯模糊

    c++ - 使用 C++20 多态性 Lambda 函数时出错

    C++ sqlite3 绑定(bind)参数

    java - 在 JApplet/Swing 中使用图形和小部件绘图?