一個簡單光線追蹤渲染器
簡單的介紹可以參考:圖形學基礎 | 光線追蹤
單獨看一個像素上的追蹤過程的話:
- 從視線方向發射一條射線;
- 取得射線與場景最近的交點;
- 取得交點的材質顏色;
- 如果材質包含反射或折射,則改變光線的方向;
- 尋找下一個交點.並重復(2). 直到在場景內找不到交點.或者達到達到最大跟蹤次數.
- 對於(2). 因爲需要遍歷場景. 開銷隨場景複雜度上升而上升.
對於一些簡單的示例場景中, 可以簡化爲 射線和球. 或者射線與平面的相交. 求最近的交點.
以下是實現代碼的幾個問題
光線投射
對像素空間的每一點,變換到世界座標系中.
發射一條光線,對光線進行跟蹤.
float aspect = width / (float)height; // 防止失真
for (size_t j = 0; j < height; j++) {
for (size_t i = 0; i < width; i++) {
// 將像素位置轉換成爲真實世界座標位置
/*
(i+0.5) 像素偏移量
+(i+0.5)/(width/2.0)* tan(fov / 2.)*aspect 世界座標系偏移量
起始世界座標 - 1*tan(fov / 2.)*aspect
*/
float x = (2* (i + 0.5) / (float)width - 1)*tan(fov / 2.)*aspect;
float y = -(2 * (j + 0.5) / (float)height - 1)*tan(fov / 2.);
// 發出的光線方向
Vec3f dir = Vec3f(x, y, -1).normalize();
framebuffer[i + j*width] = cast_ray(Vec3f(0, 0, 0), dir, spheres,lights);
}
}
射線與球的相交
參考之前的博客 射線與球的相交
反射光向量求解
折射光向量refract的計算
如何產生陰影
就是求出射線與物體的交點之後.
求解出 交點與 光源i 之間的 射線 light_dir
對 light_dir
進行跟蹤.
從交點出發,看看能不能找到與它相交的物體.
如果可以,那麼就是處於 該光源i的陰影處. 不應該進行光照計算.
// 應用光照
float diffuse_light_intensity = 0, specular_light_intensity = 0;
for (size_t i = 0; i < lights.size(); i++) {
// 光源的方向:交點指向光源
Vec3f light_dir = (lights[i].position - point).normalize();
float light_distance = light_dir.norm(); // 距離
// 判斷是否 light[i]這個光線找不到point
Vec3f shdow_orig = light_dir*N<0 ? point - N*1e-3 : point + N*1e-3;
Vec3f shadow_pt, shadow_N;
Material tempmaterial;
// 就是說從這個點出發,看看能不能找到與它相交的sphere,如果可以,那麼就是處於陰影.lights[i]找不到
if (scene_intersect(shdow_orig, light_dir, spheres, shadow_pt, shadow_N, tempmaterial))
continue;
diffuse_light_intensity += lights[i].intensity * std::max(0.f, light_dir*N);
specular_light_intensity += powf(std::max(0.f, -reflect(-light_dir, N)*dir), material.specular_exponent)
*lights[i].intensity;
}