【RAY TRACING THE REST OF YOUR LIFE 超詳解】 光線追蹤 3-1 蒙特卡羅 (一)



今天起,我們就開始學習第三本書了

  

 

 

這本書主要講的是蒙特卡羅渲染,以及相關的數學、術語概念等

這本書相較於前面兩本有着什么不同,承擔着什么樣的任務,尚涉書未深,姑妄言之:

第一本書,帶領我們初探光線追蹤技術,感受一下

第二本書,篇幅頁目最多,帶着我們一步一步,構建了一個“真正”的光線追蹤器,這里真正指的是,第二本書的內容較廣,涉及紋理、光照、煙霧,隨機初步等,所謂麻雀雖小,五臟俱全,此所謂“真正”

第三本書,讓我們更加走進了實際的光線追蹤器,偏向工業級的,所以,作者前言即講到,如果你想要從事光線追蹤相關的行業,這本書為你准備。學過第二本書,我們知道,越往后面,渲染效果越不盡人意,這里指的是如果采樣點過少(例如幾百),根本無法渲染出真實的效果,比如光照相關的Cornell box例子就有很多噪聲干擾,這些,玩玩還可以,但是,離專業還有距離,也就是它只是一個五臟俱全的劣質品,並不能稱得上是真正的光線追蹤器。

而這本書承擔的任務只有一個,就是利用Monte Carlo(MC)方法優化我們第二本書中的渲染效果,也就是為第二本書中的光線追蹤機器裝一個高端的引擎驅動內核零件,讓它更好。

 

我們先來一個簡單的開胃菜

 

chapter 1:A Simple Monte Carlo Program

蒙特卡羅方法(MC)是一種統計模擬方法,是一類很重要的數值計算方法,它是一種使用隨機數解決好很多實際問題的方法。

先來看一個很簡單的例子:估計π

有很多經典的方法,其中之一是

假設你扔了很多隨機的點到方框中,那么有一部分在圓內,其中圓內點和方框點的比例應該就是圓的面積和方框面積的比例,由此:

比例 = (π * R * R)/((2R)*(2R)) = π/4

所以上式和R無關,我們任意取R = 1,圓心位於原點,則

#include <iostream>
#include <lvgm\randfunc.hpp>
#define stds std::
using namespace lvgm;

void estimate_π(const size_t points)
{
    int inside = 0;
    for (int i = 0; i < points; ++i)
    {
        double x = 2 * rand01() - 1;
        double y = 2 * rand01() - 1;
        if (x*x + y*y < 1)
            inside++;
    }
    stds cout << "Estimate of π by" << points << "test points is " << 4 * double(inside) / points << stds endl;
}

int main()
{
  estimate_π(1000);
  estimate_π(10000);
  estimate_π(100000);
  estimate_π(1000000);
  estimate_π(10000000);
  estimate_π(10000000 / 2);
}

 

模擬結果為

 

 

 當然我們可以利用下面的程序使結果迅速逼近π

void lawDiminishingReturns()
{
    int inside = 0;
    int runs = 0;
    while (true)
    {
        runs++;
        double x = 2 * rand01() - 1;
        double y = 2 * rand01() - 1;
        if (x*x + y*y < 1)
            inside++;
        if(runs % 10000 == 0)
            stds cout << "Estimate of π by" << runs << "test points is " << 4 * double(inside) / runs << stds endl;
    }
}

結果:

.

 

一開始非常快速的逼近π,之后變化就比較緩慢了,這是一個收益遞減法(Law of Diminishing Returns)的例子

即每一個樣本對結果的收益少於后面一個,這個是MC的一個缺點,我們可以通過對樣本進行分層來減輕這種遞減收益,此法通常稱為抖動

我們進行網格划分,並在每個網格中選取一個樣本:

 

我們采用邊長為1e4的方框進行測試

void stratify()
{
    size_t inside{ 0 };
    size_t circle_stratified{ 0 };
    size_t sqrtAll = 1e4;
    for (int i = 0; i < sqrtAll; ++i)
        for (int j = 0; j < sqrtAll; ++j)
        {
            double x = 2 * rand01() - 1;
            double y = 2 * rand01() - 1;
            if (x*x + y*y < 1)
                inside++;
            x = 2 * ((i + rand01()) / sqrtAll) - 1;
            y = 2 * ((j + rand01()) / sqrtAll) - 1;
            if (x*x + y*y < 1)
                circle_stratified++;
        }
    stds cout << "Regular Estimate of π by 1e8 test points is " << 4 * double(inside) / 1e8 << stds endl;
    stds cout << "Stratified Estimate of π by 1e8 test points is " << 4 * double(circle_stratified) / 1e8 << stds endl;
}

 

圖片渲染運算讀寫文件的時候慢。。如今控制台運算輸出也整不動了。。。。

有意思~

 

分層方法能更好地收斂於漸近率。不足之處是,這個優勢隨着問題的維度而降低(例如,對於3D球體積版本,差距會更小)。 這被稱為維度詛咒(=.=)。 我們的工程將是非常高的維度(每個反射增加兩個維度),所以我不會在本書中進行分層。

但是,如果你做的是單反射或陰影或某些嚴格的2D問題,分層是個很好的選擇

 

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

 


免責聲明!

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



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