本項目參考自《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))));
...
}
實現效果: