【PBRT】《基于物理的渲染:從理論到實踐》梳理II


一如既往,本文是筆者閱讀《基于物理的渲染:從理論到實踐》的總結(jié),文章不會面面俱到地描述書中所有的東西,只會把筆者認為重要的東西整理出來。既是一種復習,也希望能對讀者有幫助。整理范圍:第2章+第3章的前5節(jié)。

由于作者水平所限,文中如果有錯誤或者沒有解釋到位的地方,還請讀者不吝指正。

交點類(Interaction)

這是第2.10節(jié)的內(nèi)容,也是第2章的最后一節(jié)。之所以認為這個重要,有兩個原因:1、PBR意味著對光線和表面的交點進行著色。2、之前的內(nèi)容都是向量、法線、光線等等非常簡單而基礎的東西,不值得拿出來再寫一遍。

pbrt中的交點分為兩種,一個是表面交點(SurfaceInteraction),這個應該是我們所熟悉的交點。另一個是介質(zhì)交點(MediumInteraction),這個東西目前還沒有講到,猜測應該是和體積渲染有關(guān),計算光在介質(zhì)中反射或者折射的信息。于是,兩種交點就提取出了共有的基類:Interaction。

Interaction

Interaction類中的成員有:

Point3f p;
Float time;
Vector3f pError;
Vector3f wo;
Normal3f n;
MediumInterface mediumInterface;

一個個來解釋:

  • Point3f p:這是交點的位置信息。這非常容易理解,交點類都沒有交點的信息算什么!
  • Float time:時間。不知道是什么時間,也許是到這個點的時間,也許是計算得到這個點所用的時間。
  • Vector3f pError:我們計算交點信息的時候用的是浮點數(shù),浮點數(shù)不可能表示數(shù)學意義上的每一個數(shù),所以計算的時候會產(chǎn)生誤差。這個pError保存的就是一個保守的誤差范圍(就是誤差絕對不會超過的范圍)。
  • Vector3f wo:這是光線方向的相反方向?;蛘哒f是光線反射進入攝像機的方向。我們計算交點是假設有一條光線從攝像機的位置發(fā)出,然后和表面相交。這個wo就保存了這條光線的反方向,也就是前一篇文章中的\omega_o
  • Normal3f n:交點的法線。
  • MediumInterface mediumInterface:介質(zhì)接口。是交點所處的環(huán)境,或者說是光在什么介質(zhì)中穿行。目前為止還沒用到。

SurfaceInteraction

SurfaceInteraction類中包含了交點的幾何信息,這個類保存了許多跟點有關(guān)的計算需要用到的幾何信息,這樣在計算的時候就不用去管這個點在哪個物體上,所以你會看到:這個類居然連TM的Shape、BSDF、BSSRDF指針都有??!

好了,不吐槽了,正經(jīng)的。

SurfaceInteraction類的成員包括:

Point2f uv;
Vector3f dpdu, dpdv;
Normal3f dndu, dndv;
const Shape *shape = nullptr;

上面的這些代碼定義的是關(guān)于幾何表面的信息,其中,Shape是幾何表面,這個在第3章中有介紹,都是曲面和曲線(所以我才去啃了一個禮拜的《曲線和曲面的微分幾何》?。?。要理解這些變量,需要理解曲面是怎么表示的。參考曲面的定義(不了解的讀者可以參考我的前一篇文章。),曲面是由u,v兩個參數(shù)映射成的三維空間中的點。這里的uv變量就是這兩個參數(shù)的值,而dpdu和dpdv則是曲面關(guān)于u,v的偏導數(shù)。dndu和dndv則是曲面的法線關(guān)于u,v的偏導數(shù)。這些數(shù)據(jù)意味著當前點附近的點的位置的變化情況以及法線的變化情況,在計算中非常有用。

struct {
    Normal3f n;
    Vector3f dpdu, dpdv;
    Normal3f dndu, dndv;
} shading;

著色幾何信息,說實話我到現(xiàn)在還不知道有啥用。講了一大堆怎么設置,怎么計算,結(jié)果有啥用沒說,很蛋疼的感覺。

剩余的成員:

const Primitive *primitive = nullptr;
BSDF *bsdf = nullptr;
BSSRDF *bssrdf = nullptr;
mutable Vector3f dpdx, dpdy;
mutable Float dudx = 0, dvdx = 0, dudy = 0, dvdy = 0;

// Added after book publication. Shapes can optionally provide a face
// index with an intersection point for use in Ptex texture lookups.
// If Ptex isn't being used, then this value is ignored.
int faceIndex = 0;

BSDF和BSSRDF不用說,梳理I中講過,faceIndex是面片的索引。這世上絕大多數(shù)模型都不能用完美的數(shù)學公式來表示,通常的方法是采用三角形面片來近似,一個模型有許多三角形面片,而這個faceIndex指的就是面片的索引。具體有什么用,暫時還不知道。剩余的變量都沒有說明,所以也不去糾結(jié)。

球面

球面的表示和交點計算算是最難的一部分,也不是說它本身有多難,而是它要很多微分幾何的知識,比如說至少得知道第一基本形式和第二基本形式都是啥,有啥用。

廢話不多說,一起來看。

球面的表示

球面的坐標方程是
x^2 + y^2 + z^2 = r^2
這個誰都知道,但是,這個沒用!我們不僅要知道交點在哪,還要知道交點附近的點的變化趨勢是什么樣的。甚至,我們還需要知道如何將紋理貼到球表面上去,這些信息上面的方程給不了,所以,我們采用的是極坐標表示方式,因為它正好有兩個參數(shù)(對應u,v非常方便),而且計算偏導數(shù)也方便。

球的極坐標方程如下:
\begin{cases} x=r\sin\theta\cos\phi \\ y=r\sin\theta\sin\phi \\ z=r\cos\theta \end{cases}
其中\theta的取值范圍是0\pi\phi的取值范圍是02\pi。幾何意義上,\theta是和z軸正方向的夾角,而\phi是和x軸正方向,沿著x\to{y}方向旋轉(zhuǎn)的夾角,如下圖所示(下圖中沒有標出\theta角,但是把按照上面描述的,把\theta比對圖片帶入計算就可以發(fā)現(xiàn)是這樣的):


進一步,這只是球面的數(shù)學表示,要用類怎么表示呢?首先要想到,我們需要兩個參數(shù)和,這兩個參數(shù)是不是其中的和?顯然不是,因為如果和是和的話,那么又是什么?所以,和不是參數(shù)方程中的任何一個變量,我們?nèi)〉们蛎嫔系狞c,首先一條就是:這是一個確定的球!也就是說,、、都是固定值,只有和才是參數(shù)。

于是,球面關(guān)于uv參數(shù)方程是:
\begin{cases} \phi=u\phi_{max} \\ \theta=\theta_{min}+v(\theta_{max}-\theta_{min}) \end{cases}
代入到極坐標方程中就是
\begin{cases} x=r\sin(\theta_{min}+v(\theta_{max}-\theta_{min}))\cos(u\phi_{max}) \\ y=r\sin(\theta_{min}+v(\theta_{max}-\theta_{min}))\sin(u\phi_{max}) \\ z=r\cos(\theta_{min}+v(\theta_{max}-\theta_{min})) \end{cases}
雖然看上去很復雜,但這就是球關(guān)于uv的參數(shù)方程。其中,r,\theta_{min},\theta_{max},\phi_{max}都是固定值,\theta_{min}\theta_{max}\theta的取值范圍,\phi_{max}\phi能取得最大值,也就是說\phi的取值范圍是[0, \phi_{max}]。

所以,Shpere類的成員就有:

const Float radius;
const Float zMin, zMax;
const Float thetaMin, thetaMax, phiMax;

其中的zMin和zMax是與thetaMin和thetaMax對應的z坐標最小值和最大值。Sphere的構(gòu)造函數(shù)是這樣初始化這些值的:

Sphere(const Transform *ObjectToWorld, const Transform *WorldToObject,
       bool reverseOrientation, Float radius, Float zMin, Float zMax,
       Float phiMax)
    : Shape(ObjectToWorld, WorldToObject, reverseOrientation),
      radius(radius),
      zMin(Clamp(std::min(zMin, zMax), -radius, radius)),
      zMax(Clamp(std::max(zMin, zMax), -radius, radius)),
      thetaMin(std::acos(Clamp(std::min(zMin, zMax) / radius, -1, 1))),
      thetaMax(std::acos(Clamp(std::max(zMin, zMax) / radius, -1, 1))),
      phiMax(Radians(Clamp(phiMax, 0, 360))) {}

但是這個初始化代碼有個問題,那就是thetaMin、thetaMax和zMin、zMax的對應關(guān)系弄反了。從上面的式子我們知道,z=r\cos\theta,很明顯,\theta在區(qū)間[0, \pi]是單調(diào)遞減函數(shù),所以,當\theta取最小值的時候,對應的z應該取最大值才對。

