2021年10月28日 星期四

Diffuse BRDF推導過程的疑惑

➡️ 目錄

CG 魚書ch 13.8裡Perfect Diffuse BRDF的 推導過程

reflectance即albedo(texture color)
(13.9)就是render equation裡的半球積分 只考慮RGB其中1個component (且值為1) 有Diffuse BRDF為 F= 1 / π

有個地方讓我困惑

第1個黃色

告訴我們
往不同方向出去的Lo有個cos(θo)修正

第2個黃色

卻告訴我們進入dA 的E 會平均分到半球的各個方向 (各方向出去的Lo為定值)

實際來算算看

假設所有Li都相等

當F = 1 / π

跑1次 式13.11
E = ∫ cos(θi) Li
會得到 E = Li * π B
跑1次 式13.9
Lo = ∫ F cos(θi) Li
會得到 Lo = Li * F * π
使用F = 1 / π
會得到
Lo = Li 式C
(代表E在任1個方向o會產生Lo)
那所有方向o 就是跑1次 式 13.12
E = ∫ cos(θo) Lo
E = Lo * π 式A

因為式C 所以
式B = 式A
E = E
能量守恆
如果跑式13.12 時忽略cos(θo)項
E = ∫ Lo
Eo = Lo * 2π
就沒有能量守恆了

當F = cos(θo) (1 / π)

跑1次 式13.9
Lo = ∫ F cos(θi) Lii
會得到
Lo = F Li cos(θi) i
Lo = cos(θo) (1 / π) Li π
Lo = cos(θo) Li 式D

再跑1次 式13.12 (不使用cos(θo) )
E = ∫ Lo
會得到
Eo = ∫ Lo dωo
Eo = ∫ cos(θo) Li dωo
Eo = Li cos(θo) dωo
Eo = Li π

實際來畫畫看

光用猜的也知道
F = cos(θo) * (1 / π) 的結果會很奇怪
現實生活中沒看過球的輪廓是黑黑的一圈

F = 1 / (π)

F = cos(θo) * 1 / (π)

到底那出了問題?

看起來像是

迷團(1)

面光源進行直接光照的計算

Lo要乘上cos(θo)


迷團(2)

從diffuse物體直接到達眼睛的Lo不用
為什麼有(1)和(2)的區別
迷團(3)

如果直目(直接目視)面光源

面光源對眼睛來說是Lo 還是 Lo * cos(θo) ?

解開迷團?

我感覺迷團(2)的答案和透視有關
透視不只是近大遠小
還包括dA的朝向

蒙地卡羅積分和俄羅斯輪盤 🌩️

➡️ 前篇 

讀了CG 鼠書 

書上把direct light和indirect light分開考慮

代碼來自 CG 鼠書

direct light
  • 點光源單位是I
  • 面光源單位是L

indirect light
  • 用了蒙地卡羅和俄羅斯輪盤,光源單位是L
  • Render Equation的半球積分就是indirect light

直接光照 ➡️ 點光源

E擊中dAs = cos(θi) I / R2

12.3 Point Lights

代碼來自 CG 鼠書

代碼來自 CG 鼠書

直接光照 ➡️ 面光源

代碼來自 CG 鼠書
就是CG 樹書的Integrals over Area

dA用了蒙地卡羅積分後就消失了
這裡對面光源上的點只取樣了1次
p(dA)= 1/A

蒙地卡羅積分


間接光照

代碼來自 CG 鼠書
dω用了蒙地卡羅積分後就消失了(只取樣1個光源)

u是介於0~1的亂數值
ρ 是俄羅斯倫盤,,如果u是0.75
當ρ>0.75 會return 0 也就是有25%的機會採樣失敗
當ρ<0.75,return的值就要會做*1/ρ的修正
(把採樣失敗的能量補回來)


看完程式碼後,可以發現
半球積分時並沒有距離衰減項

蒙地卡羅積分

先不看ρ 
最後會return  integrand *4π
integrand *4π = integrand / (1/4π)
就是只取樣1次函數值的蒙地卡羅積分
機率密度函數 p()=1/4π 

俄羅斯倫盤


如果每次蒙地卡羅積分,n都取10
那麼光線彈射m次後,就會指數暴炸 nm

所以代碼裡的n才取1
這樣彈射m次後的路徑,也只是1條光路

只是因為是俄羅斯倫盤,某個pixel的光路可能手氣不好
沒收集到L,那麼那個pixel就會很暗淡
(當很多pixel都很這樣,整個畫面就會到處是躁點)

提高手氣的方法,就是每個pixel都多試幾次
再把那些結果取平均,這樣就能降躁
(總不會那麼衰小,每次的光路都收集不到L吧)

2021年10月26日 星期二

投影與組合

有1個在世界座標定義的向量V(x,y,z)

在世界座標定義1個正交座標系 W ,W不包含位移,只有3軸,而且3軸彼此互相垂直

  • x-axis
  • y-axis
  • z-axis

投影

那麼

把V 從世界座標(空間)變換到W座標(空間)的方法,就是把v投影到W的3軸(基向量)

使用內積(dot)來投影 圖示

  • x_w =dot(V, x-axis)
  • y_w =dot(V, y-axis)
  • z_w =dot(V, z-axis)

x_w =dot(V, x-axis) = 
x✖️x-axis.x ➕ y✖️x-axis.y ➕ z✖️x-axis.z

如果把函數想成無窮維度的向量
那麼 傅立葉變換 就是在做內積


f(x)向量 投影到 基向量

組合

