【RAY TRACING THE REST OF YOUR LIFE 超詳解】 光線追蹤 3-7 混合概率密度


 

 Preface

注:鑒於很多網站隨意爬取數據,可能導致內容殘缺以及引用失效等問題,影響閱讀,請認准原創網址:

https://www.cnblogs.com/lv-anchoret/category/1368696.html

 

我們這節主要講把之前的概率密度做混合,以得到更好的效果

我們上一篇以前經常用關於cos函數的pdf,上一節用的是與光源采樣相關的pdf,那么,我們把兩者結合到一起,協調它們之間的比例,我們就可以得到一個有着兩種概率密度模型的pdf,這往往是更貼近生活的,那么我們今天就來學習測試一下。

 

 Ready

這一節就是把前幾篇的概率密度做混合,所以,需要的就是熟悉之前的內容。

當然,之前的框架代碼也比較丑,基本都是在lerp函數里面做調整,所以,我們順便把框架搭得更好一點

 

 正文

我們都知道,設計pdf的一個很重要的原則就是使得累積概率密度達到且只達到1,所以,我們先采用一種非常簡單的比例協調方式混合兩個pdf。

例如我們有如下的混合密度方程

pdf_mixture(direction) = 1/2 * pdf_reflection(direction) + 1/2 * pdf_light(direction)

即,兩者各占一半

要實現兩者,代碼描述也很簡單:

if ( rand01() < 0.5 )
    pdf_reflection();  ...
else
    pdf_light();  ...
但是評估pdf_mixture會稍微有點微妙。 我們需要同時評估pdf_reflection和pdf_light,因為有一些方向可以生成pdf方向。 例如,我們可以使用pdf_reflection生成朝向光的方向
 
如果我們回顧之前的內容,你會發現,這一部分主要解決兩個問題:
1.此處的pdf函數值
2.按照某個隨機模型產生一個隨機數
 
我們抽象出這些操作之后,就可以寫一個關於我們的pdf的一個基類:
///pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        In the Monte Carlo system, pdf acts as the
//                most important element of Important-Sample
// -----------------------------------------------------


#pragma once


namespace rt
{

// the basic class of pdf system
class pdf
    {
public:
    /*
    @brief: we get the value of pdf function by this interface
    @param: the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec & direction)const = 0;

    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur: the Three-dimensional random vector
    */
    virtual rtvec generate()const = 0;
    };


}//rt namespace

 

我們來實現關於它的一些子類

首先我們來實現關於cosine 概率密度的模型

///cosine_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        one of the pdf' forms
// -----------------------------------------------------


#pragma once


namespace rt
{

class cosine_pdf :public pdf
    {
public:
    //constructor
    cosine_pdf(const rtvec& w);            

    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const;


    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const;

private:

    onb _uvw;
    };

inline cosine_pdf::cosine_pdf(const rtvec& w)
    {
    _uvw.build_from_w(w);
    }

rtvar cosine_pdf::value(const rtvec& direction)const
    {
    rtvar cosine = dot(direction.ret_unitization(), _uvw.w());
    if (cosine > 0.)
        return cosine / π;
    else
        return 0.;
    }

rtvec cosine_pdf::generate()const
    {
    return _uvw.local(random_cosine_direction());
    }
}

這個模型之前細說過,cosine大於0的時候返回cosine/π,反之,則返回0。因為光線反射之后如果和表面法線的夾角為鈍角的時候,違反反射規律,不以反射。生成隨機數的那個之前也講過,在上上一篇

 

其實這些都不是新東西,就是把之前講的的那一套整合了一下

得到結果也就是之前的效果

我們把主函數里面的lerp()也改一下

 

每個像素點采樣100次,取均值,即sample 為 100時

 

 

 這是代碼敲錯了,意外得到的一張圖

 

 現在我們嘗試,光源采樣,即

///hit_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        toward to the hitable
// -----------------------------------------------------

#pragma once


namespace rt
{

class hit_pdf :public pdf
    {
public:

    /*
    @param: info -> Geometry information
            origion -> the point of intersection
    */
    hit_pdf(intersect* info, const rtvec& origion)
        :_intersectp(info)
        ,_o(origion)
    {
    }


    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const
        {
        return _intersectp->pdf_value(_o, direction);
        }
    

    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const
        {
        return _intersectp->random(_o);
        }

private:
        
    rtvec _o;

    intersect * _intersectp;
    };

}// rt namespace

 

對應的intersect類也要改一下

/// intersect.hpp
//https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [refre ]        2019.3
// [brief ]        the intersect-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once

#include "E:\OpenGL\光線追蹤\code\ray tracing 1-3\ray tracing 1-3\ray.hpp"


