v4vendeta's homepage

Home | About | Contacts | Blogs| Others

Ray Tracing in One Weekend Summary XV - Volumes

烟雾

光照射到volume上时,既有可能直接穿过,又可能发生反射,折射。光线在烟雾体中能传播多远,是由volume的密度决定的,密度越高,光线穿透性越差,光线传播的距离也越短。

我们在volume的内部添加一些随机方向的表面来实现这种效果

当光线通过体积时,它可能在任何点散射。 光线在任何小距离dL中散射的概率为:

概率 p=C*dL,其中C与volume密度成正比。


iostropic.h

class isotropic : public material {
    public:
        isotropic(texture *a) : albedo(a) {}
        virtual bool scatter(
            const ray& r_in,
            const hit_record& rec,
            vec3& attenuation,
            ray& scattered) const {

            scattered = ray(rec.p, random_in_unit_sphere());
            attenuation = albedo->value(rec.u, rec.v, rec.p);
            return true;
        }
        texture *albedo;
};

在isotropic.h中,volume中的散射方向是随机方向,与漫反射不同的是,漫反射的散射光线不可能指到物体内部,它一定是散射到表面外,isotropic材质的散射光线可以沿原来的方向一往前,以此实现透光性


constant_medium.h

class constant_medium : public hittable {
    public:
        constant_medium(hittable *b, float d, texture *a) : boundary(b), density(d) {
            phase_function = new isotropic(a);
        }
        virtual bool hit(
            const ray& r, float t_min, float t_max, hit_record& rec) const;
        virtual bool bounding_box(float t0, float t1, aabb& box) const {
            return boundary->bounding_box(t0, t1, box);
        }
        hittable *boundary;
        float density;
        material *phase_function;
};

bool constant_medium::hit(const ray& r, float t_min, float t_max, hit_record& rec)
const {
    const bool enableDebug = false;//用于debug
    bool debugging = enableDebug && random_double() < 0.00001;

    hit_record rec1, rec2;

    if (boundary->hit(r, -FLT_MAX, FLT_MAX, rec1)) {
        if (boundary->hit(r, rec1.t+0.0001, FLT_MAX, rec2)) {

            if (debugging) std::cerr << "\nt0 t1 " << rec1.t << " " << rec2.t << '\n';

            if (rec1.t < t_min)
                rec1.t = t_min;

            if (rec2.t > t_max)
                rec2.t = t_max;

            if (rec1.t >= rec2.t)
                return false;

            if (rec1.t < 0)
                rec1.t = 0;

            float distance_inside_boundary = (rec2.t - rec1.t)*r.direction().length();
            float hit_distance = -(1/density) * log(random_double());

            if (hit_distance < distance_inside_boundary) {

                rec.t = rec1.t + hit_distance / r.direction().length();
                rec.p = r.point_at_parameter(rec.t);

                if (debugging) {
                    std::cerr << "hit_distance = " <<  hit_distance << '\n'
                              << "rec.t = " <<  rec.t << '\n'
                              << "rec.p = " <<  rec.p << '\n';
                }

                rec.normal = vec3(1,0,0);  // arbitrary
                rec.mat_ptr = phase_function;
                return true;
            }
        }
    }
    return false;
}

hit函数里面是一些边界合法性检测

test

hittable *cornell_smoke() {
    hittable **list = new hittable*[8];
    int i = 0;
    material *red = new lambertian(new constant_texture(vec3(0.65, 0.05, 0.05)));
    material *white = new lambertian(new constant_texture(vec3(0.73, 0.73, 0.73)));
    material *green = new lambertian(new constant_texture(vec3(0.12, 0.45, 0.15)));
    material *light = new diffuse_light(new constant_texture(vec3(15, 15, 15)));

    list[i++] = new flip_normals(new yz_rect(0, 555, 0, 555, 555, green));
    list[i++] = new yz_rect(0, 555, 0, 555, 0, red);
    list[i++] = new xz_rect(213, 343, 227, 332, 554, light);
    list[i++] = new flip_normals(new xz_rect(0, 555, 0, 555, 555, white));
    list[i++] = new xz_rect(0, 555, 0, 555, 0, white);
    list[i++] = new flip_normals(new xy_rect(0, 555, 0, 555, 555, white));

    hittable *b1 = new translate(new rotate_y(new box(vec3(0, 0, 0), vec3(165, 165, 165), white), -18),vec3(130,0,65));
    hittable *b2 = new translate(new rotate_y(new box(vec3(0, 0, 0), vec3(165, 330, 165), white),  15),vec3(265,0,295));

    list[i++] = new constant_medium(b1, 0.01, new constant_texture(vec3(1.0, 1.0, 1.0)));
    list[i++] = new constant_medium(b2, 0.01, new constant_texture(vec3(0.0, 0.0, 0.0)));
        
    return new hittable_list(list,i);
}

渲染结果如下

.. ... ...