光線與球面的交點

數(shù)學上,計算光線和球面的交點非常簡單,將光線的方程
r(t)=o+t\mathbfu0z1t8os
代入球面方程中計算就好了(其中,o是光線的起始點,\mathbfu0z1t8os是光線的方向,這兩個是固定值,t是參數(shù)變量)。但是,實際應用的過程不會這樣簡單。

  • 計算出的交點需要用到我們之前定義的SurfaceInterface類,我們需要填充很多信息,包括切向量,法向量以及偏導數(shù)等等。
  • 數(shù)學中我們可以認為光線是無限長的,但是實際應用過程中,我們沒那么多資源去計算無限長的光線,所以光線類(pbrt中是Ray)就會有一個最大長度的限制,也就是t的最大值tMax。計算過程中,我們需要舍棄交點值的t大于這個tMax的情況。
  • 數(shù)學意義上的數(shù)是精確數(shù),擁有無限的精度。計算機中用的是浮點數(shù),不管是單精度還是雙精度,都是一種近似數(shù),不能精確地計算出交點的位置。在多次使用這種近似數(shù)計算之后,誤差就會累積,甚至可能出現(xiàn)原本應該是交點,結(jié)果卻不是,或者反過來,這些情況。所以,誤差也需要考慮進去。
  • ……

有意思,從數(shù)學的抽象到落地成代碼的過程從來都是最有意思的部分!

計算交點t值

第一步,先計算交點,用的方程不是極坐標參數(shù)方程,而是普通的笛卡爾坐標系中的球的隱式方程:
x^2 \quad+\quad y^2 \quad + \quad z^2 \quad = \quad r^2
將光線的方程代入其中可以得到:
(o_x + t\mathbfu0z1t8os_x)^2+(o_y+t\mathbfu0z1t8os_y)^2+(o_z+t\mathbfu0z1t8os_z)^2=r^2
整理得到:
(\mathbfu0z1t8os_x^2+\mathbfu0z1t8os_y^2+\mathbfu0z1t8os_z^2)t^2+[2(\mathbfu0z1t8os_x{o_x}+\mathbfu0z1t8os_y{o_y}+\mathbfu0z1t8os_z{o_z})]t+(o_x^2+o_y^2+o_z^2-r^2)=0
這是一元二次方程的標準形式(at^2+bt+c=0),利用求根判別式就可以知道到底有沒有交點,有幾個交點。

這個過程在代碼中的實現(xiàn)是這樣的:

EFloat a = dx * dx + dy * dy + dz * dz;
EFloat b = 2 * (dx * ox + dy * oy + dz * oz);
EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius);

// Solve quadratic equation for _t_ values
EFloat t0, t1;
if (!Quadratic(a, b, c, &t0, &t1)) return false;

EFloat是pbrt中自定義的一個結(jié)構(gòu),與內(nèi)置的float相比,最大的區(qū)別就是EFloat里包含了誤差,可以理解成帶誤差的float。代碼的實現(xiàn)和我們的數(shù)學推理基本一致,它采用了一個Quadratic函數(shù)來求解t的值,如果沒有實數(shù)根,那么整個交點檢測的函數(shù)就返回false。

t值的有效性(t是否超出[0, tMax]范圍)

即便我們計算出t值了,這個t值也不一定是有效的,它必須在光線的“射程”范圍內(nèi)。因為計算出的t值用的是pbrt自定義的“帶誤差的float”結(jié)構(gòu),所以,實際檢測的時候我們需要考慮:

  • 如果兩個交點t0,t1都不在[0, tMax]的范圍內(nèi),那么兩個交點都舍棄,返回false,意味著沒有交點。
  • 先檢測t0,如果t0在[0, tMax]范圍內(nèi),那么就認為光線和球面的交點是t0。如果t0檢測失敗,那么就檢測t1,如果t1滿足范圍要求,就認為t1是光線和球面的交點。

寫成代碼就是:

// Check quadric shape _t0_ and _t1_ for nearest intersection
if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
EFloat tShapeHit = t0;
if (tShapeHit.LowerBound() <= 0) {
    tShapeHit = t1;
    if (tShapeHit.UpperBound() > ray.tMax) return false;
}

可以這樣實現(xiàn)是因為在計算t0和t1的時候已經(jīng)內(nèi)含了排序,t0的值必定是小于t1的。