namespace rt
{
class material;
class aabb;


// the infomation of intersection point

struct hitInfo
    {
    lvgm::precision _t;        //ray 中的系數t
    rtvec _p;                //相交點、撞擊點
    rtvec _n;                //_p點的表面法線
    material* _materialp;    //材質
    rtvar _u;                //texture-u
    rtvar _v;                //texture-v
    };


// the statement of intersect class

class intersect
    {
public:

    /*
    @brief: 撞擊函數,求取撞擊點相關記錄信息
    @param: sight->視線
    系數t的上下界->篩選撞擊點
    info->返回撞擊點信息
    @retur: 是否存在合法撞擊點
    */
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const = 0;

    /*
    @brief: get the box of Geometry
    */
    virtual aabb getbox()const = 0;

    /*
    Get the value of pdf function
    */
    virtual rtvar pdf_value(const rtvec& o, const rtvec& v)const
        {
        return 0.;
        }

    /*
    generate the random number
    */
    virtual rtvec random(const rtvec& o)const
        {
        return rtvec(1, 0, 0);
        }


    };

}// rt namespace

 

因為我們現在只是拿區域光源做實驗,並不是所有的幾何體派生類都要繼承pdf相關的方法,所以,它們兩個以虛函數的形式存在即可。

那么就剩下xz長方形了

rtvar xz_rect::pdf_value(const rtvec& o, const rtvec& v)const
    {
    hitInfo rec;
    if (this->hit(ray(o, v), 1e-3, rt::rtInf(), rec))
        {
        rtvar area = (_x2 - _x1)*(_z2 - _z1);
        rtvar distance_squared = rec._t * rec._t * v.squar();
        rtvar cosine = fabs(dot(v, rec._n) / v.normal());
        return distance_squared / (cosine*area);
        }
    else
        return 0.;
    }

rtvec xz_rect::random(const rtvec& o)const
    {
    rtvec random_point = rtvec(_x1 + lvgm::rand01() * (_x2 - _x1), _other, _z1 + lvgm::rand01()*(_z2 - _z1));
    return random_point - o;
    }

 

 把上一篇寫在lerp函數里面的一大堆東西整合到類里面

那么我們的lerp就統一化了:

我們取sample為10,即可得到很好的效果:

 

現在我們將寫一個關於混合概率密度的類:

///mixture_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        mixture pdfs
// -----------------------------------------------------


#pragma once


namespace rt
{

class mixture_pdf :public pdf
    {
public:

    mixture_pdf(pdf * p1, pdf* p2)
        {
        _p[0] = p1;
        _p[1] = p2;
        }


    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const
        {
        return 0.5*_p[0]->value(direction) + 0.5*_p[1]->value(direction);
        }


    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const
        {
        if (lvgm::rand01() < 0.5)
            return _p[0]->generate();
        else
            return _p[1]->generate();
        }

private:

    pdf* _p[2];

    };


}// rt namespace

 

我們的lerp函數如下:

 

我們采樣10次得到:

但是覺得效果不是很理想,我們來做一些測試

 

1. pdf 方程修改為 mixture_pdf = 1/3 * hit_pdf + 2/3  * cosine_pdf

 

2. pdf 方程修改為 mixture_pdf = 2/3 * hit_pdf + 1/3  * cosine_pdf

 

3. random修改  2/3 取 hit_pdf產生的隨機值, 1/3 取 cosine_pdf 產生的隨機值

 

4. random修改  1/3 取 hit_pdf產生的隨機值, 2/3 取 cosine_pdf 產生的隨機值

 

我們去上述方案的3、1,即:

 

得到圖:

這張圖顯然比均分的效果要好

 

這里我們看不出到底是random起作用還是value,我們不妨取2、3組合

3把2的彩色噪聲消除了些,但是這張圖和原始的均分圖差不多一樣

 

所以結論,random和value的比例交叉比較好

 

我們采樣1000次得到:

 渲染中。。。。(就是清晰了點)

/***********************************************************************************/

跑了一晚上爬起來發現除零錯誤了,又抽空跑完了

/************************************************************************************/

 

本書第九章(下一章)介紹了一些關於當前渲染器的看法

作者在描述陰影光線和混合密度設計時,作者個人更偏向於混合密度設計,所以並沒有在渲染器中采用陰影光線

作者描述了關於lerp函數中內存問題以及編碼的不足

作者描述了關於玻璃材質和鏡面的一些處理方法

作者還描述了關於HDR的0~1浮點表示以及RGB分組的0~255表示,還說明了這個渲染器是RGB的且基於物理的,還有一種是基於光譜的,以及兩者結合的,但做起來很難,所以我們堅持RGB且基於物理的渲染器。

 

 感謝您的閱讀,生活愉快~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM