2023年3月30日 星期四

Custom Projection Matrix to fit Vulkan

➡️ 前篇

前技

這是Projeciton Matrix裡

A和B的製造過程

投影矩陣對z的修正(Directx的版本,NDC的z會落在0~1之間)

客製化Projeciton Matrix

為了fit Vulkan奇怪的NDC

需要

Mb * Ma = Projection Matrix

Ma怎麼來?

Ma

Mb怎麼來?

NDC的z會落在-1~1之間 🐞
Mb

Fix 🐞

vulkan NDC的z落在0~1之間才對
OpenGL才是落在-1~1之間

vulkan tutorial裡有1行
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
就是在指定NDC要用0~1

code from GLM setup.hpp

這樣一來

Mb其實是
A = F/(F-N)
B= -NF/(F-N)
 

比較一下

(MbMa)和GLM的perspectiveRH_ZO
只差在 Result[1][1]的正負

這代表

➡️ 前篇 的結果2結果3
其實是同個投影矩陣

回頭看結果2的鏡相空間

結果2 在NDC的 鏡相空間

不就和結果3一樣嗎?

下圖的Z軸方向可能會讓人產生誤會
應該要畫得跟上圖的Z軸一樣才對
結果3 在NDC

在clip space做Face culling會怎樣?

➡️ 前篇

World ➡️ Camera ➡️ NDC



NDC space

左手NDC和右手NDC互為鏡相


從NDC退回 Clip space

