【Ray Tracing The Next Week 超詳解】 光線追蹤2-8 Volume

 

 Prefacenode

今天有兩個東東,一個是體積煙霧,一個是封面圖dom

下一篇咱們總結項目代碼ide

 

Chapter 8:Volumes函數

咱們須要爲咱們的光線追蹤器添加新的物體——煙、霧,也稱爲participating media。 咱們還須要補充一個材質——次表面散射材質,它有點像物體內的濃霧。測試

體渲染一般的作法是,在體的內部有不少隨機表面,來實現散射的效果。好比一束煙能夠表示爲,在這束煙的內部任意位置,均可以存在一個面,以此來實現煙、霧spa

 

對於一個恆定密度體,一條光線經過其中的時候,在煙霧體中傳播的時候也會發生散射,光線在煙霧體中能傳播多遠,也是由煙霧體的密度決定的,密度越高,光線穿透性越差,光線傳播的距離也越短。從而實現煙霧的透光性。code

引用書中一張圖(光線可穿透可散射)orm

當光線經過體積時,它可能在任何點散射。 光線在任何小距離dL中散射的機率爲:blog

機率= C * dL,其中C與體積的光密度成比例。ip

對於恆定體積,咱們只須要密度C和邊界。 

 

/// isotropic.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the isotropic-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

class isotropic :public material
    {
public:
    isotropic(texture* tex) :_albedo(tex) {  }

    virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override
        {
        scattered = ray(info._p, lvgm::random_unit_sphere());
        attenuation = _albedo->value(info._u, info._v, info._p);
        return true;
        }

private:
    texture * _albedo;
    };

} // rt namespace

 

這個材質的散射原理和漫反射磨砂材質的大同小異,均屬於碰撞點轉換爲新視點,沿任意方向發射新的視線,只不過漫反射的視線方向向量指向外相切球體表面,而isotropic的視線方向指向以碰撞點爲球心的單位球體表面

區別就在於

漫反射的散射光線不可能指到物體內部,它必定是散射到表面外部(視線方向指向外切球體表面)

isotropic材質的散射光線能夠沿原來的方向一往前,以此視線透光性

由於煙霧內部只是顆粒而不存在真正不可穿透的幾何實體,因此漫反射實體不可穿透,只能散射到表面外部,而煙霧可穿透

 

接下來咱們看一下煙霧體

/// constant_medium.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the constant_dedium-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------


#pragma once

namespace rt
{

class constant_medium :public intersect
    {
public:
    constant_medium(intersect* p, rtvar d, texture* tex);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _item;

    rtvar _density;    //煙霧密度

    material* _materialp;
    };



inline constant_medium::constant_medium(intersect* p, rtvar d, texture* tex)
    :_item(p)
    ,_density(d)
    ,_materialp(new isotropic(tex))
    {
    }

aabb constant_medium::getbox()const
    {
    return _item->getbox();
    }

bool constant_medium::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const 
    {
    hitInfo info1, info2;
    if (_item->hit(sight, -rtInf(), rtInf(), info1)) {
        if (_item->hit(sight, info1._t + 0.0001, rtInf(), info2)) {
            if (info1._t < t_min)
                info1._t = t_min;
            if (info2._t > t_max)
                info2._t = t_max;
            if (info1._t >= info2._t)
                return false;
            if (info1._t < 0)
                info1._t = 0;
            float distance_inside_boundary = (info2._t - info1._t)*sight.direction().normal();
            float hit_distance = -(1 / _density)*log(lvgm::rand01());
            if (hit_distance < distance_inside_boundary) {
                info._t = info1._t + hit_distance / sight.direction().normal();
                info._p = sight.go(info._t);
                info._n = rtvec(1, 0, 0);  // arbitrary
                info._materialp = _materialp;
                return true;
                }
            }
        }
    return false;
    }

} // rt namespace

 

 

hit函數裏面是一些邊界合法性檢測

 

場景測試代碼

intersect* cornell_smoke()
{
    intersect ** list = new intersect*[9];

    int cnt = 0;
    material* red = new lambertian(new constant_texture(rtvec(0.65, 0.05, 0.05)));
    material * blue = new lambertian(new constant_texture(rtvec(0.05, 0.05, 0.73)));
    material* white = new lambertian(new constant_texture(rtvec(0.73, 0.73, 0.73)));
    material* green = new lambertian(new constant_texture(rtvec(0.12, 0.45, 0.15)));
    material* light = new areaLight(new constant_texture(rtvec(7, 7, 7)));

    list[cnt++] = new xz_rect(113, 443, 127, 432, 550, light);
    list[cnt++] = new flip_normal(new xz_rect(113, 443, 127, 432, 550, light));
    list[cnt++] = new flip_normal(new yz_rect(0, 555, 0, 555, 555, green));
    list[cnt++] = new yz_rect(0, 555, 0, 555, 0, red);
    list[cnt++] = new flip_normal(new xz_rect(0, 555, 0, 555, 555, white));
    list[cnt++] = new xz_rect(0, 555, 0, 555, 0, white);
    list[cnt++] = new flip_normal(new xy_rect(0, 555, 0, 555, 555, blue));

    intersect* box1 = new translate(new rotate_y(new box(rtvec(), rtvec(165, 165, 165), white), -18), rtvec(130, 0, 65));
    intersect* box2 = new translate(new rotate_y(new box(rtvec(), rtvec(165, 320, 165), white), 15), rtvec(265, 0, 295));

    list[cnt++] = new constant_medium(box2, 0.006, new constant_texture(rtvec(0.8, 0.58, 0.)));
    list[cnt++] = new constant_medium(box1, 0.008, new constant_texture(rtvec(0.9, 0.2, 0.72)));

    return new intersections(list, cnt);
}

 

