1. 程式人生 > >Q132:PBRT-V3,BSSRDF(雙向散射表面反射分佈函式)(5.6.2章節、11.4章節)

Q132:PBRT-V3,BSSRDF(雙向散射表面反射分佈函式)(5.6.2章節、11.4章節)

一、BSSRDF的定義

BSSRDF描述的是次表面中光的傳播。考慮次表面中光的傳播的話,光線離開次表面時的點(Po)和光線進入次表面時的點(Pi)不再是同一位置。如下方圖示:
這裡寫圖片描述

BSSRDF的定義式:

這裡寫圖片描述

BSSRDF對應的渲染方程的一般形式:

這裡寫圖片描述

二、BSSRDF的建模與近似

在給定po、wo、pi、wi時,總不能根據BSSRDF的定義式來求解S(po, wo, pi, wi)吧。
最直接的方法是建立各種BSSRDF模型來近似模擬真實的物理情況。

書上提供了這個近似模型:

這裡寫圖片描述
這個近似去除了位置(po, pi)和方向(wi,wo)之間的耦合。
其中各個分量的具體含義,原文截圖如下:
這裡寫圖片描述

三個分量,一個一個求解唄:

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

接下來,只剩下Sp分量啦,不過這個也是最為複雜的。

三、Sp(po, pi)

對Sp進行進一步近似和簡化:
這裡寫圖片描述

Sp(po, pi)表示Sp的值由po、pi的位置決定。
Sr(||po-pi||)表示Sr的值只由po、pi之間的距離決定。也就是說,到po相同距離的所有的pi對應的Sr值時相同的,這些所有的pi在一個以po為球心,||po-pi||為半徑的球面上,“Sr”中的“r”表示的就是“radial”。稱“Sr”為“半徑分量”好了。用Sr近似Sp,簡化了很多啊,有木有?

“半徑分量Sr”的值在各種材料屬性都確定的情況下,只和“半徑r”有關。但是,不同的材料對應著不同的材料屬性。所以,考慮“半徑分量Sr”的一般形式:
這裡寫圖片描述


尼瑪,這個形式也忒複雜了吧,必須簡化。

這裡寫圖片描述

到目前為止,“半徑分量Sr”已經簡化到只和(rho, r)相關。接下來,怎麼辦呢?

書上講到:
這裡寫圖片描述
即:“半徑分量Sr”的具體值存放在一個叫做“BSSRDFTable”的表中。
根據(rho, r)到“BSSRDFTable”的表中獲取對應的“半徑分量Sr”的值。

原來早就算好了所有的“半徑分量Sr”的值,來來回回各種簡化,最後原來是“查表”哈!

這裡寫圖片描述

“profile”這個表裡存放的是先前計算好的“半徑分量Sr”的值,相當於是對Sr進行取樣之後的取樣點。

現在要用這個表裡的資料,可不能只是簡單“讀取出來直接使用吧”。
應該怎麼辦?
“取樣”對應“重構”嘛。
“profile”表中儲存的是“半徑分量Sr”的取樣點,使用時當然得對取樣點做相應的“重構”。

書上取樣的重構方式是:雙三次張量插值(bi-cubic tensor interpolation)。

什麼意思?

先分別對rho_sample和r_sample進行雙三次(樣條)插值求得各自的權係數,然後求權係數的張量積,然後根據權係數的張量積對對應的“半徑分量Sr”的值進行累加。

關於rho_sample和r_sample的“樣條插值”,下圖假設r=0.5, rho=0.2:
這裡寫圖片描述
書上計算這個插值的函式是CatmullRomWeights()。

bool CatmullRomWeights(int size, const Float *nodes, Float x, int *offset,
                       Float *weights) {
    // Return _false_ if _x_ is out of bounds
    if (!(x >= nodes[0] && x <= nodes[size - 1])) return false;

    // Search for the interval _idx_ containing _x_
    int idx = FindInterval(size, [&](int i) { return nodes[i] <= x; });
    *offset = idx - 1;
    Float x0 = nodes[idx], x1 = nodes[idx + 1];

    // Compute the $t$ parameter and powers
    Float t = (x - x0) / (x1 - x0), t2 = t * t, t3 = t2 * t;

    // Compute initial node weights $w_1$ and $w_2$
    weights[1] = 2 * t3 - 3 * t2 + 1;
    weights[2] = -2 * t3 + 3 * t2;

    // Compute first node weight $w_0$
    if (idx > 0) {
        Float w0 = (t3 - 2 * t2 + t) * (x1 - x0) / (x1 - nodes[idx - 1]);
        weights[0] = -w0;
        weights[2] += w0;
    } else {
        Float w0 = t3 - 2 * t2 + t;
        weights[0] = 0;
        weights[1] -= w0;
        weights[2] += w0;
    }

    // Compute last node weight $w_3$
    if (idx + 2 < size) {
        Float w3 = (t3 - t2) * (x1 - x0) / (nodes[idx + 2] - x0);
        weights[1] -= w3;
        weights[3] = w3;
    } else {
        Float w3 = t3 - t2;
        weights[1] -= w3;
        weights[2] += w3;
        weights[3] = 0;
    }
    return true;
}

(原理在8.6.1章節,此處略去)

這裡寫圖片描述

關於這行程式碼,再補充說明一下:

        // Cancel marginal PDF factor from tabulated BSSRDF profile
        if (rOptical != 0) sr /= 2 * Pi * rOptical;

根據式子“11.9”,咱的目的是求解具體點pi對應的Sp,但是根據||po-pi||從Sr表格中查找出來的是“以po為圓心,||po-pi||為半徑的po處surface的切平面上一個圓周”對應的Sr。所以,針對單個點pi的Sr,需要在查詢結果的基礎上除以“周長”。

歸根到底是:簡化,近似,查表

流水賬式的記錄,差不多就是這樣~