您當前的位置:首頁 > 體育

渲染流水線[Render_Pipeline]

作者:由 BTan 發表于 體育時間:2021-09-11

渲染管線(RenderPipeline)

渲染流水線[Render_Pipeline]

渲染管線可以理解為處理資料的各個階段,實時3D渲染流水線【基於光柵器插值】主流的實時3D渲染流水線分為:Direct 3D 和 OpenGL

\\ \color{green}{應用階段}  \xrightarrow{\qquad} \color{green}{幾何階段 }\xrightarrow{\qquad} \color{green}{光柵階段}

\color{green}{應用階段} \begin{cases} \cdot準備場景資料[\color{grey}{相機位置,視錐體,模型,光源}] \\ \cdot粗粒度剔除[\color{grey}{不用再提交幾何階段}]\\ \cdot設定每個模型狀態[\color{grey}{材質,紋理,shader}] \end{cases} \xrightarrow{\quad輸出幾何資料(圖元)\quad}

\color{green}{幾何階段} \begin{cases} \cdot 處理幾何,繪製的圖元是什麼,咋繪製,在哪繪製\\ \cdot 逐頂點,逐多邊操作 \\ \cdot 最重要是把頂點座標變換到螢幕空間  \end{cases} \xrightarrow{螢幕空間頂點[\color{grey}{深度值,著色}]}

\color{green}{光柵階段} \begin{cases} \cdot 以上一階段圖元資料產生片段\\ \cdot 決定每個渲染圖元中的那些畫素應該被繪製在螢幕上 \\ \cdot上一階段逐頂點資料進行插值  \end{cases} \xrightarrow{輸出插值後片段資料}

應用階段

\\ \underbrace{資料載入到視訊記憶體}_{\color{red}{硬碟->記憶體(RAM)->視訊記憶體(VRAM)}}  \xrightarrow{\quad} \underbrace{設定渲染狀態}_{\color{red}{CPU設定\quad 指導GPU\color{grey}{[網格應該如何被渲染]}}} \xrightarrow{\quad} \underbrace{呼叫Draw Call}_{\color{red}{指向需要渲染圖元列表(不含材質)}}

——當把資料載入到VRAM時,RAM的資料可以移除,但對於一些資料,CPU仍然需要訪問它們【CPU訪問網格資料進行碰撞檢測】

幾何階段

 \\\underbrace{頂點變換\quad著色}_{\color{red}{頂點著色器}}  \rightarrow  \underbrace{細分圖元}_{\color{red}{曲面細分著色器}}  \rightarrow  \underbrace{產生更多圖元}_{\color{red}{幾何著色器}}  \rightarrow \underbrace{看不見的頂點裁掉}_{\color{red}{裁剪[\color{grey}{可配置}]}} \rightarrow   \underbrace{圖元座標變換到螢幕座標}_{\color{red}{螢幕對映[\color{grey}{不可配置}]}}

光柵化階段

\underbrace{  \Delta設定組裝圖元  \quad  \Delta遍歷  柵格化圖元}_{\color{red}{2.光柵化}} \xrightarrow{\quad}   \underbrace{ 操作柵格化片元}_{\color{red}{3.片元著色器}}   \xrightarrow{\quad}  \underbrace{ 修改顏色, 深度緩衝,混合... }_{\color{red}{4.逐片元操作}}

渲染流水線[Render_Pipeline]

渲染流水線基本階段和流程

幾何階段[頂點處理]

 \xrightarrow{應用端載入,DrawCall指定 頂點資料}

\underbrace{頂點空間變換\quad著色}_{\color{red}{1.頂點著色器}}  \rightarrow  \underbrace{細分圖元}_{\color{red}{2.曲面細分著色器}}  \rightarrow  \underbrace{產生更多圖元}_{\color{red}{3.幾何著色器}}  \rightarrow  \underbrace{看不見頂點裁掉}_{\color{red}{4.裁剪[\color{grey}{可配置}]}} \rightarrow   \underbrace{圖元[\color{grey}{螢幕座標}]}_{\color{red}{5.螢幕對映}}

渲染流水線[Render_Pipeline]

——頂點處理器是一個硬體單元,可以執行Cg頂點程式,片段處理器可以執行Cg片段程式

\\ \underbrace{ \quad \quad \cdot 頂點處理的資料流模型從載入每個頂點屬性到頂點處理器開始\\然後VexterShader重複的取出下一個指令並執行它,直到頂點程式結束\\ \quad \quad \cdot \color{green}{指令作用:}存取幾個不同的包含不同的向量值的暫存器庫。eg. [position][color][normal]\\ \quad \quad \cdot   \color{green}{頂點屬性暫存器:}只讀,包含了應用程式指定的頂點屬性集\\ \quad \quad \cdot \color{green}{臨時暫存器:}讀寫,被用來計算中間結果\\ \quad \quad \cdot  \color{green}{輸出暫存器:}只寫,程式負責把結果寫入這些暫存器\\ \quad \quad \cdot輸出的結果暫存器包含了變換後頂點,在三角形組裝和光柵化後\\每個暫存器的值經過插值後傳遞給片段處理器   }_{\color{red}{頂點處理器-暫存器}}

1.幾何階段之頂點著色器

\\ \color{green}{模型空間}\xrightarrow{\color{red}{\quad \quad \quad \quad }}\color{green}{裁剪空間}

\\頂點[\color{grey}{moldespace}] \xrightarrow{\color{red}{\qquad 座標變換,逐頂點光照\qquad }}頂點[\color{grey}{clipspace}] \xrightarrow{透視除法}

________插一嘴________

\\\underbrace{ 列舉\quad 索引 }_{\color{red}{頂點的組織方式兩種}}

--列舉

順序讀取3個頂點構成三角形

--索引

\\\underbrace{ 頂點緩衝區_{\color{grey}{[頂點只存一份]}}\quad 索引緩衝區_{\color{grey}{每個頂點分配一個整數索引值}} }_{\color{red}{緩衝區}}

記錄三角形的方式:直接記錄頂點本身

\rightarrow

記錄頂點索引

渲染流水線[Render_Pipeline]

\\\underbrace{ 三角面法線\quad \quad頂點法線 }_{\color{red}{法線有兩種 }}

--座標系和頂點法線

渲染流水線[Render_Pipeline]

座標系:環繞順序:x-y-z-w

三角面法線

確定座標系,當把頂點資料從左(右)手匯入右(左)手系時,會產生內外表面相反

解決方案:需要重新調整頂點在緩衝區中的排列順序

頂點法線

DCC在編輯模型時可以直接指定頂點法線,理論上一個頂點的法線可以是過該點的任意一條射線

渲染流水線[Render_Pipeline]

\\ \underbrace{\begin{cases}  \\w≠0: \begin{pmatrix} wx\\ w y\\ wz \\w\end{pmatrix}_{ homogeneous}= \begin{pmatrix}\frac{x}{w}\\ \frac{y}{w}\\\frac{z}{w} \\\frac{w}{w}\end{pmatrix}_{ homogeneous}=\begin{pmatrix}\frac{x}{w}\\ \frac{y}{w}\\\frac{z}{w} \\\end{pmatrix}_{ Car} \\w=0:表示方向    \end{cases}  }_{\color{red}{齊次空間座標和笛卡爾座標關係}}

__________插完嘴_________

在頂點從在模型空間變換裁剪空間 ,到最終把圖元畫到螢幕上 。這期間還會涉及到幾個空間變換

首先

\quad \quad  \\\color{green}{模型空間}\xrightarrow{模型變換\quad M_{[model]}[平移,旋轉,縮放] \quad  } \color{green}{世界空間}

DCC

中建模頂點都是依據自身座標系建模,每個模型都是獨立的,跟其他模型的沒有關係,當模型放到Unity,在頂點著色器就 可以訪問到

