《用一週學習光線追蹤》3.純色紋理和棋盤紋理

本項目參考自《Ray Tracing in One Weekend》系列
上接《用兩天學習光線追蹤》,繼續學習光線追蹤。
項目鏈接:https://github.com/maijiaquan/ray-tracing-with-imgui

目錄:
《用兩天學習光線追蹤》1.項目介紹和ppm圖片輸出
《用兩天學習光線追蹤》2.射線、簡單相機和背景輸出
《用兩天學習光線追蹤》3.球體和表面法向量
《用兩天學習光線追蹤》4.封裝成類
《用兩天學習光線追蹤》5.抗鋸齒
《用兩天學習光線追蹤》6.漫反射材質
《用兩天學習光線追蹤》7.反射向量和金屬材質
《用兩天學習光線追蹤》8.折射向量和電介質
《用兩天學習光線追蹤》9.可放置相機
《用兩天學習光線追蹤》10.散焦模糊

《用一週學習光線追蹤》1.動態模糊
《用一週學習光線追蹤》2.BVH樹、AABB相交檢測
《用一週學習光線追蹤》3.純色紋理和棋盤紋理


本節將實現純色紋理和棋盤紋理,效果如下:


回憶一下我們之前是如何定義lambertian的球體的:

list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1)));

我們將各個通道的反射率賦值給lambertian的構造函數,然後發射的射線命中該位置後,如果產生了散射光線,將該位置的反射率作爲係數,乘以散射光線所採樣得到的顏色值,從而實現該材質對射線的顏色吸收與反射。

hit_record結構體增加兩個變量u和v,目前還不會用上,後面貼圖纔會用上,加進來先:

struct hit_record
{
		...
    float u;
    float v;
};

texture抽象類:

class texture  {
    public:
        //返回一個三通道的顏色值,p爲命中終點座標
        virtual vec3 value(float u, float v, const vec3& p) const = 0;
};

純色紋理類constant_texture繼承自texture:

class constant_texture : public texture {
    public:
        vec3 color;

        constant_texture() { }
        constant_texture(vec3 c) : color(c) { }
        
        //返回一個三通道的color
        virtual vec3 value(float u, float v, const vec3& p) const {
            return color;
        }
};

修改lambertian:

class lambertian : public material
{
public:
    texture *albedo; //反射率
    lambertian(texture *a) : albedo(a) {}
    virtual bool scatter(const ray &r_in, const hit_record &rec, vec3 &attenuation, ray &scattered) const
    {
        vec3 s_world = rec.p + rec.normal + random_in_unit_sphere();
        scattered = ray(rec.p, s_world - rec.p, r_in.time()); //scattered爲散射光線
        attenuation = albedo->value(rec.u, rec.v, rec.p);     //注意這是各通道的反射率!
        return true;
    }
};

注意看第10行,此時的各通道的反射率已經和命中點的位置掛鉤了。

要定義並使用該紋理,跟上一節的邏輯本質上一樣:

list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(new constant_texture(vec3(0.4, 0.2, 0.1))));

constant_texture的物體表面的反射率不會隨着射線命中點的位置而變化,僅僅只是返回一個三通道的color而已,所以即使反射率已經和命中點的位置掛鉤了,着色還是跟之前一樣是純色。

注意,lambertian中的反射率變量,當texture爲constant_texture的時候,該值的意義爲constant_texture中的color,也就是說,反射率就相當於color。其實光追是一個逆向的過程,當前的反射率乘以散射光線採樣到的顏色值,其實是散射光線逆向照在命中點所對應的物體表面後反射的顏色,因爲顏色值相乘可以用來模擬光源照在物體後反射的顏色,具體可參考:現代OpenGL教程(七):基礎光照——顏色

如果要實現棋盤紋理的話,就要讓表面的反射率跟射線命中點的位置關聯起來:

//棋盤紋理
class checker_texture : public texture {
    public:
        texture *odd;
        texture *even;

        checker_texture() { }
        checker_texture(texture *t0, texture *t1): even(t0), odd(t1) { }
        
        virtual vec3 value(float u, float v, const vec3& p) const {
            float sines = sin(10*p.x())*sin(10*p.y())*sin(10*p.z());
            if (sines < 0)
                return odd->value(u, v, p);
            else
                return even->value(u, v, p);
        }
};

這樣一來,我們可以將棋盤的兩種constant_texture(純色材質)的指針賦值給checker_texture,實現棋盤紋理。

修改random_scene()

hittable *random_scene() {
    srand((unsigned)time(NULL));
    ...
    texture *checker = new checker_texture(new constant_texture(vec3(0.2, 0.3, 0.1)), new constant_texture(vec3(0.9, 0.9, 0.9)));
    list[0] =  new sphere(vec3(0,-1000,0), 1000, new lambertian(checker));

   	...
    list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(new constant_texture(vec3(0.4, 0.2, 0.1))));
    ...
}

實現效果:


參考資料:Ray Tracing: The Next Week

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章