You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《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返回黑色,所以该路径的反射部分为黑;若反弹射线未命中物体,则返回天空色,这部分是正常的。

原函数正确性说明

原函数的逻辑完全符合光线追踪的路径累积规则:

  1. 光线耗尽终止:当depth<=0,说明光线已达到最大反弹次数,不再收集光线,返回黑色(无颜色贡献)。
  2. 命中物体处理:若射线命中物体,调用材质的scatter方法生成反弹射线,并获取该材质的颜色衰减值attenuation。递归计算反弹射线的颜色后,与attenuation相乘——这一步是累积当前材质对光线颜色的贡献。
  3. 未命中返回天空色:若射线未命中任何物体,返回渐变天空色,作为该路径的最终颜色来源。

只有当初始调用传入足够大的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:43:18