把 (x_w,y_w,z_w)從W座標(空間)變換回世界座標(空間)

就是 圖示

從W空間的原點(0,0,0)開始

  • 移動x_w長度的x-axis
  • 再移動y_w長度的y-axis
  • 再移動z_w長度的z-axis

(0,0,0) ➕ x_w✖️x-axis y_w✖️y-axis z_w✖️z-axis

等同於在做3個基向量的組合

傅立葉逆變換 就是在 重新組合所有的基函數



座標變換:上半是投影、下半是組合

座標變換 VS 傅立葉變換

x_w、y_w、z_w都是實數c

但F(w)可以是複數 a+bi


為什麼基函數不一樣

為什麼?

傅立葉變換用的基函數是 

傅立葉逆變換 用的基函數是 

 
上面用到了歐拉公式

看起來投影得到的 a- bn i

組合之後實數部才會是

Σ ( acos(2π n x) + bn sin(2π n x) )

建模軟體要怎麼判定tangent.w的正負?

https://goo.gl/photos/ckGBx1HN5G49GmBeA

2021年10月25日 星期一

右手座標和左手座標的資料交換

如果有1個建模軟體是右手座標系

首先
在建模軟體裡成生1個假的左手座標系 W
(就是生成1組3軸和原點,視你引擎的需要來定義)

mesh的部分

把資料變換到該座標系 W
(透過world To Local transform 幾何意義)

資料可能是
  • 頂點vertexs
  • 法向量normal
  • 切向量tangent (轉換tangent.xyz之外, tangent.w 也要乘上1個負號 )
這樣一來 在座標系W 裡的 local 資料
就能直接拿出去給左手的引擎使用

也能在建模軟體正確的顯示
(透過local To World transform 幾何意義)


bone的部分

bone其實就是階層式的座標系
(也就是階層式的matrix)

Bn代表第n個bone
  • 每個Bn其實可以寫成1個 bone matrix Mn
  • 已經和parent bone matrix相乘的叫=> Mn_world 
  • 還沒和parent bone的matrix相乘的叫 => Mn_local 
  • matrix同時有S(縮放)、R(旋轉)、T(位移) 3個資訊
  • R(旋轉)矩陣 等價於「1個繞軸旋轉」可以用「1個四元數」來表示
  • S(縮放)矩陣 等價於 vS = (Sx,Sy,Sz)
  • T位移 等價於 vP = (Px,Py,Pz)

我想到的方法是
  • (步驟1)把Mn_world轉換成對映的 Qn_world 、vSn_world、vPn_world
  • (步驟2)把Qn_world 、vSn_world、vPn_world變換到座標系 W 得到 (為什麼?)
    • Qn_world_w
    • vSn_world_w
    • vPn_world_w 
  • 在座標系 W裡 把上面3個東西轉換為Mn_world_w
  • 在座標系 W裡 重建 Mn_world_w的階層關系得到Mn_local_w
  • export的時候再把Mn_local_w轉成
    • Qn_local_w
    • vSn_local_w
    • vPn_local_w 

上面的(步驟2) 把matrix拆成3種分量做轉換是因為考慮到
  • 如果直接轉換matrix 會像這樣 圖示  (W座標系其實和建模世界的world重疊,但為了好比較,所以畫的時候偏移了一下)
  • bone代表的3軸並沒有變成左手的

  • 如果是先透過 四元數(繞軸旋旋)來轉換
  • 就會像這樣 圖示
  • ⚠️只是如果Sx,Sy,Sz都不一樣,轉換後scale 還要把Sy,Sz做交換
這麼說來
(步驟1)(步驟2)
改成直接轉換Mn_world到Mn_world_w 圖示 
再交換bone的y軸、z軸也是可以的(但縮放不交換)

W座標系

根據 圖示 以row-major寫出 W座標系就是(用column-major其實會得到1樣的結果)
W=
1 0 0 
0 0 1
0 1 0

因為W只是旋轉矩陣
W的逆=W的transpose=W
1 0 0 
0 0 1
0 1 0

(x,y,z)W =(x,z,y)

所以轉換mesh的
  • 頂點
  • 法向量
  • 切向量
到W座標系

實際上做的也只是交換y,z值而已

如何根據uv計算出頂點的Tangent

https://photos.app.goo.gl/1Nj8ojgitocw5iSu7

下面是詳細過程


三角形的頂點是A、C、B、對映到的uv座標是a、b、c


uv座標的2個軸是u-axis、v-axis向量

a= (a.u,a.v) 代表

從uv座標的原點 o=(0,0)出發

沿著u-axis軸移動 a.u 長度、再沿著v-axis軸移動a.v長度,會到達點a

o + u-axis✖️a.u + v-axis✖️a.v = a


b和c也是類似的

o + u-axis✖️b.u + v-axis✖️b.v = b

o + u-axis✖️c.u + v-axis✖️c.v = c


這有什麼用呢?

現在換看頂點ACB

想像ACB會在同1個平面上P,然後再想像P上會有1個原點D

一開始我們不知道D的實際位置

但我們可以模仿uv空間

o + u-axis✖️a.u + v-axis✖️a.v = a

寫出

D + T✖️a.u + BN✖️a.v = A

D + T✖️b.u + BN✖️b.v = B

D + T✖️c.u + BN✖️c.v = C


再來只要解上面3個聯立方程式,就能找出tangent了

https://photos.app.goo.gl/1UVE8M3LQBSwupPm8

T.w就是Peek tangent in Unity裡的w

建模軟體要怎麼判定tangent.w的正負?連結