上一节生成的图看起来很暗,有一个问题是因为有些物体反射的光线,会在 t = 0 时再次击中自己,而由于浮点数精度的问题,这些值可能是 0.00000001 或 -0.0000001 之类的任意接近0的浮点数,所以我们 hit 函数的 t_min 参数,需要忽略掉 0 附近的一小部分范围,防止物体发出的光线再次与自己相交。这样也就避免了阴影痤疮(Shadow ance)的产生。

修改 main.rs 中 ray_color 函数中的 word.hit(),改为 if let Some(hit_record) = world.hit(r, 0.001, f64::INFINITY),也就是 t_min 参数传值 0.001。

cargo run --release > diffuse_random_in_sphere.ppm 生成图如下

可以看到,已经比上一节生成的图亮了很多。

我们上一节使用的漫反射光线散射的方法,是在球体内部生成一个随机的点。然后,这样生成的向量,有很大的概率会和法线方向相近,并且及小概率会沿着入射方向反射回去。然而,真正的理想散射(Lambertian 反射)后的光线距离法向量比较近的概率会更高,但是分布规律会更加均衡,而实现这个方法,就是在球面上选取一个点,而不是在球内。我们可以通过在球内选取一个点,然后将其标准化,来得到球面上的点,因为我们的球是单位球。

在 vec3.rs 中 Vec3 的实现里,添加一个新的方法 random_unit_vector,代码如下

// src/vec3.rs
pub fn random_unit_vector() -> Vec3 {
    let point = Vec3::random_in_unit_sphere();
    return Vec3::unit_vector(point);
}

然后修改 main.rs 中的 ray_color 函数,将原来调用 random_in_unit_sphere 的代码改成调用 random_unit_vector

// src/main.rs ray_color 函数
let target: Vec3 =
            hit_record.p + hit_record.normal + Vec3::random_unit_vector();

cargo run --release > diffuse_random_unit_vector.ppm 生成图如下

可以看到整个画面更亮了,阴影部分也得到了改善。

还有另一种散射方法,还是先从球内随机一个点,然后判断这个点与光线打到的点的法线方向,如果方向一致,则返回这个点,否则,返回反向的点。代码如下

// src/vec3.rs
pub fn random_in_hemisphere(normal: Vec3) -> Vec3 {
    let in_unit_sphere = Vec3::random_in_unit_sphere();
    if Vec3::dot(in_unit_sphere, normal) > 0.0 {
        // 生成的点与法线在同一侧
        return in_unit_sphere;
    } else {
        return -in_unit_sphere;
    }
}

然后修改 main.rs 中的 ray_color 函数,将调用改为 random_in_hemisphere,代码如下

// src/main.rs
fn ray_color(r: &Ray, world: &dyn Hittable, depth: i32) -> Color {
    if depth <= 0 {
        return Color::zero();
    }

    if let Some(hit_record) = world.hit(r, 0.001, f64::INFINITY) {
        let target: Vec3 =
            hit_record.p + hit_record.normal + Vec3::random_in_hemisphere(hit_record.normal);
        let ray = Ray::new(hit_record.p, target - hit_record.p);
        return 0.5 * ray_color(&ray, world, depth - 1);
        // return 0.5 * (hit_record.normal + Color::one());
    }

    let unit_direction = Vec3::unit_vector(r.direction);
    let t = 0.5 * (unit_direction.y + 1.0);
    return (1.0 - t) * Color::one() + t * Color::new(0.5, 0.7, 1.0);
}

cargo run --release > diffuse_random_in_hemisphere.ppm 生成图如下

但是我生成的图,与原博客生成的图,不太一样,这张图应该和上一张差不多亮,阴影部分也不应该那么黑,目前还不知道哪里是否做的不对,如果有大佬知道,求指正。

完整代码: https://github.com/moeif/rtiow-rs/tree/09