基本概念介紹
求積分其實要麼是求面積,要麼就是在求體積。可能有的人會覺得積分很討厭,那麼一大堆奇怪的符號,不知道該怎麼辦,但是實際上這是最自然的去描述一個自然事物的公式。同學們可以去嘗試了解一下黎曼和,因為這個傢伙在我們的圖形學中也很重要,重要的是它的思想,只不過它用了一種逼近的方式去描述出了積分,如果說成是人話的話,那麼就是黎曼和用離散的數據去逼近求解得到了連續的積分。不過我們說了,這只是一種思想,實際上我們相信,這種思想只要是個人就能想到,只不過要形成一套整體的方法論和解決方案,我們需要把這些思想武器擺放到正確的位置上。
我們先前的文章介紹了蒙特卡洛方法,它是一種統計學的方法,那麼我們怎麼把這個統計學的方法與定積分聯繫起來呢?為什麼我們要做這樣的一套思路連接呢?那是因為我們在做PBS或者PBR的時候,我們會去一個半球上對一個小區域內的燈光進行球面積分,然後算出這些入射光最終得出了怎樣的一個色彩。
為什麼我們在PBS或者PBR裡要對一個半球上的光線做積分呢?難道我們在傳統光柵化的Lmabert光照的計算方式有什麼錯誤嗎?要解決這個問題,就需要同學們去學習光線追蹤了,我們之所以要在一個半球上對光線做積分,最主要的原因是在現實世界中,射入我們眼睛的光線是由很多光線共同作用的結果,而不是我們在光柵化中提到的那個一個入射光對應一個出射光的模型。所以我們要儘可能的採樣更多的光束,才能得到一個相對正確的結果。這也是PBS或者PBR是基於物理的渲染的原因,我們沒有說這套渲染方式是:物理渲染。其中的含義就是,我們採用的是一套逼近物理世界的算法在進行渲染,因此叫:Physical Based Rendering或者是Physical Based Shading。
與定積分做連接
我們知道真實的環境中,射到物體表面的光線數量是無限多的,所以我們這裡要採用統計學的方式去模擬這種效果,因此我們就需要背後的這套蒙特卡洛方法與無限多的入射光的這一現實問題做思路的對接。
為了說明蒙特卡洛方法是如何解決現實中的連續數據問題的,我們來看看下面這個小例子,這個例子其實就是在求在第一象限,
其實還是跟之前那個求PI的思路一樣,我們之前是在一個邊長為2的盒子裡,算隨機出來的點落在圓裡面的概率,那麼我們依照這個思路,我們在第一象限裡,執行n次下面的操作:
在0~2的範圍內隨機出一個點x,然後求出
把所有第一步求出來的
然後用sum乘以2再除以n
這便得到了
#include <iostream>#include <stdio.h>#include <math.h>float randf() { return float(rand()) / float(RAND_MAX);}float srandf() { return randf()*2.0f - 1.0f;}int main(int argc, char **argv) { int count = 1000000; float sum = 0.0f; for (int i=0;i<count;++i){ float x = randf()*2.0f; sum += x * x; } printf("area is %f\n", 2.0f*sum /float(count)); return 0;}上面這些代碼為什麼可以這麼寫呢?背後我們出於怎樣的思路把代碼寫成這樣子的呢?因為我們的定積分公式表示的是
所以對於積分公式而言,我們求的就是這條曲線下面的這塊積分面積。這裡底邊x軸的邊長是2,如果我們把求面積的方法寫成近似的值的話,我們可以把它看成是把底邊切成n份,每一份的寬度是
所以我們最終求得的面積為:
完結撒花,這貌似就是一個求積分吧,跟蒙特卡洛方法有聯繫?當然是有聯繫的,我們看到具體的代碼實現,不難發現,我們的x並不是等於
如果沒辦法想明白這個x取隨機數的原因,那麼可以回想一下先前我們舉例的圓的例子,在一個方形內隨機一個點,落在圓裡的概率,通過那個概率我們可以求出PI的值。