t值的有效性(是否超出了球面的\theta\phi范圍)

我們在定義球面的時候確定了\theta的最小值和最大值,這就確定了z坐標的最小值和最大值,因為z\theta之間有著非常明確的關(guān)系。同樣,對\phi的范圍我們也進行了限制,這就又給交點的計算帶來了麻煩,我們必須確定這個交點在球面的范圍之內(nèi)。

// Compute sphere hit position and $\phi$
pHit = ray((Float)tShapeHit);

// Refine sphere intersection point
pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
phi = std::atan2(pHit.y, pHit.x);
if (phi < 0) phi += 2 * Pi;

// Test sphere intersection against clipping parameters
if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) ||
    phi > phiMax) {
    if (tShapeHit == t1) return false;
    if (t1.UpperBound() > ray.tMax) return false;
    tShapeHit = t1;
    // Compute sphere hit position and $\phi$
    pHit = ray((Float)tShapeHit);

    // Refine sphere intersection point
    pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
    if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
    phi = std::atan2(pHit.y, pHit.x);
    if (phi < 0) phi += 2 * Pi;
    if ((zMin > -radius && pHit.z < zMin) ||
        (zMax < radius && pHit.z > zMax) || phi > phiMax)
        return false;
}

忽略減少誤差的操作(Refine spher intersection point的過程),我們來看怎么計算\phi值的。因為\frac{y}{x}=\frac{r\sin\theta\sin\phi}{r\sin\theta\cos\phi}=\tan\phi,所以\phi=\arctan\frac{y}{x},代碼中也是這么計算的,并且為了確保\phi值在[0, 2\pi]之間,對計算出的小于0的數(shù)還加上了2\pi進行調(diào)整。

然后是判斷交點的z值是否在[z_{min}, z_{max}]的范圍內(nèi),\phi值是否在[0, \phi_{max}]范圍內(nèi)。如果不在,那么就換一個交點試試,如果另一個交點也不在,那就表示沒有交點,交點檢測失敗了,返回false。

交點鄰域的變化情況

交點鄰域的變化情況主要指兩個:
1、點在鄰域內(nèi)的變化情況。主要由點關(guān)于uv的偏導數(shù)(\frac{\partial{p}}{\partial{u}}\frac{\partial{p}}{\partial{v}})組成,暗示了在uv兩個維度上,微小變化量會帶來什么樣的點位置改變。
2、法向量的變化情況。主要由法向量關(guān)于uv的偏導數(shù)(\frac{\partial{n}}{\partial{u}},\frac{\partial{n}}{\partial{v}})組成,暗示了在uv兩個維度上,微小變化量會帶來法向量的什么改變。

計算這些量的前提是,我們必須有u和v的值才行,從上面關(guān)于球面的uv方程中,我們很容易就能推導出:
\begin{cases} u=\frac{\phi}{\phi_{max}} \\ v=\frac{\theta-\theta_{min}}{\theta_{max}-\theta_{min}} \end{cases}
\theta可以通過交點的z值公式,利用反余弦函數(shù)求出,具體的代碼是:

// Find parametric representation of sphere hit
Float u = phi / phiMax;
Float theta = std::acos(Clamp(pHit.z / radius, -1, 1));
Float v = (theta - thetaMin) / (thetaMax - thetaMin);

接著求p關(guān)于u和v的偏導數(shù)。
\begin{cases} \frac{\partial{x}}{\partial{u}}=-r\sin\theta\sin(u\phi_{max})\phi_{max}=-y\phi_{max}\\ \frac{\partial{y}}{\partial{u}}=r\sin\theta\cos(u\phi_{max})\phi_{max}=x\phi_{max}\\ \frac{\partial{z}}{\partial{u}}=0 \end{cases}
\begin{cases} \frac{\partial{x}}{\partial{v}}=r\cos(\theta_{min}+v(\theta_{max}-\theta_{min}))\cos\phi(\theta_{max}-\theta_{min})=z\cos\phi(\theta_{max}-\theta_{min})\\ \frac{\partial{x}}{\partial{v}}=r\cos(\theta_{min}+v(\theta_{max}-\theta_{min}))\sin\phi(\theta_{max}-\theta_{min})=z\sin\phi(\theta_{max}-\theta_{min})\\ \frac{\partial{z}}{\partial{v}}=-r\sin(\theta_{min}+v(\theta_{max}-\theta_{min}))(\theta_{max}-\theta_{min})=-r\sin\theta(\theta_{max}-\theta_{min}) \end{cases}
代碼實現(xiàn)如下:

