今天我們來學最后一章
Chapter11:Defocus Blur
Preface
散焦模糊 也稱 景深
首先,我們來了解一下散焦模糊,我們在真實相機中散焦模糊的原因是因為它們需要一個大圈(而不僅僅是一個針孔)來聚光。這會使所有東西都散焦,但是如果用小孔的話,那么通過前后調整相機鏡頭,就會使得一切景色都會聚焦到相機鏡頭中,也就是會匯聚到那個孔內。物體聚焦的那個平面的距離由鏡頭和膠片/傳感器之間的距離控制。這就是為什么當你改變焦點時可以看到鏡頭相對於相機移動的原因。
光圈是一個可以有效控制鏡頭大小的孔。對於真正的相機,如果你需要更多光線,你可以使光圈更大,同時也會獲得更多的散焦模糊。對於我們的虛擬相機,我們也需要一個光圈
真正的相機具有復雜的復合鏡頭。對於我們的代碼,我們可以模擬順序:傳感器,然后是鏡頭,然后是光圈,並找出發送光線的位置並在計算后翻轉圖像(圖像在膠片上倒置投影)。人們通常使用薄透鏡模擬近似。
引用書上一張圖(相機聚焦成像)
我們不需要這么復雜,我們通常從鏡頭表面開始射線,並將它們發送到虛擬膠片平面,方法是找到膠片在焦點平面上的投影(在距離focus_dist處)。
正文
前面說了一大堆,看着比較復雜,其實並沒有那么難
前言說了三件事情:
第一點,生活中的相機成像分為兩個部分,inside和outside,涉及3個物:film(膠片)、lens(鏡片)、focusPlane(焦點平面),而我們只需要outside部分
其次第二點,我們的眼睛(或者相機)不再是一個點而是眼睛所在的周圍圓盤上的隨機點,因為實際的相機是有攝像鏡頭的,攝像鏡頭是一個大光圈(很大一個鏡片),並不是針孔類的東東,所以,我們要模擬鏡頭,就要隨機采針孔周圍的光圈點。
可能有人不明白這里,可以回去看看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(), 0) - 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 }; } #endif
所以,我們用上一章的球體設置,把相機改一下,渲染一把
渲染效果就是開篇那張圖
晚安