【Ray Tracing in One Weekend 超詳解】 光線追蹤1-9 景深
今天我們來學最後一章
Chapter11:Defocus Blur
Preface
散焦模糊 也稱 景深
首先,我們來了解一下散焦模糊,我們在真實相機中散焦模糊的原因是因為它們需要一個大圈(而不僅僅是一個針孔)來聚光。這會使所有東西都散焦,但是如果用小孔的話,那麼通過前後調整相機鏡頭,就會使得一切景色都會聚焦到相機鏡頭中,也就是會匯聚到那個孔內。物體聚焦的那個平面的距離由鏡頭和膠片/感測器之間的距離控制。這就是為什麼當你改變焦點時可以看到鏡頭相對於相機移動的原因。
光圈是一個可以有效控制鏡頭大小的孔。對於真正的相機,如果你需要更多光線,你可以使光圈更大,同時也會獲得更多的散焦模糊。對於我們的虛擬相機,我們也需要一個光圈
真正的相機具有複雜的複合鏡頭。對於我們的程式碼,我們可以模擬順序:感測器,然後是鏡頭,然後是光圈,並找出傳送光線的位置並在計算後翻轉影象(影象在膠片上倒置投影)。人們通常使用薄透鏡模擬近似。
引用書上一張圖(相機聚焦成像)
我們不需要這麼複雜,我們通常從鏡頭表面開始射線,並將它們傳送到虛擬膠片平面,方法是找到膠片在焦點平面上的投影(在距離focus_dist處)。
正文
前面說了一大堆,看著比較複雜,其實並沒有那麼難
前言說了三件事情:
第一點,生活中的相機成像分為兩個部分,inside和outside,涉及3個物:film(膠片)、lens(鏡片)、focusPlane(焦點平面),
其次第二點,我們的眼睛(或者相機)不再是一個點而是眼睛所在的周圍圓盤上的隨機點,因為實際的相機是有攝像鏡頭的,攝像鏡頭是一個大光圈(很大一個鏡片),並不是針孔類的東東,所以,我們要模擬鏡頭,就要隨機採針孔周圍的光圈點。
可能有人不明白這裡,可以回去看看1-3
看一下這張圖
這是之前我們講的光線追蹤的成像過程,從eye開始發射視線,這個掃描螢幕中的每個點,如果中間被物體遮擋,那麼計算,計算之後的畫素值為螢幕上該點的畫素值,如果沒有遮擋,那麼螢幕上那個點的畫素值就是背景對應的值
我們這裡只不過是把eye變為周圍單位圈內的隨機點,僅此模擬實際相機鏡頭
第三點,這個應該是上一章節提到的問題:
上一章節我們說了,為了方便,上一章節假定成像平面位於z = -1(或者是-w平面,按w基向量算)
所以 tan(theta/2) = (h/2) / dis ,其中dis為1
而這一章,我們使dis真正變成了一個變數,即:焦距(鏡片到成像平面之間的距離)
(圖片來自百度百科:)
隨之,我們的成像平面也就到了z = -focus,或者是-focus * w平面(按w基向量算)
所以,建構函式,我們就需要加兩個引數,改兩行行即可
還有就是單位圓盤取隨機點函式
const rtvec random_unit_disk() //find a random point in unit_disk { rtvec p; do { p = 2.0*rtvec(rtrand01(), rtrand01(), rtrand01()) - rtvec(1, 1, 0); } while (dot(p, p) >= 1.0); return p; }
下面是所有的camera類
/// camera.h // ----------------------------------------------------- // [author] lv // [begin ] 2019.1 // [brief ] the camera-class for the ray-tracing project // from the 《ray tracing in one week》 // ----------------------------------------------------- #ifndef CAMERA_H #define CAMERA_H #include "ray.h" namespace rt { class camera { public: camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus) :_eye(lookfrom) ,_lens_radius(aperture/2) { rtvar theta = vfov * π / 180; rtvar half_height = tan(theta / 2) * focus; //tan(theta/2) = (height/2) / 焦距 rtvar half_width = aspect * half_height; _w = (lookfrom - lookat).ret_unitization(); _u = cross(vup, _w).ret_unitization(); _v = cross(_w, _u); //向量運算 _start = _eye - half_width * _u - half_height * _v - focus * _w;//高和寬都乘了焦距,w也要乘,不然公式是錯的 _horizontal = 2 * half_width * _u; _vertical = 2 * half_height * _v; } inline const ray get_ray(const rtvar u,const rtvar v)const { rtvec rd = _lens_radius * random_unit_disk(); rtvec offset = _u * rd.x() + _v * rd.y(); return ray{ _eye * offset, _start + u*_horizontal + v*_vertical - _eye - offset }; } inline const ray get_ray(const lvgm::vec2<rtvar>& para)const { return get_ray(para.u(), para.v()); } inline const rtvec& eye()const { return _eye; } inline const rtvec& start()const { return _start; } inline const rtvec& horizontal()const { return _horizontal; } inline const rtvec& vertical()const { return _vertical; } inline const rtvec& u()const { return _u; } inline const rtvec& v()const { return _v; } inline const rtvec& w()const { return _w; } inline const rtvar lens_r()const { return _lens_radius; } private: rtvec _u; rtvec _v; rtvec _w; rtvec _eye; rtvec _start; //left-bottom rtvec _horizontal; rtvec _vertical; rtvar _lens_radius; //the radius of lens }; } #endifcamera.h
所以,我們用上一章的球體設定,把相機改一下,渲染一把
渲染是真的慢。。。渲染完這張圖就睡了,玩不起
算了,睡了,明早再掛圖
晚安