// Compute sphere $\dpdu$ and $\dpdv$
Float zRadius = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y);
Float invZRadius = 1 / zRadius;
Float cosPhi = pHit.x * invZRadius;
Float sinPhi = pHit.y * invZRadius;
Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0);
Vector3f dpdv =
    (thetaMax - thetaMin) *
    Vector3f(pHit.z * cosPhi, pHit.z * sinPhi, -radius * std::sin(theta));

代碼中,為了減少除法的消耗,將z的倒數(shù)保存起來,計算了\cos\phi\sin\phi,然后利用上面推導的公式,計算出想要的值。

接著就是最困難的部分了:如何計算法向量的偏導數(shù)?

我們用一個被稱為溫加頓方程(Weingarten equations)的東西來計算法向量的偏導數(shù)。溫加頓方程(Weingarten equations)是用\frac{\partial{p}}{\partial{u}}\frac{\partial{p}}{\partial{v}}的線性組合來表示法向量的偏導數(shù)。那么,為什么能用線性組合來表示呢?這就要用到上一篇文章的知識了。假設你已經(jīng)了解了曲線和曲面的相關(guān)知識,特別是第一基本形式和第二基本形式的相關(guān)知識。

在曲面中,法向量n意味著一個法向量場,表示的是表面的所有法向量的集合。而在代碼中,它僅僅表示了一個向量,但是這沒法計算變化了,所以,我們還是要放到整個曲面的意義上去。

法向量的計算方式是n=\frac{x_u\times{x_v}}{|x_u\times{x_v}|},也就是說,法向量與曲面在這點上的切平面垂直,并且它是單位向量。然后,對n\bullet{n}=1兩邊求導可得,n'\bullet{n}=0,就意味著n'\perp{n}。而n又垂直于切平面,所以n'必然在其平面內(nèi),于是,它就可以表示成x_u,u_v的線性組合。

接著,由第二基本形式可以得到edu^2+2fdudv+gdv^2,即
e=<x_{uu},n>=-<x_u,n_u>
f=<x_{uv},n>=-<x_u,n_v>=-<x_v,n_u>
g=<x_{vv},n>=-<x_v,n_v>
由于n_u,n_v也在切平面內(nèi),我們可以用x_u,x_v線性組合的方式來表示n_u,n_v
\begin{cases} n_u=\alpha_1x_u+\alpha_2x_v \\ n_v=\beta_1x_u+\beta_2x_v \end{cases}
n_un_v帶入到e,f,g的表達式中得:
\begin{cases} -e=<x_u, \alpha_1x_u+\alpha_2x_v>=\alpha_1(x_u)^2+\alpha_2x_ux_v=\alpha_1E+\alpha_2F \\ -f=<x_u,\beta_1x_u+\beta_2x_v>=\beta_1(x_u)^2+\beta_2x_ux_v=\beta_1E+\beta_2F \\ -f=<x_v, \alpha_1x_u+\alpha_2x_v>=\alpha_1x_ux_v+\alpha_2(x_v)^2=\alpha_1F+\alpha_2G \\ -g=<x_v, \beta_1x_u+\beta_2x_v>=\beta_1x_ux_v+\beta_2(x_v)^2=\beta_1F+\beta_2G \end{cases}
這里的E,F(xiàn),G都是第一基本形式的系數(shù),具體是E=(x_u)^2,F=x_ux_v,G=(x_v)^2。將上述的方程組表示成矩陣就是
-\begin{bmatrix} e & f\\ f & g \end{bmatrix}=\begin{bmatrix} \alpha_1 & \alpha_2 \\ \beta_1 & \beta_2 \end{bmatrix} \begin{bmatrix} E & F \\ F & G \end{bmatrix}
于是
\begin{bmatrix} \alpha_1 & \alpha_2 \\ \beta_1 & \beta_2 \end{bmatrix}=- \begin{bmatrix} e & f\\ f & g \end{bmatrix} \begin{bmatrix} E & F \\ F & G \end{bmatrix}^{-1}
逆矩陣可以使用余子式來計算
\begin{bmatrix} E & F \\ F & G \end{bmatrix}^{-1}=\frac{1}{EG-F^2}\begin{bmatrix} G & -F \\ -F & E \end{bmatrix}
最后得到
\begin{bmatrix} \alpha_1 & \alpha_2 \\ \beta_1 & \beta_2 \end{bmatrix}=\frac{1}{EG-F^2}\begin{bmatrix} fF-eG & eF-fE \\ gF-fG & fF-gE \end{bmatrix}
最終,求\frac{\partial{n}}{\partial{u}}\frac{\partial{n}}{\partial{v}}的公式就是:
\frac{\partial{n}}{\partial{u}}=\frac{fF-eG}{EG-F^2}x_u+\frac{eF-fE}{EG-F^2}x_v
\frac{\partial{n}}{\partial{v}}=\frac{gF-fG}{EG-F^2}x_u+\frac{fF-gE}{EG-F^2}x_v
觀察代碼,我們發(fā)現(xiàn),它的過程與我們推導出的結(jié)果完全一致:

