1. 程式人生 > >【Ray Tracing in One Weekend 超詳解】 光線追蹤1-9 景深

【Ray Tracing in One Weekend 超詳解】 光線追蹤1-9 景深

 

 

今天我們來學最後一章

 

 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(), 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

    };

}

#endif
camera.h

 

 

所以,我們用上一章的球體設定,把相機改一下,渲染一把

 

渲染是真的慢。。。渲染完這張圖就睡了,玩不起

 

 算了,睡了,明早再掛圖

 

 

 晚安