下面是效果:sample -> 1000

 

註釋 // arbitrary處爲隨機方向,以前爲(1,0,0)

我以爲改成(rand01(),rand01(),rand01())較爲合適,下面是改以後的效果

 

Chapter 9:A Scene Testing All New Features

最後的封面圖是這樣一個場景:

體積霧:有一個藍色的次表面散射球體,可是這個東西並無單獨實現,而是把它包裹在了一個玻璃球內。

體積霧:整個場景是由層薄薄的霧氣籠蓋着的

長方體:地面是一堆隨機長方體

玻璃球:一個做爲藍色煙霧的容器,一個是單純的玻璃球

映射紋理:地球紋理球體

過程紋理:柏林噪聲紋理球體

運動模糊球體:有一個棕色的運動球體

鏡面球體:銀白色的鏡面球

區域光照:頂部是一個長方形的區域光源

其餘未說明材質的都是漫反射

渲染器中剩下的最大缺陷是沒有陰影射線,但這就是爲何咱們容易得到焦散和次表面散射效果的緣由。

 

下面是渲染代碼,關於相機參數設置還需等待渲染結果出來才能公佈

VS四開,渲染了一天還沒完,我也實屬無奈

 

intersect* finalScene()
{
    int nb = 20;
    intersect ** list = new intersect*[30];
    intersect ** boxlist = new intersect*[2000];
    intersect ** boxlist2 = new intersect*[2000];
    
    material * white = new lambertian(new constant_texture(rtvec(0.73, 0.73, 0.73)));
    material * ground = new lambertian(new constant_texture(rtvec(0.48, 0.83, 0.53)));

    int b = 0;
    for (int i = 0; i < nb; ++i)
        for (int j = 0; j < nb; ++j)
        {
            rtvar w = 100;
            rtvar x0 = -1000 + i*w;
            rtvar z0 = -1000 + j*w;
            rtvar y0 = 0;
            rtvar x1 = x0 + w;
            rtvar y1 = 100 * (lvgm::rand01() + 0.01);
            rtvar z1 = z0 + w;
            boxlist[b++] = new box(rtvec(x0, y0, z0), rtvec(x1, y1, z1), ground);
        }

    int l = 0;
    list[l++] = new bvh_node(boxlist, b, 0, 1);
    material * light = new areaLight(new constant_texture(rtvec(10, 10, 10)));
    list[l++] = new xz_rect(123, 423, 147, 412, 550, light);
    rtvec center(400, 400, 200);
    list[l++] = new moving_sphere(center, center + rtvec(30, 0, 0), 0, 1, 50, new lambertian(new constant_texture(rtvec(0.7, 0.3, 0.1))));
    list[l++] = new sphere(rtvec(260, 150, 45), 50, new dielectric(1.5));
    list[l++] = new sphere(rtvec(0, 150, 145), 50, new metal(new constant_texture(rtvec(0.8, 0.8, 0.9)), 10.0));
        
    intersect * boundary = new sphere(rtvec(360, 150, 145), 70, new dielectric(1.5));
    list[l++] = boundary;
    list[l++] = new constant_medium(boundary, 0.2, new constant_texture(rtvec(0.2, 0.4, 0.9)));
    boundary = new sphere(rtvec(), 5000, new dielectric(1.5));
    list[l++] = new constant_medium(boundary, 0.0011, new constant_texture(rtvec(1., 1., 1.)));
    
    int x, y, n;
    unsigned char * tex = stbi_load("earthmap.jpg", &x, &y, &n, 0);
    material * emat = new lambertian(new image_texture(tex, x, y));
    list[l++] = new sphere(rtvec(400, 200, 400), 100, emat);
    texture * pertext = new noise_texture(0.1);
    list[l++] = new sphere(rtvec(220, 280, 300), 80, new lambertian(pertext));
    int ns = 1000;
    for (int j = 0; j < ns; ++j)
        boxlist2[j] = new sphere(rtvec(165 * lvgm::rand01(), 165 * lvgm::rand01(), lvgm::rand01()), 10, white);

    list[l++] = new translate(new rotate_y(new bvh_node(boxlist2, ns, 0, 1), 15), rtvec(-100, 270, 395));

    return new intersections(list, l);
}

 

/-----------------------更新線-------------------------------/

對應的相機參數

 

 

效果

其中,薄霧效果過重了,霧氣參數應該小一點,大約在1e-4左右較好

vfov可能太大了,45°應該更好一點吧

鏡頭應該更靠近些

 

感謝您的閱讀,生活愉快~

相關文章
相關標籤/搜索