我正在实现带有反射的递归光线追踪器。光线追踪器当前正在反射阴影中的区域,我不知道为什么。当反射代码被注释掉时,光线追踪器的阴影方面会按预期工作,所以我认为这不是问题。
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 文件,但我在输出中绘制了箭头,其中表面应该反射阴影,但事实并非如此。
最佳答案
需要检查的几件事:
内部可见性检查
for
循环可能会返回误报(即,对于某些surfaces[k]
,它计算出所有lights[j]
都不比交点更接近j
)。这会导致它错误地添加light[j]
对您的贡献accumulator
。这会导致阴影丢失,但它应该发生在任何地方,包括您的顶级递归级别,而您只能在反射中看到丢失的阴影。color()
中可能存在错误返回一些错误值的方法,然后该值会增加到accumulator
。虽然没有看到该代码,但很难确定。您正在
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/