// Compute sphere $\dndu$ and $\dndv$
Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0);
Vector3f d2Pduv =
    (thetaMax - thetaMin) * pHit.z * phiMax * Vector3f(-sinPhi, cosPhi, 0.);
Vector3f d2Pdvv = -(thetaMax - thetaMin) * (thetaMax - thetaMin) *
                  Vector3f(pHit.x, pHit.y, pHit.z);

// Compute coefficients for fundamental forms
Float E = Dot(dpdu, dpdu);
Float F = Dot(dpdu, dpdv);
Float G = Dot(dpdv, dpdv);
Vector3f N = Normalize(Cross(dpdu, dpdv));
Float e = Dot(N, d2Pduu);
Float f = Dot(N, d2Pduv);
Float g = Dot(N, d2Pdvv);

// Compute $\dndu$ and $\dndv$ from fundamental form coefficients
Float invEGF2 = 1 / (E * G - F * F);
Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu +
                         (e * F - f * E) * invEGF2 * dpdv);
Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu +
                         (f * F - g * E) * invEGF2 * dpdv);

當然,最后保存關(guān)于交點的信息以及集中時,參數(shù)t的值。

// Initialize _SurfaceInteraction_ from parametric information
*isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v),
                                             -ray.d, dpdu, dpdv, dndu, dndv,
                                             ray.time, this));

// Update _tHit_ for quadric intersection
*tHit = (Float)tShapeHit;

直接把計算得到的信息都保存進SurfaceInteraction結(jié)構(gòu)里,很直觀,然后將這些信息都轉(zhuǎn)換到世界空間中。

關(guān)于誤差

誤差的主要來源是浮點數(shù)計算時的舍入誤差,每一次計算都會有誤差,不管是加減還是乘除法。這點書中第三章的最后介紹得非常詳細,之后會總結(jié)出來。

總結(jié)

雖然看上去并不多,但是花了很多時間在微分幾何上,就為了理解溫加頓方程。這一篇文章像是如何實現(xiàn)數(shù)學公式,事實上也是,圖形和交點,怎么能沒計算呢?想到后面還有茫茫多的計算,不禁打個冷戰(zhàn),想想是不是要再寫篇文章壓壓驚?

參考資料

Physically Based Rendering
pbrt源碼,版本3
Differiential Geometry of Curves and Surfaces

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是筆者閱讀《基于物理的渲染:從理論到實踐》第一章的總結(jié),整理成文章算是一種復習,同時也希望能對讀者有幫助。 簡...
    閃電的藍熊貓閱讀 5,718評論 6 5
  • 考試形式和試卷結(jié)構(gòu)一、試卷滿分及考試時間 試卷滿分為150分,考試時間為180分鐘 二、答題方式 答題方式為閉卷、...
    幻無名閱讀 872評論 0 3
  • 2017年考研數(shù)學一大綱原文 考試科目:高等數(shù)學、線性代數(shù)、概率論與數(shù)理統(tǒng)計 考試形式和試卷結(jié)構(gòu) 一、試卷滿分及考...
    SheBang_閱讀 740評論 0 7
  • 考試科目:高等數(shù)學、線性代數(shù)、概率論與數(shù)理統(tǒng)計 考試形式和試卷結(jié)構(gòu) 一、試卷滿分及考試時間 試卷滿分為150分,考...
    Saudade_lh閱讀 1,162評論 0 0
  • 1、天災人禍 從ICU轉(zhuǎn)來的一個車禍病人,六十多歲一個阿姨渾身插滿八九個管子,肋骨斷了...
    南司先生閱讀 332評論 1 2

友情鏈接更多精彩內(nèi)容