《One Weekend光线追踪》中ray_color函数的逻辑正确性疑问
《Ray Tracing in One Weekend》ray_color函数逻辑解析
原书ray_color函数代码
color ray_color(const ray& r, int depth, const hittable& world) const { // If we've exceeded the ray bounce limit, no more light is gathered. if (depth <= 0) return color(0,0,0); hit_record rec; if (world.hit(r, interval(0.001, infinity), rec)) { ray scattered; color attenuation; if (rec.mat->scatter(r, rec, attenuation, scattered)) return attenuation * ray_color(scattered, depth-1, world); return color(0,0,0); } vec3 unit_direction = unit_vector(r.direction()); auto a = 0.5*(unit_direction.y() + 1.0); return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0); }
疑问与问题根源
你遇到的测试异常,核心是对depth参数的定义理解错误:
- 原函数中
depth代表剩余的光线反弹次数,而非总发射次数。初始调用时必须传入足够大的值(比如20、50),而非1或2。 - 当你设置
depth=1时,初始射线命中物体后,递归调用的depth-1=0直接返回黑色,导致最终结果为黑——这完全符合函数逻辑:depth=1意味着只允许初始射线的一次检测,不允许任何反弹。 - 当
depth=2时,仅允许一次反弹:若反弹后的射线命中物体,再次递归时depth=0返回黑色,所以该路径的反射部分为黑;若反弹射线未命中物体,则返回天空色,这部分是正常的。
原函数正确性说明
原函数的逻辑完全符合光线追踪的路径累积规则:
- 光线耗尽终止:当
depth<=0,说明光线已达到最大反弹次数,不再收集光线,返回黑色(无颜色贡献)。 - 命中物体处理:若射线命中物体,调用材质的
scatter方法生成反弹射线,并获取该材质的颜色衰减值attenuation。递归计算反弹射线的颜色后,与attenuation相乘——这一步是累积当前材质对光线颜色的贡献。 - 未命中返回天空色:若射线未命中任何物体,返回渐变天空色,作为该路径的最终颜色来源。
只有当初始调用传入足够大的depth(比如50),才能让光线完成多次反弹,得到正确的漫反射、反射效果。
你的修改后代码
Color RayTracer::TraceRay(const Ray& ray, int depth, float weight) { if (depth <= 0) return Utils::Colors::BLACK; HitPayload hp = FindClosestHit(ray); if (hp.t < 0.0f) { // We didn't hit anything, just return a sky color of some kind. const float alpha = 0.5f * (ray.Dir().y + 1.0f); const Color skyColor = (1.0f - alpha) * Utils::Colors::WHITE + alpha * Utils::Colors::SKY_COLOR; return skyColor * weight; } const Material* mat = m_Scene->GetMaterial(hp.Shape->GetMaterialIdx()); Color colorOut{}; Ray rayOut{}; rayOut.SetOrigin(hp.Position + Utils::Constants::EPSILON * hp.Normal); // TODO: Cast shadow ray(s) if (mat->Shade(ray, rayOut, colorOut, hp)) if (depth == 1) return colorOut; else return (weight * colorOut) * TraceRay(rayOut, depth - 1, 0.75f * weight); // Material is a black body return Utils::Colors::BLACK; }
你的修改引入了weight参数来累积衰减,但其实原函数的attenuation已经承担了这个角色。另外,你在depth==1时直接返回colorOut的逻辑,相当于强制让初始射线命中物体时直接返回材质颜色,忽略了后续可能的光线贡献(比如物体反射天空的颜色),这会导致场景缺少真实的光影交互。
内容的提问来源于stack exchange,提问作者unusually_crispy_crisps
相关产品推荐
相关产品推荐