世界空間為我們所關心的最大的空間,現在需要把頂點從模型空間變換世界空間,讓物體間產生聯絡。變換矩陣為

M_{(model)}

\\M_{[model]} \begin{pmatrix} x\\  y\\ z \\ 1\end{pmatrix}_{[modelspace]} =\begin{pmatrix} x^{

\\\underbrace{M_{[model]}=M_{[transform]}*M_{[ratotion]}*M_{[scale]}}_{\color{red}{矩陣乘法為右往左依次複合 \leftarrow}}

M_{[transform]}= \begin{pmatrix}   \color{red}{1} & \color{green}{0} &\color{blue}{0} &t_{x}\\ \color{red}{0} &\color{green}{1}&\color{blue}{0}&{t_{y}}\\ \color{red}{0}&\color{green}{0}&\color{blue}{1}& t_{z}\\  0&0&0&1 \end{pmatrix}   \quad    M_{[rotation]}= \begin{pmatrix}   \color{red}{cosθ} & \color{green}{0} &\color{blue}{sinθ} &0\\ \color{red}{0} &\color{green}{1}&\color{blue}{0}&0\\ \color{red}{-sinθ}&\color{green}{0}&\color{blue}{cosθ}&0\\  0&0&0&1 \end{pmatrix}    \quad   M_{[scale]}= \begin{pmatrix}   \color{red}{k_{x}} & \color{green}{0} &\color{blue}{0} &0\\ \color{red}{0} &\color{green}{k_{y}}&\color{blue}{0}&0\\ \color{red}{0}&\color{green}{0}&\color{blue}{k_{z}}& 0\\  0&0&0&1 \end{pmatrix}

渲染流水線[Render_Pipeline]

定義一個三維旋轉操作,需要定義對應的旋轉軸

\\\underbrace{均勻縮放\qquad 非均勻縮放}_{\color{red}{法向量[\vec n]的變換M_{scale}兩種情況} }

--均勻縮放

\\M_{[scale]} * [\vec n]_{[modelSpace]}=[\vec n]_{[worldSpace]}

--非均勻縮放

當模型非均勻縮放,法線不再垂直於模型表面,

\vec n

可透過乘以

M_{scale}

的逆轉置矩陣

((M_{scale})^{-1})^{T}

,求得正確的法線

\vec n

\\ [ \vec n]^{T}_{[moldeSpace]}*(M_{scale}^{-1})^{T}=[\vec n]^{T}_{[worldsapce]}

Unity中,頂點使用左手系,從DCC匯出頂點資料時,都要將其轉換到左手系

頂點緩衝區中,頂點座標定義為

w

分量為

1

的齊次座標

\\\begin{pmatrix} x\\  y\\ z \\ 1\end{pmatrix}_{[modelspace]} =\begin{pmatrix} x\\  y\\ z \end{pmatrix}_{[modelspace]}

渲染流水線[Render_Pipeline]

unity_ObjectToWorld內建變數

\quad \quad  \\ \color{green}{世界空間}\xrightarrow{觀察變換 \quad M_{[view]}[旋轉,平移] \quad  }  { \underbrace{\color{green}{觀察空間}}_{\color{grey}{[當涉及position 和\vec n,此空間更為有效]}}  }

\\M_{[view]} \begin{pmatrix} x\\  y\\ z \\ 1\end{pmatrix}_{[worldspace]} =\begin{pmatrix} x^{

可以在

worldspace

內定義

camera

,當給定

camera

的狀態後,則觀察空間也得以確立,且世界空間中的頂點也隨之變換到觀察空間

\\ \underbrace{ \qquad \cdot 通常攝像機需要透過3個引數定義 \\\qquad \cdot \color{green}{Eye(o)}:worldspace中攝像機位置的座標 \quad [Transform看到]\\\qquad \cdot \color{green}{LookAt}: worldspace 中攝像機所觀察位置的座標 \\\qquad \cdot \color{green}{Up}: worldspace中近似於攝像機朝上的方向向量,通常定義為 worldspace的 y 軸  }_{\color{red}{定義觀察空間【右手座標系】}}

向量組

\left\{\vec u,\vec v,\vec n \right\}

,在

viewspace

中,攝像機位於

Eye(o)

原點處且指向

-\vec n

即攝像機的觀察方向,也稱朝前方向為

-\vec n

渲染流水線[Render_Pipeline]

構造觀察空間的方法

觀察矩陣

M_{[view]}

一種思路

觀察變換用到的

M_{[view]}

操作分解為

M_{[transfrom]}

M_{[rotation]}

是在給定觀察座標系的前提下,保持觀察座標系不動;計算出模型的頂點相對於觀察座標系3個座標軸平移了多少,旋轉了多少;最終複合成觀察矩陣

M_{[view]}

,但計算旋轉度就會麻煩

換一個思路

讓模型和座標系“錨定”,透過【平移】【旋轉】觀察座標系,使之最終與世界座標系重合。當觀察座標系【平移】【旋轉】時,與之“錨定”的模型頂點也隨之【平移】【旋轉】

最終世界空間與觀察空間重合時,模型頂點變換後得到的世界座標值,實質上就是它在觀察座標系下的值

一個例子

\\M_{[view]}=M_{[rotation]}M_{[transfrom]}

\\  M_{[transfrom]}=\begin{pmatrix}   \color{red}{1} & \color{green}{0} &\color{blue}{0} &t_{x}\\ \color{red}{0} &\color{green}{1}&\color{blue}{0}&{t_{y}}\\ \color{red}{0}&\color{green}{0}&\color{blue}{1}& t_{z}\\  0&0&0&1 \end{pmatrix}      \quad  Eye=\begin{pmatrix} 0 \\ 3\\ 20\\1 \end{pmatrix}_{[worldspace]}\quad LookAt=\begin{pmatrix} 0 \\ 2\\10\\ 1\end{pmatrix}_{[worldspace]}

Eye

移到原點

O

的矩陣

M_{[transfrom]}

\\\begin{pmatrix}   \color{red}{1} & \color{green}{0} &\color{blue}{0} &t_{x}\\ \color{red}{0} &\color{green}{1}&\color{blue}{0}&{t_{y}}\\ \color{red}{0}&\color{green}{0}&\color{blue}{1}& t_{z}\\  0&0&0&1 \end{pmatrix}  \begin{pmatrix} 0 \\ 3\\ 20\\1 \end{pmatrix}_{[worldspace]}=\begin{pmatrix} 0 \\ 0\\ 0\\1 \end{pmatrix}_{[worldspace]}\Rightarrow解得:  \begin{cases} t_{x}=0\\ t_{y}=-3\\ t_{z}=-20  \\ \end{cases}   \rightarrow M_{[transfrom]} = \begin{pmatrix}  1 & 0 & 0 &  0\\  0 & 1 & 0 &  -3\\ 0 & 0 & 1 &  -20\\ 0 &  0 & 0 & 1 \end{pmatrix}

Eye

為原點,

LookAt

Eye_{(o)}

的距離為

\\distance(Eye,LookAt)=\sqrt{101} \xrightarrow{\quad \quad }\underbrace{ \bar{n} =\frac{\vec n}{|\vec n|}  =\frac{Eye-LookAt}{||Eye-LookAt||}}_{\color{red}{為 z 軸 LookAt 指向 Eye} }

觀察空間為右手系 所以

LookAt

座標為

 (0 ,0, -\sqrt{101},1)_{[viewspace]}^{T}

那按點

LookAt

的方式重新定義的點

\\ \quad (LookAt)_{[vs]}= M_{[transfrom]}(LookAt)_{[ws]} = \begin{pmatrix}  1 & 0 & 0 &  0\\  0 & 1 & 0 &  -3\\ 0 & 0 & 1 &  -20\\ 0 &  0 & 0 & 1 \end{pmatrix}  \begin{pmatrix} 0 \\ 2\\10\\1 \end{pmatrix}_{[ws]}= \begin{pmatrix} x \\ y\\z\\1 \end{pmatrix}_{[vs]} \xrightarrow{解得}  \begin{cases} x=0\\y=-1\\z=-10\\w=1 \end{cases}

\\ 旋轉矩陣 \quad M_{[rotation]}=  \begin{pmatrix}  \color{red}{a} &\color{green}{b} &\color{blue}{c}&0 \\   \color{red}{e} &\color{green}{f} &\color{blue}{g}&0 \\ \color{red}{i} &\color{green}{j} &\color{blue}{k}&0 \\ 0&0&0&1\end{pmatrix}

要在保持觀察座標系的向量組

\left\{ \vec u,\vec v,\vec n \right\}

相互垂直的前提下,旋轉它們並使其朝向和向量組完全重合

\\\left\{ \vec x= (1 ,0, 0)^{T}, \vec y= (0 ,1, 0)^{T} ,\vec z= (0 ,0, 1)^{T}\right\}

也就是說,要構造一個矩陣,使得向量組

\left\{ \vec u,\vec v,\vec n \right\}

列向量右乘矩陣

M_{[rotation]}

時,分別等於

\left\{ \vec x,\vec y,\vec z \right\}

的方向向量值

\\M_{[rotation]} [\vec u]=    \begin{pmatrix}  \color{red}{a} &\color{green}{b} &\color{blue}{c}&0 \\   \color{red}{e} &\color{green}{f} &\color{blue}{g}&0 \\ \color{red}{i} &\color{green}{j} &\color{blue}{k}&0 \\ 0&0&0&1\end{pmatrix}    \begin{pmatrix} u_{x} \\  u_{y}\\  u_{z}\\ 0 \end{pmatrix} _{vs} =\begin{pmatrix} 1\\  0\\  0\\0 \end{pmatrix}_{ws}

\\ \begin{cases} \color{red}{a}u_{x} + \color{green}{b}u_{y} +\color{blue}{c}u_{z}=1\rightarrow dot(\vec u, \vec s)\rightarrow u//  \vec s \\  \color{red}{e} u_{x} + \color{green}{f}  u_{y} + \color{blue}{g}   u_{z}=0\rightarrow dot(\vec u,\vec t)\rightarrow \vec u\bot \vec t\\   \color{red}{i}u_{x} +  \color{green}{j}u_{y} + \color{blue}{k}u_{z}=0\rightarrow dot(\vec u,\vec m)\rightarrow u \bot \vec m  \end{cases} \xrightarrow{\quad 解得 \quad }\begin{cases}  \color{red}{a}=u_{x}\\ \color{green}{b}=u_{y} \\ \color{blue}{c}=u_{z}   \end{cases}

另外2軸可以透過代入

 y

z

,可得出矩陣

M_{[rotation]}

\\M_{[rotation]}  = \begin{pmatrix} u_{x} & u_{y} & u_{z} & 0\\   v_{x} & v_{y} & v_{z} &0\\  n_{x} & n_{y} & n_{z}&0    \\0&0&0&1\end{pmatrix}

Unity 3D中世界座標系和觀察座標系分別是左手系和右手系,這兩種座標系的

x

軸和

y

軸是重合的,

z

軸則相反

\\M_{[negative-z]} = \begin{pmatrix} 1 & 0 & 0 & 0\\  0&1&0&0\\0&0&-1&0\\0&0&0&1\end{pmatrix}

--最終矩陣為

M_{[view]} =M_{[negative-z]} M_{[rotation]}M_{[transform]}=\begin{pmatrix} 1 & 0 & 0 & 0\\  0&1&0&0\\0&0&-1&0\\0&0&0&1\end{pmatrix}   \begin{pmatrix} u_{x} & u_{y} & u_{z} & 0\\   v_{x} & v_{y} & v_{z} &0\\  n_{x} & n_{y} & n_{z}&0    \\0&0&0&1\end{pmatrix}  \begin{pmatrix}  1 & 0 & 0 &  0\\  0 & 1 & 0 &  -3\\ 0 & 0 & 1 &  -20\\ 0 &  0 & 0 & 1 \end{pmatrix}

Unity3D中的觀察空間座標系

渲染流水線[Render_Pipeline]

透過使用unity_MatrixV內建變數,可以把頂點從基於左手座標系的世界空間變換到基於右手座標的觀察空間

\quad \quad  \\ \color{green}{觀察空間}\xrightarrow{ 投影變換 \quad M_{[projection]} \quad  }  { \underbrace{\color{green}{裁剪空間}}_{\color{grey}{[渲染圖元進行裁剪[方便]]}}  }

\\M_{[projection]} \begin{pmatrix} x\\  y\\ z \\ 1\end{pmatrix}_{[viewspace]} =\begin{pmatrix} x^{

這塊空間取決於視截體(view frustum),它是空間中的一塊區域,決定了相機可以看到的空間,視錐體是由六個平面包圍而成的【由 Camera元件中的引數和Game檢視的橫縱比共同決定】

渲染流水線[Render_Pipeline]

\\\xrightarrow{視截體定義} \begin{cases} \color{red}{fovY}:垂直方向的視野區域 \\\color{red}{aspect}:截體底面寬高比 =\frac{width}{high}  \\\color{red}{nearClipPlane}:靠近camera 的視截體底面 (z)_{[viewspace]}=(-Near)_{[viewspace]}  \\\color{red}{farClipPlane}: 遠離camera 的視截體底面 (z)_{[viewspace]}=(-Far)_{[viewspace]}   \end{cases}

投影矩陣有兩個目的

首先:是為投影做準備,雖然

M_{(projection)}

的名稱包含了投影二字,但並沒有進行真正的投影工作,而是在為投影做準備 。真正的投影發生在後面的齊次除法過程中, 而經過

M_{(projection)}

的變換後,頂點的

w

分量將會具有特殊的意義

其次:是對

(x,y,z)

分量進行縮放。如果直接使用視錐體的6個裁剪平面來進行裁剪會比較麻煩, 而經過投影矩陣的縮放後,我們可以直接使用

w

分量作為一個範圍值 ,如果

x,y,z

分量都位於這個範圍內,就說明該頂點位於裁剪空間內

視截體剔除操作:

頂點資料投遞給渲染流水線之前,把這些無貢獻的模型頂點丟棄,則會大幅地提升效能

操作:在執行之前的預處理階段,計算網格的包圍體,隨後CPU執行多邊形網格的包圍體與視截體的相交測試

如果多邊形網格的包圍體完全在視截體之外——丟棄;其餘的丟進渲染流水線

mesh

並不是完全在視截體內,有一部分與遠截面相交。因此

mesh

應根據視截體的邊介面進行裁剪處理,僅顯示視截體之內的那一部分

mesh

裁剪操作並不是在

[viewspace]

中,而是在光柵化階段中的

clipSpace

中,由硬體完成。因此,在頂點處理階段,投影變換可視為最後一步操作

投影矩陣

M_{[projection]}

及其推導過程

裁剪空間:

透過

M_{[projection]}

可以將正稜臺狀的視截體

\rightarrow

軸對齊的立方體

xy\in[-1,1]

z\in[-1,0]

渲染流水線[Render_Pipeline]

視截體轉換為一個軸對齊的立方體

透視投影變換並不生成二維影象,只使場景中的三維物體發生變形

設  \qquad \qquad M_{[projection]}=\begin{pmatrix}  \color{red}{a} &\color{green}{b}  &\color{blue}{c } &d\\   \color{red}{e}&\color{green}{f}&\color{blue}{g}&h\\ \color{red}{i}&\color{green}{j}&\color{blue}{k}&l\\ m&n&o&p  \end{pmatrix} \quad p=\begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[viewspace]}\quad   p^{

\\  M_{[projection]}\begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[viewspace]}= \begin{pmatrix} x^{

渲染流水線[Render_Pipeline]

視截體的橫切面,藍色線條為投影面

定義一個投影平面,

M_{[projection]}

限定了

(x^{

z^{

此平面的定義公式為

\\cot(\frac{fovY}{2}) =\frac{z}{1}\Rightarrow z= -cot(\frac{fovY}{2})<0

y^{

fovY

為垂直視野角度

\\cot(\frac{fovY}{2})=\frac{y}{z}=\frac{y^{

x^{

渲染流水線[Render_Pipeline]

D是座標系原點到投影平面的距離,即z′

從圖可知

fovX

未知,但可以根據

fovY

aspect

得到

fovX

\\\quad \begin{cases} tan(\frac{fovY}{2}) = \frac{H}{D}\rightarrow H=tan(\frac{fovY}{2})D \\ tan(\frac{fovX}{2}) = \frac{W}{D}\rightarrow W=tan(\frac{fovx}{2})D \\ \frac{tan(\frac{fovX}{2}) D}{tan(\frac{fovY}{2})D} =\frac{cot(\frac{fovY}{2}) }{cot(\frac{fovX}{2})} \end{cases} \quad \xrightarrow{\qquad \qquad  }  cot(\frac{fovX}{2})=\frac{cot(\frac{fovY}{2})}{aspect}=\frac{x^{

\\M_{[projection]}\begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[viewspace]}  \quad =\begin{pmatrix}\frac{xcot(\frac{fovY}{2})}{z\cdot aspect}\\\ \frac{y}{z}cot(\frac{fovY}{2})\\z^{

根據齊次座標的性質,如果

w≠0

,則

\left( x,y,z,1\right)=\left( wx,wy,wz,w\right)

,現在把

p′

的座標值乘以

-z

\\\begin{pmatrix}\frac{x \cdot cot(\frac{fovY}{2})}{aspect}\\\ y\cdot cot(\frac{fovY}{2})\\-zz^{

帶入矩陣函式

\\\begin{pmatrix}  \color{red}{a} &\color{green}{b}  &\color{blue}{c } &d\\   \color{red}{e}&\color{green}{f}&\color{blue}{g}&h\\ \color{red}{i}&\color{green}{j}&\color{blue}{k}&l\\ \color{red}{m}&\color{green}{n}&\color{blue}{o}&p  \end{pmatrix}_{[projection]}   \begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[viewspace]}= \begin{pmatrix}\frac{x\cdot cot(\frac{fovY}{2})}{aspect}\\\ y\cdot cot(\frac{fovY}{2})\\-zz^{

\\ \quad \quad  \color{red}{a}x + \color{green}{b}y +\color{blue}{c}z + d =\frac{x\cdot cot(\frac{fovY}{2})}{Aspect} \qquad \xrightarrow{解得}\quad   \color{red}{a} = \color{red}{ \frac{cot(\frac{fovY}{2})}{Aspect}}   ,  \color{green}{b} =0, \color{blue}{c} =0,d= 0

\\ \quad \quad  \color{red}{e}x+\color{green}{f}y+\color{blue}{g}z +h =\ y\cdot cot(\frac{fovY}{2}) \quad\xrightarrow{解得}\quad  \color{red}{e} =0, \quad  \color{green}{f} =cot(\frac{fovY}{2}),  \color{blue}{g} =0,h=0

\\ \quad \quad \color{red}{i}x +\color{green}{j}y + \color{blue}{k}z +l = zz^{

\\ \quad \quad \color{red}{m}x+\color{green}{n}  y +\color{blue}{o}z + p=- z\quad\xrightarrow{解得}\quad \color{red}{m}=0,\color{green}{n}=0 ,\color{blue}{o}=-1,p=0

在視截體中,任意一個平行於投影平面的平面,其上面的任意一個頂點投影到投影平面上,其z都是相等的,即待投影點的投影z和其x、y無關

渲染流水線[Render_Pipeline]

由於投影后

z^{

\\\begin{cases} 遠截面的 z (即 -Far )投影后的 z^{

代入式

\color{blue}{k}z +l = zz^{

\\\begin{cases} \color{blue}{k}(-Far) +l = Far(-1) \\  \color{blue}{k}(-Near) +l = Near*0 \end{cases}  \Rightarrow  \begin{cases} \color{blue}{k}= \color{blue}{\frac{Far}{Far-Near} } \\ \\ l=\frac{Near\cdot Far}{Far-Near} \end{cases}

\\ M_{[projection]}= \begin{pmatrix}    \color{red}{ \frac{cot(\frac{fovY}{2})}{Aspect}}  & 0 & 0 &0 \\  0&  \color{green}{cot(\frac{fovY}{2})}&0&0\\0&0&  \color{blue}{\frac{Far}{Far-Near} } & \frac{Near\cdot Far}{Far-Near}\\0&0&-1&0\end{pmatrix}

裁剪空間【軸對齊立方體】是基於右手系 ,投影變換後

\\ 頂點 \xrightarrow{\qquad 輸入\qquad} 硬體光柵化階段

在光柵化階段,裁剪空間採用左手系,需要把右手系的裁剪空間

\rightarrow

左手系

左手系的裁剪空間

x ,y

軸的朝向與右手座標系相同,即

z

軸相反。因此

 M_{[projection]}

右乘倒轉

z

軸的

M_{z}

才能得到最終

M_{[projection]}

\\M_{[projection]}= \begin{pmatrix} 1 & 0 & 0 &0 \\ 0 & 1 & 0 &0\\  0& 0 & -1 &0\\ 1 & 0 & 0 &1\end{pmatrix}   \begin{pmatrix}  \color{red}{\frac{cot(\frac{fovY}{2})}{Aspect} }& 0 & 0 &0\\  0& \color{green}{cot(\frac{fovY}{2})}   &0&0\\0&0&  \color{blue}{\frac{Far}{Far-Near} } &\frac{Near\cdot Far}{Far-Nera}\\0&0&-1&0\end{pmatrix}

\xrightarrow {現在把該軸對齊立方體拉大,使z\in[-1,1] } \begin{cases} 遠截面的 z(即 -Far)投影后的 z^{

 \xrightarrow{代入式 \color{blue}{k}z +l = zz^{

\\M_{[projection]}= \begin{pmatrix}  \color{red}{\frac{cot(\frac{fovY}{2})}{Aspect}}  & 0 & 0 &0\\  0& \color{green}{cot(\frac{fovY}{2})} &0&0\\0&0& \color{blue}{\frac{Far+Near}{Far-Near} }& -\frac{2\cdot Nera\cdot Far}{Far-Near}  \\0&0&-1&0\end{pmatrix}

 \underbrace{\begin{pmatrix}   \color{red}{\frac{cot(\frac{fovY}{2})}{Aspect}}  & 0 & 0 &0\\  0& \color{green}{cot(\frac{fovY}{2})} &0&0\\0&0& \color{blue}{\frac{Far+Near}{Far-Near} }& -\frac{2\cdot Nera\cdot Far}{Far-Near}  \\0&0&-1&0\end{pmatrix}}_{M_{[projection\quad Matrix]}}   \begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[vs]} =   \underbrace{\begin{pmatrix} x\cdot \color{red}{\frac{cot(\frac{fovY}{2})}{Aspect}} \\ y\cdot \color{green}{cot(\frac{fovY}{2})}\\  z\cdot \color{blue}{\frac{Far+Near}{Far-Near} } -\frac{2\cdot Nera\cdot Far}{Far-Near} \\ -z\end{pmatrix}_{[clipspace]}   }_{變換後的位置}

M_{[projection]}

分別是Unity3D在Direct3D平臺和OpenGL平臺上的投影矩陣值,根據式中投影后

z^{

的不同取值範圍,

M_{[projection]}

的第三行在不同平臺上有不同的值

現在可以按如下不等式來判斷 個變換後的頂點是否,任何不滿足上述條件的圖元都需要被剔除或者裁剪

\\ [-w\leq x \leq w\quad |\quad-w\leq y \leq w\quad |\quad-w\leq z \leq w]

Direct3D平臺,

z^{

,OpenGL平臺,

z^{

當座標系從右手系

\rightarrow

左手系

Direct3D平臺,

z^{

,OpenGL平臺,

z^{

Direct3D平臺上

p′=zz^{

中的

z^{

p′=zz′

中的

z\in[n,f]

,所得到未除以

w

分量的齊次座標值

z

分量的取值範圍是

[0,f]

OpenGL平臺上

z^{

p′=zz′

中的

z\in[n,f]

,得到未除以

w

分量的齊次座標值

z\in[-n,f]

\\\quad \color{green}{裁剪空間}  \xrightarrow{\quad\quad 透視除法\quad\quad} \color{green}{\underbrace{NDC空間}_{\color{grey}{歸一化裝置座標}}}

——

透視除法

[PerspectiveDivision]

\\M_{[projection]}\begin{pmatrix} x \\y\\ z\\ 1\end{pmatrix}_{[viewspace]}=\begin{pmatrix} x^{

M_{[projection]}

的第4行是

[0, 0,-1, 0]

一個

w

分量為

1

齊次座標

(x, y, z, 1)_{[viewspace]}^{T} \rightarrow M_{[projection]}(x^{

一個例子

渲染流水線[Render_Pipeline]

\\p_{1}=(\color{red}{0},\color{green}{1},-1,1),p_{2}=(\color{red}{0},\color{green}{1},,-2,1),q_{1}=(\color{red}{0},\color{green}{0},-1,1),q_{2}=(\color{red}{0},\color{green}{0},-2,1)

\\fovY=\frac{π}{2},\quad aspect=1, \quad Near=1, \quad Far=2

矩陣函式

\\M_{[projection]}* p_{1}=p_{1}^{

\\解得\quad p_{1}^{

經過投影變換後,在兩個空間中對應的頂點,其

x,y

沒有變化,原來等長的

l_{1}

l_{2}

還是等長但是

z

有變化,但變換後的

z

超出了

[0,1]

。為了把該齊次座標變成笛卡兒座標,需要把他們各自除以它們的

w

分量

\\解得:p_{1}^{

這時

x,y

發生了變化,產生了“近大遠小”,並且

z

也限定在了

[0,1]

範圍內

w

分量不為

1

的裁剪空間座標值除以

w

,使得

w

分量等於

1

,四維齊次座標降維成三維笛卡兒座標的操作

Direct3D平臺

經過透視除法,裁剪空間中齊次座標值

z

分量的取值範圍,從原來在

[0, far]

\rightarrow

[0,1]

OpenGL平臺上

把裁剪空間中齊次座標值

z

分量的取值範圍,從原來的

[-n,f]

\rightarrow

[-1,1]

範圍內。兩種平臺下裁剪空間中齊次座標值

x,y

則限制在

[-1,1]

Unity的裁剪空間座標系

在各平臺下Unity3D的裁剪空間使用左手座標系,並且在未經透視除法之前

\\ (wx ,wy, wz, w)_{clip}^{T}\ne( x ,y, z)

[viewSpace]_{[righthand]}

\rightarrow

[clipSpace]_{[lefthand]}

\\M_{[projection]} \begin{pmatrix} x \\ y\\ z\\ 1\end{pmatrix}_{[viewspace]} =\begin{pmatrix} wx \\wy\\ wz\\ w\end{pmatrix}_{[clipspace]}

由於

M_{[projection]}

不是仿射變換,因此

 (wx ,wy,wz, w)_{clip}^{T}

w

分量不為1

呼叫UnityViewToClipPos函式,可以把頂點從

[viewspace]

變換到

[clipspace]

,程式碼如下:

渲染流水線[Render_Pipeline]

背面剔除操作

\underbrace{消除了部分在視截體之外}_{\color{red}{視截體剔除}}\quad \underbrace{在裁剪空間之間的多邊形剪掉}_{\color{red}{裁剪操作}}\quad \underbrace{背向於攝像機方向的多邊形消除掉}_{\color{red}{背面剔除操作}}

觀察空間中做背面剔除操作的原理,判斷一個三角形

t

是正面還是背面,可以透過計算三角形法線向量

\vec n

camera

位置到當前三角形

\vec n

連線

\vec c

之間的點積

\\\vec n\cdot \vec c >0 \quad |Front|  \quad  \vec n\cdot \vec c< 0 \quad |Back| \qquad |\vec n\cdot \vec c< 0 \quad |Side|

大部分渲染流水線中,背面剔除操作並不是在觀察空間中完成的

該演算法裡,要判斷每一個三角形的正面背面,必須計算出

camera

到該三角形法線的連線

\vec c

。每次渲染有成千上萬個三角形,這個計算操作是非常耗時,因此,最佳化手段是不計算這些連線向量

而是想辦法把連線

\vec c

常數化。透過

M_{[projection]}

把頂點變換到裁剪空間之後,投影線便可以當做連線

\vec c

使用

[vertex]_{[viewspace]}

[vertex]_{[clipspace]}

的頂點順序排列是一樣

渲染流水線[Render_Pipeline]

視見立方體的z軸朝向和真正的裁剪空間的z軸朝向是相反的。因此,裁剪空間中的連線向量應該是[0 0 1]

[vertex]_{[viewspace]}\quad [vertex]_{[clipspace]}

的頂點順序排列是一樣,但是在觀察空間中的三角形

t

的頂點排列順序是逆時針順序,在裁剪空間中變成了順時針順序,兩者的正面和背面是剛好相反的

渲染流水線[Render_Pipeline]

在裁剪空間中做背面剔除操作

\\設\quad \quad \vec a=p_{2}^{

左手法則

\vec a\times \vec b = (a_{x} ,a_{y} ,a_{z}) \times(b_{x} ,b_{y} ,b_{z})=(a_{y}b_{z}-b_{y}a_{z} ,a_{z}b_{x}-a_{x}b_{z} ,a_{x}b_{y}-a_{y}b_{x} ) \\ \xrightarrow{\qquad \qquad } \left[ 0,0, (x_{2}-x_{1})  (y_{3}-y_{1}) -(y_{2}-y_{1})(x_{3}-x_{1})    \right]

\vec a× \vec b

和連線

\vec c=(0,0,1)

做點積

(\vec a\times \vec b) \cdot \vec c = \left[ 0,0, (x_{2}-x_{1})  (y_{3}-y_{1}) -(y_{2}-y_{1})(x_{3}-x_{1})    \right] \cdot (0,0,1)\\\xrightarrow{\qquad \qquad }(x_{2}-x_{1})  (y_{3}-y_{1}) -(y_{2}-y_{1})(x_{3}-x_{1})

帶入數值可解

(\vec a×\vec b)·\vec c

的值為

-15

三角形

\vec n

與連線

\vec c

的夾角為鈍角。也就是說,三角形

t_{1}

的正面對著攝像機,對應的三角形

T_{1}

則背面對著攝像機。因此,三角形

T_{1}

應該被剔除

渲染流水線[Render_Pipeline]

\\\quad {\underbrace{\color{green}{NDC空間}}_{\color{grey}{歸一化裝置座標}}}\xrightarrow {\qquad     視口變換 \quad M_{[viewport]}\qquad } {\underbrace{\color{green}{螢幕空間}}_{\color{grey}{會得到真正畫素位置}}}

視為當前場景所投影的矩形區域。視口定義於當前螢幕空間中,並且不一定非得為全部螢幕,可以為當前程序視窗的部分割槽域

\underbrace{ \quad \cdot 原點:位於視窗左上角處\\ \quad \cdot三軸: \vec x 右, \vec y下, \vec z 指向螢幕內,透過給定 minX , minY , Width , Heigth 及深度範圍 [minX,maxZ]  \\\quad \cdot視口的深度範圍值 [minZ,maxZ] 定義了投影場景的 z 的範圍。 z 將會應用於深度緩衝區中   }_{\color{red}{螢幕空間定義[\color{grey}{[右手系]}}}

視口的寬高比等於視截體的寬高比

\\(\frac{Width}{Height})_{[viewport]} =(\frac{Width}{Height})_{[camera]}

(clipSpace)_{leftHand}\rightarrow(screenSpace)_{righthand}

且兩者

xz

軸同向,

y

軸相反,因此,從裁剪空間中變換到螢幕空間中首先需要進行逆置

y

軸操作

渲染流水線[Render_Pipeline]

定義螢幕空間

\\\underbrace{M_{[invertY]} = \begin{pmatrix} 1 & 0 & 0 &0 \\ 0 & -1 & 0 &0\\  0& 0 & 1 &0\\ 1 & 0 & 0 &1\end{pmatrix}  }_{\color{red}{逆置y軸}}

原來在裁剪空間中

xy

軸方向上大小範圍是

[-1,1] 縮放到 [Width,Height]

z

軸方向上大小範圍是

[-1,1]

[0,1]

的頂點縮放到

[maxZ-minZ]

\\設 \qquad \qquad M_{[scale]} = \begin{pmatrix} \ \color{red}{a} & 0 & 0 &0 \\ 0 &\color{green}{b} & 0 &0\\  0& 0 &\color{blue}{c} &0\\ 0 & 0 & 0 &1\end{pmatrix}

\\ \begin{pmatrix} \ \color{red}{a} & 0 & 0 &0 \\ 0 &\color{green}{b} & 0 &0\\  0& 0 &\color{blue}{c} &0\\ 0 & 0 & 0 &1\end{pmatrix}   \begin{pmatrix}  x_{(-1,1) } \\ y_{(-1,1) }\\ z_{(0,1||-1,1) }\\ 1\end{pmatrix}_{[ndc]} =\begin{pmatrix}  x_{(0-Width) } \\    y_{(0-Higth) } \\  z_{(maxZ-minZ) }\\ 1\end{pmatrix}_{[screen]}

解得

\\M_{[scale]} = \begin{pmatrix} \frac{Width}{2} & 0 & 0 &0 \\ 0 &  \frac{Higth}{2}& 0 &0\\  0& 0 & maxZ-minZ &0\\ 1 & 0 & 0 &1\end{pmatrix}

完成縮放操作後,再做一次平移操作,使座標系的原點從中間移到螢幕左上角

\\M_{[translation]} = \begin{pmatrix}1 & 0 & 0 & minX+ \frac{Width}{2} \\ 0 & 1& 0 &minY + \frac{Higth}{2}\\  0& 0 &1 &minZ\\ 1 & 0 & 0 &1\end{pmatrix}

\underbrace{M_{[viewport]} =M_{[translation]} M_{[scale]} M_{[invertY]} }_{ \color{red}{從右到左依次複合\leftarrow}}    =\begin{pmatrix} \frac{Width}{2} & 0 & 0 & minX+ \frac{Width}{2} \\ 0 & - \frac{Higth}{2}& 0 & minX+ \frac{Heigth}{2}\\  0& 0 & maxZ-minZ &minZ\\ 1 & 0 & 0 &1\end{pmatrix}

組成圖元的頂點即完全變換到二維螢幕上。接下來進行掃描轉換,把圖元插值成片段

光柵化

\\\quad \underbrace{ \color{green}{組裝:}  \quad 硬體依據頂點資料輸入流/頂點索引流,把頂點組裝為圖元 \\ \color{green}{光柵:}  \quad 用cross 判定 這個畫素點是在 是否被這個三角形覆蓋,最終生成片段 }_{\color{red}{圖元組裝/光柵化}}

光柵化流程

\\\quad 頂點[\color{grey}{[螢幕空間]}\underbrace{\xrightarrow{ \color{red}{三角形設定/遍歷/圖元被分解為片段/culling/}}}_{光柵化}片段\color{grey}{[經過插值的引數]}\underbrace{\qquad  \xrightarrow { \color{red}{}}}_{\color{green}{片段著色器}}

光柵化階段有兩個最重要的目標 :計算每個圖元覆蓋了哪些畫素,以及為這些畫素計算它們的顏色

\\ [Function]\begin{pmatrix}  x \\    y \\  z\\ 1\end{pmatrix}_{[screen]}=離散化資料

三角形設定

上一個階段輸出的都是

\Delta

網格的頂點,即

\Delta

網格每條邊的兩個端點。但如果要得到整個

\Delta

網格對畫素的覆蓋情況。 就必須計算每條邊上 的畫素座標。為了能夠計算邊界畫素的座標資訊,就需要得到

\Delta

形邊界的表示方式。這樣個計算三角網格表示資料的過程就叫做三角形設定

三角形遍歷

三角形遍歷階段將會檢查每個畫素是否被 一個

\Delta

網格所覆蓋。如果被覆蓋的話,就會生成一個片元,而這樣一個找到哪些畫素被

\Delta

網格覆蓋的過程就是

\Delta

遍歷,這個階段也被稱為掃描變換

\Delta

遍歷階段會根據上 個階段的計算結果來判斷一個

\Delta

網格覆蓋了哪些畫素,並使用

\Delta

網格 3個頂點的頂點資訊對整個覆蓋區域的畫素進行插值。

對這些資訊進行插值都是用硬體電路實現,當全部片元[並不是真正意義上的畫素 ,而是包含了很多狀態的集合]屬性都完成掃描轉換之後,即完成光柵化階段

片元處理

\\片段\color{grey}{[經過插值的引數]}\underbrace{\xrightarrow{ \color{red}{執行一系列的數學和貼圖操作}}}_{\color{green}{片段著色器}}最終片段\color{grey}{[顏色,深度去影響下一個階段]}

渲染流水線[Render_Pipeline]

\\ \underbrace{  \quad \quad \cdot \color{green}{輸入暫存器:}只讀,是經過插值獲得的片段引數\\ \quad \quad \cdot \color{green}{臨時暫存器:}讀寫,讀寫中間結果\\ \quad \quad \cdot  \color{green}{輸出暫存器:}只寫,片段的顏色和可選的新的深度,片段指令包括了紋理獲取 }_{\color{red}{片段處理器-暫存器}}

\\ \color{green}{    \quad    \underbrace{[texel]  }_{紋素}  \quad   } 紋理中最小的一個單元\quad \quad  texel 用來區分 Cbuffer 中的 \color{green}{ \underbrace{[pixel]}_{畫素} }

\\ \quad 紋理 \rightarrow 二維的紋素陣列 \rightarrow 每一個紋素 \rightarrow 一個唯一地址 \underbrace{\rightarrow 橫縱索引}_{\color{red}{紋理對映座標 }}

渲染流水線[Render_Pipeline]

採用標準化紋理座標(u,v)訪問不同紋理並且獲取到不同的紋素

--紋理操作

\underbrace{\quad \quad \cdot 紋理對映三維模型的表面頂點,這些對映關係通常DCC中完成  \\ \quad \quad \cdot 建模階段給多邊形網格頂點賦予紋素地址時 \\會產生某一特定mesh和某一特定紋理相耦合即一張紋理的規格只能用在一個網格上 }_{\color{red}{確定 紋理 和待渲染模型表面對應關係}}

--解決方案

\underbrace{ \quad 1.紋理座標標準化,即把整數紋素索引規格化到 [0, 1],\quad 這些在 [0, 1] 範圍內的座標通常用 (u, v) \\ \quad 2.紋理座標對映為紋理1中的紋素索引(2, 2),對映為紋理2中的紋素索引(3, 3) }_{\color{red}{解決方案}}

\underbrace{ \color{green}{OpenGL:}紋理座標系的原點在紋理圖的左下角, u 軸水平向右, v 軸垂直向上\\ \color{green}{Direct3D:}紋理座標系的原點在紋理圖的左上角, u 軸水平向右, v 軸垂直向下}\\  _{\color{red}{不同平臺上的紋理對映座標採用不同的紋理空間座標系}}

如果紋理多邊形網格從Direct3D

\rightarrow

OpenGL平臺,則各個頂點的紋理座標應從

(u, v)

調整為

(u, 1-v)

渲染流水線[Render_Pipeline]

Direct3D和OpenGL的紋理座標系的差異

紋理對映座標與紋素陣列索引

渲染流水線[Render_Pipeline]

光柵化階段插值生成的片元紋理對映座標

當給定了片元的紋理對映座標後,對應的紋理陣列索引值將透過圖形API在執行期中自動計算得到

Direct3D 9平臺下

計算紋理陣列索引值

(t_{x},t_{y})

根據紋理對映座標

(u, v)

和紋理的高寬

(sizeX, sizeY)

\\ t_{x}  =sizeX  \times  s_{x} -0.5 \quad \color{green}{|}\quad  t_{y}  =sizeY  \times  s_{x} -0.5

輸出合併

\\  \quad \xrightarrow{}   最終片段 \underbrace{ \xrightarrow{\color{red}{AlphaTex/模板/深度測試,混合}} }_{\color{grey}{逐片元操作[Opengl \quad DX]}}   混合 \xrightarrow{\color{red}{幀快取寫操作}} \underbrace{混合的顏色替代畫素的顏色}_{顏色緩衝區}

渲染流水線會比較

(Color,Alpha,depth)_{fragment} <comp>  (pixel)_{Cbuffer}

輸出合併階段不可程式設計操作,主要解決每個片元的可見性問題。會提供一系列的【Alpha測試】【Alpha混合】【深度測試】等指令來對片元透明值和深度值進行比較整合操作

\underbrace{ Depthbuffer 和 Cbuffer 解析度相同,記錄了儲存於當前緩衝區中的深度值\\ \quad \cdot 當位於 (x,y) 處的片元從片元函式返回\\ \quad \cdot [DepthValue]_{fragment(x,y)} <comp>[DepthValue]_{DepthBuffer(x,y)} \\ if\quad  [DepthValue]_{fragment(x,y)}< [DepthValue]_{DepthBuffer(x,y)}  \\則它的[Color,Alpha,Depth]將分別更新到位於 (x, y) 處的Cbuffer 和Dbuffer,\quad else 丟棄  }_{\color{red}{深度緩衝區機制[\color{grey}{基於深度緩衝區的相關演算法}]}}

渲染流水線[Render_Pipeline]

先繪製淺色三角形後繪製深色三角形的過程

渲染流水線[Render_Pipeline]

先繪製深色三角形後繪製淺色三角形的過程

只要啟用了深度測試機制,圖元就可以以任意的先後順序繪製而最終仍能得到正確的結果

\\ \underbrace{  \quad \quad \cdot [DepthValue]_{fragment(x,y)} < [DepthValue]_{DepthBuffer(x,y)} 畫素和當前片元之間呈現半透明效果\\ \quad \quad \cdot 可以透過 [Color]_{fragment(x,y)}  和 [color]_{ColorBuffer(x,y)} 之間執行顏色混合操作得以實現 \\ \quad \quad \cdot FinalCol=A_{f}C_{f} +(1-A_{f})C_{p},處理過程採用[alpha]_{fragment(x,y)}和[alpha]_{ColorBuffer(x,y)} 操作  }_{\color{red}{輸出合併中的Alpha值操作 }}

r

通道包含

8

位資料,則

Alpha

通道也應包含8位資料。因此顏色值

32

位的

rgba,

Alpha=\left( 0,256 \right)

,通常在程式設計實踐中使用單位化的浮點數取值範圍

\left( 0.0,1.0\right)

\\ \underbrace{ Color=A_{f}C_{f}+(1-A_{f})C_{p}\qquad alpha=A_{f}A_{f}+(1-A_{f})A_{f} }_{\color{red}{ C_{f}: \color{grey}{當前片元的顏色}\quad  C_{p}:  \color{grey}{顏色緩衝區中片元對應的畫素點顏色}\quad  A_{f}: \color{grey}{片元的Alpha值}\\ A_{p}:  \color{grey}{畫素點的Alpha值}\quad  color:  \color{grey}{混合後的最終顏色}\quad  alpha:  \color{grey}{混合後的最終Alpha值} }}

--一個例子

渲染流水線[Render_Pipeline]

\\ \underbrace{  深色\Delta頂點\quad rgba=(0,0, 1.0, 0.5) \quad \color{green}{|}\quad  淺色\Delta頂點\quad rgba=(0.772, 0.878, 0.705,1.0)  }_{\color{red}{2個幾何三角形}}

\\ \underbrace{  深色\Delta的全部片元將被賦值為 (0, 0, 1.0,0.5) \quad \color{green}{|}\quad  淺色\Delta的全部片元將被賦值為 (0.772, 0.878, 0.705, 1.0)  }_{\color{red}{光柵化會對 rgba 通道執行插值}}

假定了渲染順序為先繪製淺色三角形後繪製深色三角形

Color=A_{f}C_{f}+(1-A_{f})C_{p}=0.5*(0,0, 1.0, 0.5) +0.5*(0.772, 0.878, 0.705, 1.0)

理論上深度緩衝演算法與圖元的繪製順序無關,但實際上要繪製透明圖元時無法以任意順序進行渲染

--半透明物體應執行排序操作

\\ \underbrace{  \quad \quad \cdot 需要在全部不透明物體渲染完畢後,以從離當前攝像機最遠到最近的順序先後依次渲染\\ \quad \quad \cdot最精細準確的就是基於圖元三角形進行排序。然而三角形的數量十分龐大 \\而且當前攝像機的觀察方向可能每幀都會發生變化,基於三角形排序的方法可能無法實時地執行\\ \quad \quad \cdot因此,實時3D引擎若要對半透明物體進行排序,通常都是以單個物體模型為粒度  }_{\color{red}{半透明物體應執行排序操作}}

Unity 3D ShaderLab中的Alpha混合指令及深度測試指令

\\ \underbrace{  C_{f}:顏色值\quad \color{green}{|}\quad A_{f}:透明值 \quad \color{green}{|}\quad SrcFactor:顏色混合係數 \quad\color{green}{|}\quad SrcFactorA: 透明值混合係數 }_{\color{red}{當前片元【fragment】 }}

\\ \underbrace{ C_{p}: 對應的畫素點顏色值\quad \color{green}{|}\quad A_{p}: 透明值\quad \color{green}{|}\quad DstFactor: 顏色混合係數\quad\color{green}{|}\quad DstFactorA: 透明值混合係數 }_{\color{red}{顏色緩衝區中 }}

\\ \underbrace{ OpColor: 顏色混合運算子\quad \quad  \color{green}{|}\quad \quad  OpAlpha: 透明值混合運算子 }_{\color{red}{混合運算子 }}

\\ \underbrace{ Color=C_{f}SrcFactor<OpColor>C_{p}DstFactor\quad \quad  \color{green}{|}\quad \quad  Alpha=A_{f}SrcFactorA<OpAlpha>A_{p}DstFactorA }_{\color{red}{混合操作後的結果顏色值 Color 和透明值 Alpha  }}

OpColor

OpAlpha

可以是同一個運算子號,也可以不同;片元或畫素的顏色操作係數和透明值混合係數也可以是同一個係數,也可以不同

渲染流水線[Render_Pipeline]

常用的Alpha混合運算子及混合係數

渲染流水線[Render_Pipeline]

常用的顏色值透明值混合係數

渲染流水線[Render_Pipeline]

在著色器程式碼中宣告Alpha混合所用到的顏色值透明值混合運算子和混合操作係數。

④深度測試指令

ShaderLab語言提供了控制是否寫入當前片元的深度值到深度緩衝區的控制函式,以及和當前深度緩衝區對應點的深度值相比滿足何種關係時才寫入的一系列判斷函式

Cg簡介

\\ \underbrace{ \underbrace{Jave\quad C++\quad C}_{多用途語言}  \qquad  \underbrace{\color{green}{RenderMan}\quad 實時/畫素流光照語言}_{非實時光照語言} \qquad \underbrace{Direct3D\quad OpenGL\quad RealityLab\quad IRISGL}_{可程式設計GPU和3D應用程式程式設計}  }_{\color{red}{ Cg[NVIDIA]來源}}

\color{green}{RenderMan}

著色語言只是非實時渲染器PRMan(PhotoRealistic RenderMan)一部分

\\ \underbrace{【\underbrace{ 環境分量權重 \ast環境光 }_{\color{red}{環境光}}  \quad + \quad  \underbrace{ 鏡面反射分量權重 \ast(\vec n\quad  \vec v\quad roughness) }_{\color{red}{鏡面反射光}}】*  \underbrace{ 銅色}_{\color{red}{反射率}}}_{\color{red}{著色樹}【最終顏色】}

在著色樹和現實主義中渲染物體的表面外觀進行廣泛的控制需要可程式語言

\\ \underbrace{ \quad \quad \cdot 每個階段的通訊被限制在之前和之後,一個演算法的硬體實施最有 效\\ \quad \quad \cdot RenderMan 使用的Reyes演算法不太適合有效的硬體實現,主要是高階的幾何處理  \\ \quad \quad \cdot PixelFlow:新的可程式設計圖形硬體架構,促進了計算機圖形研究的一個新的領域服從硬體的著色器\\過於昂貴  \\ \quad \quad \cdot  OpenGL渲染:Silicon Graphics 研究人員開發了一個系統能夠把著色器翻譯成多過程的OpenGL渲染  \\ \quad \quad \cdot 斯坦福實施著色語言(RTSL):為第二代和第三代圖形處理器設計的著色語言 \\用RTST編寫的著色器可以編譯成一個或者多個OpenGL渲染過程  \\ \quad \quad \cdot斯坦福啟發了NVIDIA,開發了一個商業質量的服務與硬體著色語言Cg  }_{\color{red}{受硬體指導著色語言} }

\\ \underbrace{ OpenGL\quad \quad Direct3D}_{\color{red}{三維程式設計介面}}

Cg編譯器和執行庫(Runtime)

——GPU不能夠從文字方式直接執行Cg程式,那麼就要把Cg翻譯成GPU可以執行的形式

\\  \xrightarrow{\quad Cg程式\quad} \underbrace{Cg編譯器}_{\color{red}{ 如何編譯取決GPU型別年代}} \xrightarrow{\quad Cg程式\quad三維程式設計介面支援的形式\quad}

\\  \xrightarrow{\quad 三維應用程式呼叫\quad} \underbrace{ [OpenGL][Direct3D]驅動}_{\color{red}{}} \xrightarrow{\quad 編譯後Cg程式\quad GPU所需要的硬體可執行格式 \quad}

\\  \underbrace{ \begin{cases} \quad \cdot \color{green}{靜態} \quad  非實時過程,編譯器把程式編譯成CPU上直接執行的可執行檔案\\一旦編譯以後,程式就不需要再重新編譯,除非改變程式碼 \\ \quad \cdot \color{green}{動態} \quad Cg編譯器不是一個獨立程式,是Cg執行庫的一部分,使用Cg程式的三維應用程式必須與Cg執行庫連結 \\連結後,應用程式呼叫Cg執行庫來編譯,操作Cg程式\\動態編譯允許Cg程式為安裝在你機器上的GPU的精確模型進行最佳化 \end{cases}  }_{\color{red}{編譯}}

\begin{cases} \qquad \cdot  \color{green}{CgGL函式庫}:應用程式使用了OpenGL\\將使用CgGL庫來呼叫正確OpenGL例行過程把翻譯後的Cg程式傳遞給OpenGl驅動程式 \\ \qquad \cdot  \color{green}{CgD3D函式庫}:如果使用Direct3D,則呼叫CgD3D,驅動程式將翻譯後Cg程式翻譯成GPU可以執行的形式 \end{cases}

渲染流水線[Render_Pipeline]

Cg嵌入到一個Cg應用程式中

CgFX工具箱和檔案格式

\\\underbrace{三維模型\qquad 紋理\qquad 其他資料}_{\color{red}{Cg程式需要的資料}}  \quad + \quad 正確的三維程式設計介面配置和狀態

提供一種方法把渲染一個三維資訊和Cg程式捆綁在一起

\underbrace{\quad \cdot 可以用來表現全部的效果和外觀,語法是Cg的一個超集,並且可以包含多個Cg程式.fx字尾來識別CgFX \\ \quad \cdot CgFX檔案描述了為一個特殊效果所需要的全部渲染狀態\\多pass,紋理狀態和任意數目的單獨的頂點和片段程式可以被用來定義創造一個完整的外觀和效果 \\\quad \cdot 開發工具箱被提供用來使用和分析CgFX檔案,它展示了使用者介面到寄主應用程式的鉤子\\ 這樣知道CgFX的應用程式就可以自動的向用戶和開發人員提供有意義的控制和語義 \\ \quad \cdot Cg程式描述了發生一個單獨的渲染頂點或者片段的處理,但是一些複雜的著色演算法需要多個渲染過程,\\ CgFX提供了一種個來編碼複雜的多過程效果,包括為每個渲染過程指定應用的Cg程式 }_{\color{red}{CgFX提供一種標準的檔案格式}}

\\ \underbrace{  \\\quad \cdot CgFX提供一種機制,來指定多渲染過程和為某個單獨的效果提供可選擇的多個實現(顯示卡選擇/SubShader?) \\\quad \cdot CgFX允許指定不可程式設計的渲染狀態,eg. Alpha測試模式和紋理過濾,\\對這些渲染狀態的是定採用簡單的表示式,當效果被初始化,它將在CPU上計算 \\\quad \cdot CgFX允許在著色引擎和著色引擎引數上加註釋,這些註釋為應用程式包括內容創作程式,提供了附加資訊\\eg.一個註釋可以指定一個著色引擎引數所允許的數值範圍 }_{\color{red}{CgFX還支援三種附加功能}}

\\ \underbrace{  \\\quad \cdot CgFX檔案格式:封裝了對一個給定的著色引擎的多個Cg程式的實現,也就是說它可以用於第三代GPU和第四代GPU \\同時也可以包含一個簡單的程式只支援第二代GPU \\\quad \cdot 應用程式載入CgFX檔案後,可以基於計算機的GPU來決定最合適的著色引擎實現 \\\quad \cdot 使用CgFX的Cg程式多例項化是一種解決在不同時代的功能各異的圖形處理器和不同硬體廠商的方法\\多例項化還可以專門為一個特殊的三維API開發一個Cg程式 \\\quad \cdot CgFX工具箱由支援全部CgFX語法的CgFX編譯器,載入和操作CgFX檔案的 CgFX執行API 和 DCC設計的外掛模組組成 \\\quad \cdot 在CgFX之前,沒有一個標準方法可以使DCC輸出包含所有相關著色資訊三維內容\\以使這寫內容可以被實施渲染 }_{\color{red}{多個著色引擎例項化}}

標簽: 頂點  裁剪  空間  座標系  三角形