假設view space的三角形頂點是(x',y',z',1)
clip space的三角形頂點是(x,y,z,w)

下面的頂點指的是(x,y,w)

用這種方法,三角形應該要顯示
結果1沒有
所以Vulkan不是這樣做的

鏡相空間

➡️ 前篇

World ➡️ Camera ➡️ NDC



NDC space

左手NDC和右手NDC互為鏡相

原本的左手NDC不行
在鏡相空間的右手NDC一樣不行
下面就不繼續畫了

DirectX、OpenGL、Vulkan在那裡Face cullng?

 

開場

設定逆時鐘方向是三角形的正面


這是我們的三角形和camera設定

使用glm::lookAt,預設是右手的Camera

為什麼3角形沒有出來?

結果1
程式碼來自vulkan tutorial

但是他的可以

結果2
程式碼來自vulkan tutorial

然後改成這樣也行?

結果3


NDC space

仔細想想,不管是左手camera還是右手camera
我們都可以把它變換到左手的NDC

肉眼可以看出大家在NDC都是逆時針
如果vulkan是在這裡做face culling
結果1就會看到三角形

但沒看到
所以不是在這裡做!

Screen(Buffer) space

先變換到Screen(Buffer)座標系

逆定順時鐘、逆時鐘的座標系必須是
x軸是從銀幕左往右
y軸是從銀幕下往上

所以我們先自己鏡射一下
如果是在這裡做face culling
OpenGL、Vulkan都是逆時鐘
結果1就會看到三角形
但沒看到
所以也不是在這裡做!

那麼現在

假設存在1個 Face cullng space

只用來判定face culling
rasterization還是在screen space填充

備註:其實OpenGL不用

這樣一來
DirectX和OpenGL都是逆時鐘,看得到三角形
而Vulkan是順時鐘,看不到
結果1相符

結果2

在NDC延著y軸翻轉

順時針,所以不是這裡

別忘了逆定順時鐘、逆時鐘的座標系
所以Vulkan在Screen Spcae是順時鐘

Vulkan在face culling space是逆時鐘
所以結果2看的到
這是一個負負得正的結果

結果3

我們真的去fit這種奇怪的NDC

別忘了逆定順時鐘、逆時鐘的座標系
所以在NDC是順時鐘

在Screen Space也是順時鐘
在face culling space是逆時鐘
所以結果3看的到

參考資料


Sascha的方法

假設我們使用右手的Camera


直接修改viewport

相當於下面的紅色區塊
在Face culling space是逆時鐘方向
所以能正常顯示

But 

仔細看完VkFrontFace Manual Page
發現它使用的是矩阵求三角形面积的結果再*(-1) 

VkFrontFace Manual Page
這樣不用座標變換
就可以得到Face culling space的結果了

2023年3月26日 星期日

快速配置vulkan學習環境

安裝C++ Compiler

Microsoft C++ Compiler

安裝scoop、xmake ➡️ 教學

vscode 裝上面2個插件

用xmake生成專案

xmake create -t console hello_world

用xmake安裝glfw、glm ➡️ 教學

xrepo install glfw
xrepo install glad

設定xmake.lua ➡️ 教學

是不是比Visual Studio的設定簡單?

生成compile_commands

xmake project -k compile_commands

複製貼上程式碼 ➡️ main.cpp


編譯和執行exe

xmake build
xmake run
exe跑起來了,只是現在還有毛毛蟲

重開VSCode

現在要不要?


就自動幫你生成c_cpp_properties.json

再執行1次
xmake project -k compile_commands

毛毛蟲消失了
自動完成也能正常運作

編輯時使用c++17

會用到c++17才有的std::optional

載入圖片

image篇載入圖片會用到stb_image.h

安裝庫
xrepo install stb

載入模型

image篇載入圖片會用到tiny_obj_loader.h

安裝庫
xrepo install tinyobjloader

更新xmake.lua

更新compile_commands
xmake project -k compile_commands

毛毛蟲才會不見

2023年3月17日 星期五

速覽SH for glossy transport

今天本來不想卷的,但

上次看GAMES202

有個部分沒想通,做個記錄

p = q,積分項=1
p ≠ q,積分項 = 0

第6堂:Precomputed Radiance Transfer


第7堂:SH for glossy transport
這個矩陣怎麼來的?

SH一瞥

SH是一組2維基函數

抓前面m個基函數來使用


投影到SH

因為Diffuse和視角無關,上面橘色框框只是1個2維函數F

把F投影到SH上,得到投影系數C1…Cm


Glossy BRDF一瞥

BRDF每個ωo,會對映到多個ωi

等同於每個ωo,都對映到1個2維函數Fn


在球面上取n個方向

令每個方向為ωn

每個ωn,都會對映到1個2維函數Fn


第1次投影:把BRDF投影到SH

對每個ωn,把Fn投影到SH上
得到投影系數Tn1Tnm
(水平方向)

畫成表格就是

    m ➡️  SH系數
  n
⬇️    T11T1m
⬇️    T21T2m
⬇️            :
⬇️    Tn1Tnm
 方
 向


用SH系數重新分組

令 m=1 為一組
可以得到1個2維函數S1

    m ➡️  SH系數
  n
⬇️    T11
⬇️    T21
⬇️       :
⬇️    Tn1
 方
 向

全部分組完,可以得到m個2維函數Sm

第2次投影

把每個2維函數Sm都投影到SH上
最後會得到m組t1m...tMm
(垂直方向)

m = M
    m ➡️  SH系數
 M
⬇️    t11t1m         
⬇️    t21t2m
⬇️           :            
⬇️    tM1tMm   
 SH
 系
 數

到這裡都是預計算(Offline)

還原

已知ωo的方向為 n=2
進行m次基函數的疊加可以還原成

    m ➡️  SH系數
  n
⬇️         
⬇️    T21T2m
⬇️            
⬇️    
 方
 向


再把上面投影片
藍色框框的系數cp和T21T2m做內積
就是最後的光照結果

我叫tMm,這裡叫tij

把上面的過程寫成矩陣乘法

L = ( B t ) l = B ( t l )

和下面是同個意思



2023年3月16日 星期四

速覽全局光照

BRDF, Diffue, Fresnel of Specular(Mirror) And Light


From Direct Light to Indirect Light

四十歲後,不滯於物,草木竹石均可為燈

自此精修,漸進於無燈勝有燈之境



📑 一、什麼是BRDF

假設有1個物體,草木竹石均可(不透明的草)
物體上有1個微小面積dA

光會射入dA,然後從dA離開(射出)
  • 射入Li:dA在Normal半球方向都能接收光
  • 射出Lo:dA在Normal半球方向都可能射出光

描述光如何射入dA,再從dA射出的函數就叫
Bidirectional Reflectance Distribution Function
雙向反射分布函數

不同的物體,有不同的BRDF,形狀都長的不太一樣

眼睛方向

眼睛方向:dA指向eye的向量


Diffuse BRDF

和眼睛方向無關
眼睛動的時候,不會變

光從1個方向射入後,會均均向所有方向射出

cos(θo)衰減會被能量的透視補嘗抵消掉
所以可以當作Lo所有方向都是定值

Sepcular(Mirror) BRDF

眼睛動的時候,會跟著變

Mirror BRDF
從1個方向射入,從另1個方向射出

Specular BRDF
從1個方向射入
但從多個方向射出

BRDF可以做加法
Mirror 是1條線;Specular是1個Lobe狀


Specular(Mirror) BRDF比較 

左:1條線  中:小Lobe 右:大Lobe


牆上會動的高光是:Specular

不會動的是:Diffuse


鏡子:Mirror BRDF


木牆高光:Specular BRDF


白牆:Diffuse BRDF


📑 二、Light 

直接光源間接光源
  • 直接光源:燈、手電筒、手機銀幕
  • 間接光源:Open小將、鏡子、鏡框、書本,草木竹石勻可為燈

直接光源

直接光源:燈

木牆高光:Specular


Fresnel比較

x軸:夾角;y軸:Fresnel值

夾角眼精方向dA平面法向量的夾角

Copper效果最好,難怪古人要用銅鏡


鏡子上的反射:Mirror

應該是水銀(汞)吧?Fresnel值很高


木牆高光:Specular

Fresnel值變化很大,夾角接近90度時,反射最明顯


📑 三、關燈光照

間接光源:從窗子透近來的間接光源

牆上高光,會動:

間接光源打在Specular表面


天花板的光不會動:

間接光源打在Diffuse表面


直接光源:手機銀幕的面光源

牆上靠近手機會動的是:直接光源打在Specular表面

牆上靠近手機不會動的是:直接光源打在Diffuse表面

牆的材質是Combined BRDF


牆上遠離手機會動的是:間接光源打在Specular表面


BGM


拍完收工

可以去吃早餐了? 🤠😋 吃光光