圖形學3D渲染管線學習
圖形學3D渲染管線
DX和OpenGL左右手坐標系不同,會有一些差距,得出的矩陣會不一樣;
OpenGL的投影平面不是視景體的近截面;
頂點(vertexs)
頂點坐標,顏色,法線,紋理坐標(UV),連線索引;
圖元(primitives)
幾何頂點被組合為圖元(點,線段或多邊形),圖元裝配;
片元(fragments)
圖元被分幾步轉換為片元:圖元被適當的裁剪,顏色和紋理數據也相應作出必要的調整,相關的坐標被轉換為窗口坐標。最後,光柵化將裁剪好的圖元轉換為片元;
一、頂點數據(Vertex)
頂點坐標,顏色,法線,紋理坐標(UV),連線索引(三角形連線順序-左右手坐標系順序 不同);
頂點數據在流水管線中以圖元處理:
點(GL_POINTS)、線(GL_LINES)、線條(GL_LINE_STRIP)、三角面(GL_TRIANGLES);
四邊形頂點信息:頂點位置、顏色、UV、四個頂點順序;
索引可以避免共享頂點間數據的多餘——頂點緩存對象(Vertex Buffer Object,VBO)。
二、頂點着色器(Vertex Shader)
1.矩陣變換
通過矩陣變換將世界坐標系下的頂點變換到視口坐標系下顯示;
齊次變換:將一個原本是n維的向量用一個n+1維向量來表示(是不是很像投影);
互為逆矩陣:相乘結果為1;
轉置矩陣:行列互換;
正交矩陣:行列向量互相垂直,正交矩陣的逆矩陣就是他的轉置矩陣;
1)模型矩陣到世界矩陣
Local Coordinate——World Coordinate;
頂點在世界坐標系下可以進行平移(Translation),旋轉(Rotation),縮放(Scaling)操作;
按順序矩陣連乘得到世界矩陣下頂點的坐標;
如果進行縮放操作要考慮法線變換(非均勻縮放-逆矩陣的轉置矩陣獲得變換法線-切線空間);
OpenGL Normal Vector Transformation
2)世界矩陣到攝像機矩陣
World Coordinate——View Coordinate;
攝像機矩陣也叫觀察矩陣;
攝像機的一些參數:
EyePosition:攝像機位置
FocusPosition:觀察目標點(at)
UpDirection:攝像機上方向(不是Y,相對世界)
Near:近截面、Far:遠截面
FOV(vertical field of view):垂直視場角
Aspect Ratio :屏幕縱橫比
以上參數定義了視景體(台體);
頂點坐標轉換到攝像機矩陣逆向思維,理解為將當前頂點平移攝像機當前位置向量的反向量;
攝像機*攝像機變換矩陣肯定會得到一個單位矩陣(坐標系都是單位矩陣);
攝像機當前的x,y,z軸組成的矩陣和攝像機變換矩陣互為逆矩陣;
通過攝像機參數求得攝像機的xyz軸向量;
由於正交矩陣的逆矩陣就是他的轉置矩陣,再添加單位1;
將攝像機變換矩陣和平移矩陣相乘得到攝像機矩陣;
以上為左手坐標系下的攝像機矩陣;
3)攝像機矩陣到投影矩陣
View Coordinate——Projection Coordinate;
正交投影和透視投影;
在Dx中近截面就是投影平面;
正交投影每個坐標點只是丟棄了z坐標,就不推導了;
透視投影:通過相似三角形求出視景體內頂點x,y坐標在投影平面的相對位置,保留z坐標;
D3DXMATRIX matProject;
// 這個函數是設置正交投影矩陣
D3DXMatrixOrthoLH(&matProject, width, height, Znear, Zfar);
pD3dDevice->SetTransform(D3DTS_PROJECTION, &matProject);
從右到左將以上矩陣連乘獲得局部空間到裁剪空間的變換矩陣:
V(clip) = M(projection)*M(view)*M(model)*V(local)
2.着色計算
Flat Shading——一個頂點代表三角形顏色,默認索引中第一個頂點顏色;
Gouraud Shading——逐頂點着色,只計算三個頂點關照信息,在光柵化階段做插值得到各個片段光照信息,缺點:插值導致高光非線性;
Phong Shading——馮氏光照,逐片段着色(像素)法線插值出各個片元的法線信息,在片元着色器中使用法線、UV、位置等計算光照;
三、曲面細分着色器(Tessellation Shader)
可選階段;
外殼着色器(Hull Shader)、鑲嵌器(Tessellation)、域着色器(Domain Shader)
不創建高模,根據與攝像機距離使用鑲嵌器細化模型,添加三角面(LOD);
四、幾何着色器(Geometry Shader)
可選階段;
以圖元作為輸入數據,可以創建銷毀幾何圖元;
法線可視化——毛髮效果;
根據距離攝像機遠景動態調整多邊形邊數實現LOD效果;
公告牌技術(BillBoards)——3D圖片替代模型,總是朝向攝像機;
五、圖元組裝(Primitive Assembly)
1.裁剪(Clipping)
1)線段裁剪
點面關係,線面關係,通過向量在面上的投影,以及點是否在矩形內;
八方位裁剪法;
2)三角形裁剪
平頂三角和平底三角形,判斷出界平移頂點;
裁剪算法有Cohen-Sutherland算法、Liang-Barsky算法和Sutherland-Hodgman多邊形裁剪算法
2.背面剔除(Back-Face Culling)
只和三角形與攝像機的距離有關,不依賴攝像機朝向;
根據三角形頂點順序,使用左右手坐標系,叉乘求出法向量;
頂點順時針法向量朝外剔除,逆時針朝內剔除;
3屏幕映射 (Screen Mapping)
1)透視剔除(Perspective Division)
將視景體空間頂點,除以w分量獲得標準設備空間,-1到1的立方體空間(CVV-標準視體);
w分量保留的z坐標的信息,正交投影w分量為1,w分量也就是z值代表了深度信息;
2)視口變換(View Transform)
通過執行透射除法獲得歸一化的設備坐標NDC(Normalized Device Coordinate);
z值在視口變換過程中被映射,OpenGL映射到[-1,1],DirectX[0,1];
NDC坐標映射到窗口坐標要通過平移和縮放過程(相機的寬高比);
視口矩陣:xy為窗口相對於屏幕的位置;
六、光柵化(Rasterization)
將圖元離散為片元的過程;
圖元覆蓋一個片元(Overlap)——點採樣(Point Sampling),像素中間點在三角形內;
超級採樣和多重採樣技術,涉及到抗鋸齒;
1.三角形組裝(Triangle Setup)
三角形組裝會對頂點的輸入數據(比如,顏色、法線、紋理坐標)進行插值,得到各個片段對應的數據值,為後面的片元着色器提供片元數據;
2.三角形遍歷(Triangle Traversal)
遍歷這些三角圖元覆蓋了哪些片元的採樣點,隨後得到該圖元所對應的片元;
頂點數據插值獲得片元的顏色,法線,UV,深度等信息,用於投射正交插值獲得正確的透視顏色紋理等信息;
3.線段的掃描轉化
-
數字微分畫線算法DDA (Digital Differential Analyzer)
直線公式,斜率小於1,y增量為整數,大於1,x增量為整數;
4.多邊形填充
- 掃描線填充算法(Scanline Filling)
- 重心計算多邊形片元顏色
求多邊形的幾何重心,以重心為頂點,向多變的各個頂點連線,分割為三角形;
重心顏色:所有三角形頂點顏色的平均值;
計算每個三角形的重心(三個頂點的x,y分別相加除以3);
將三角形重心坐標和面積關聯起來;
sx += curx*curs;
sy += cury*curs;
重心的坐標為:tatal.s總面積
center.x = sx/total.s;
center.y = sy/total.s;
七、片元着色器(Fregment Shader)
色彩混合:顏色的RGB值相加,用於混合所有光照效果
色彩調製:色彩乘法,RGB值分別相乘,相當於給RGB值都乘以一個係數,將顏色變亮或變暗;
1.Phong光照模型
1)環境光
用來模擬全局光照效果;在物體光照信息基礎上疊加較小的光照常量;
2)漫反射
光線進入物體內部重新散射出來,看做均勻分部所以和觀察者位置無關;隻影響亮度;
-
蘭伯特餘弦定律(Lambert Consine Law)
取決表面法線和光線的夾角,夾角越大分量越小,漫反射越小;90度漫反射幾乎為0;
-
半蘭伯特模型(Half Lambert)
v社做半條命時提出,改變物體暗區域的光照信息;
將漫反射係數從[0,1]改為[0.5,1],提高暗部的亮度信息;
//法線和光線的夾角, float diflight = dot(s.Normal,lightDir); float hLambert = difLight * 0.5 + 0.5;
3)鏡面反射高光
和觀察者的位置有關,不同角度觀察結果不同;
- Phong光照模型
光線反射向量和觀察向量的夾角;
高光指數:e
在夾角大於90度的情況,會造成高光丟失現象,光線會不連續,有明顯的明暗分界線;
- Blinn-Phong光照模型
光線向量和觀察向量的中間位置(半角向量)和法線的夾角;
在任何角度觀察,夾角都不會大於90度;不會出現高光不連續現象;
-
材質
struct Material { vector3 ambient; //環境光 vector3 diffuse; //漫反射 vector3 Specular; //鏡面反射 vector3 emissive; //自發光 float e; //鏡面反射係數 }
物體最終顏色 = 環境光結果*環境光反射係數 + 漫反射結果*漫反射係數+鏡面反射結果(計算了高光指數)*鏡面反射係數+材質自發光顏色*自放光係數;
2.紋理貼圖 (Textures)
紋理映射,將圖像信息映射到三角形網格;
凹凸貼圖(bump mapping)、法線貼圖(normal mapping)、高度紋理(height mapping)、視差貼圖(parallax mapping)、位移貼圖(displacement mapping)、立方體貼圖(cubemap)、陰影貼圖(shadowmap);
- UV坐標的尋址方式
歸一化到[0,1]之間,像素大小為2的次方,方便計算處理;
尋址方式也叫平鋪方式:重複尋址(repeat)、邊緣鉗制尋址(clamp)和鏡像尋址(mirror);
uv超出0-1,該如何尋址(就是圖片的平鋪,邊緣像素擴展,鏡像);
- 紋理採樣方式
紋理像素和圖元像素不是一一對應,要用到紋理的濾波方式;
點過濾(point)、線性過濾(linear)、最近領點過濾(nearest neighbor point)和雙線性過濾(bilinear),Unity的Trilinear濾波的技術;
- 法線貼圖 (Normal Mapping)
物體空間(object space)和切線空間(tangent space)
根據物體空間計算的法線,在物體旋轉移動後回得到錯誤的光照信息;
切線空間:相對於頂點坐標存儲計算;
頂點本身法線為N軸,模型給定定義一條和該頂點相切的切線T軸,N和T叉乘得到B軸;
法線(N)、切線(T)和副切線(B) ,三個軸組成切線空間;
法線貼圖就是在切線空間中記錄了法線擾動的方向;
以頂點法線N為z軸坐標系,擾動後的法線z軸也總是朝向(0,0,1),所以得得到法線貼圖總是淡藍色(RGB);
法線紋理最終值需要做個映射,由於維度向量取值[-1,1],紋理通道範圍在[0,1],最終記錄結果為:(normal+1)/2;
切線空間坐標繫到世界坐標系的轉換矩陣
物體移動旋轉時,法線乘以這個矩陣就可以得到改變後的法線;
因為z總是朝向(0,0,1)紋理就可以直接記錄xy——紋理壓縮:DXT1,DXT5;
3.鋸齒和抗鋸齒 (Aliasing and Anti-aliasing)
-
超級採樣抗鋸齒 (Super-Sampling Anti-aliasing——SSAA)
將原圖分辨率放大一倍,再採樣;光柵化和片元着色都是原來的4倍,渲染緩存也是4倍;
-
多重採樣抗鋸齒 (Multi-Sampling Anti-aliasing——MSAA)
每個片元有多個採樣點,計算採樣點的覆蓋率(Coverage),光柵化階段計算採樣點覆蓋率,在片元着色器計算顏色值後乘以這個覆蓋率;
MSAA和延遲渲染(deferred render)不兼容(延遲渲染需要Geometry 和Lighting兩個Pass,lighting階段無法通過GBuffer獲得片元覆蓋率);
4.陰影 (Shadows)
光照烘焙獲得Shadowmap;先光照烘焙獲得深度信息,再通過陰影貼圖判斷那些片元落在陰影中;
Shadowmap的精度會導致陰影粉刺,需要便宜深度來消除粉刺現象;
陰影鋸齒通過百分比漸進過濾(PCF)實現軟化陰影(softshadow);
八、測試和混合(Tests & Blending)
1.裁切測試 (Scissor Test)
裁切測試可以避免當視口比屏幕窗口小時造成的渲染浪費問題;一般默認不開啟,
2.Alpha測試 (Alpha Test)
片段着色器中丟棄alpha值小於0.1的片段;
-
Early-Z Culling
硬件廠商用來加速渲染的手段;在片元着色之前提出被遮擋的片元;
但是生效要求只能通過光柵化插值得到深度,不能再片元着色器階段去修改深度緩衝;
3.模板測試 (Stencil Test)
模板測試有一個對應的緩存, 即模板緩存(Stencil Buffer), 用於記錄所有像素的模板值, 默認值為0;
片元攜帶的參考值和模板緩存中的值比較,滿足比較函數,調用操作函數更新模板值;
-
模板值: 模板緩存中已經存在的值
-
參考值: 在渲染該物體前, 由程序設置的指定值
-
比較函數: 決定如何將兩個值作比較的函數
-
操作函數: 定義通過或者不通過測試後對模板值的更新操作
Unity中模板測試不可編程,可配置管線階段;
Stencil
{
Ref refValue //參考值
Comp always //比較函數
Pass keep //模板測試和深度測試都通過後的操作
Fail keep //模板測試和深度測試都未通過後的操作
ZFail keep //模板測試通過而深度測試未通過後的操作
WriteMask 255 //使用參考值更新模板值之前, 在模板值與掩碼按位與之後再更新 255代表不做處理
ReadMask 255 //讀取模板值後, 將其與掩碼按位與之後再與參考值作比較 255代表不做處理
}
/*
UnityEngine.Rendering.CompareFunction比較函數枚舉
0(Disabled): 關閉模板測試, 等同於全部通過測試, 經過測試發現不是真的關閉.
1(Never): 全部不能通過測試
2(Less): 待比較的值小於緩存中的值時通過測試
3(Equal): 待比較的值等於緩存中的值時通過測試
4(LessEqual): 待比較的值小於等於緩存中的值時通過測試
5(Greater): 待比較的值大於緩存中的值時通過測試
6(NotEqual): 待比較的值不等於緩存中的值時通過測試
7(GreaterEqual): 待比較的值大於等於緩存中的值時通過測試
8(Always): 全部通過測試, 默認值
*/
/*
UnityEngine.Rendering.StencilOp操作函數枚舉
0(Keep): 保持模板緩存中的值不變
1(Zero): 將模板緩存中的值置為0
2(Replace): 使用參考值替換模板緩存中的值
3(IncrementSaturate): 使模板緩衝區值增大, 最大限制為可表示的最大無符號值
4(DecrementSaturate): 使模板緩衝區值減小, 最小限制為0
5(Invert): 對模板緩衝區值按位求反
6(IncrementWrap): 與IncrementSaturate類似, 只是達到最大後繼續增大將重新設置為 0
7(DecrementWrap): 與DecrementSaturate類似, 只是達到最小後繼續減小將重新設置為可表示的最大無符號值
*/
4.深度測試 (Depth Test)
比較當前片段的深度值是否比深度緩衝中預設的值小(默認比較方式),如果是更新深度緩衝和顏色緩衝;否則丟棄片段不更新緩衝區的值;
Early-Z Culling也是利用Z-Buffer的技術來進行深度測試的,只不過該測試是在片段着色器之前進行;
深度測試是可配置的階段——ZTest和ZWrite;
-
Z-Fighting
深度緩衝精度不夠,深度值相近的片元會造成重疊模糊問題:
解決:物體不要靠太近;使用高精度的深度緩衝;
- 隱藏面消除 (Hidden Surface Removal, HSR)
前面的圖元組裝裁剪,背面揀選,和Z-Buff都屬於HSR,裁剪針對圖元,Z-Buffer針對像素點;目的都是為了減少到達片元着色器的片元個數,提高渲染性能;
1)視椎體剔除 (Viewing-Frustum Culling)
利用物體包圍盒來做交差檢測,常見的包圍盒有軸對齊包圍盒(AABB)和有向包圍盒(OBB)兩種;
需要是由高效數據結構來提升碰撞檢測的效率——八叉樹(OcTree)、二分空間劃分(Binary Space Partitioning)、四叉樹(Quad Tree)、場景圖(Scene Graphs)、kd樹(K-Dimensional Tree)和層次包圍(Bounding Volume Hierarchies);
計算:
點法式判斷三維空間點和面的關係,裁剪掉不在視景體內的包圍盒頂點;
求視景體六個面:
通過投影矩陣,求出投影和攝像機的逆矩陣,反向求出視景體對應面;
求逆矩陣——行列式,代數餘子式,伴隨矩陣,行列式的值,有固定公式;
近截面裁剪:
三角形和面的關係,用向量表示三角形和面;兩條共起點的向量可以表示三角形;分解成線和面的關係;
三角形和近截面的關係:
1個角在視景體內——偏移另外兩個頂點到視景體平面上;
2個角在視景體內——三角形拆分為2個三角形,注意三角形頂點連線順序;
2)入口剔除 (Portal Culling)
將室內的門或者窗戶看做視椎體來進行裁剪;實現”籠中窺夢”的效果;
3)遮擋剔除 (Occlusion Culling)
通過離線烘焙的犯法來預先計算出潛在可視集合(Potentially Visible Set,PVS);
PVS記錄了每個地形塊(Tiles)可能看到的物體的集合,用於運行時查找計算;
場景劃分為小地形塊,每個塊上隨機取N個採樣點;
從採樣點發射射線獲取場景中和射線相交的物體,記錄物體ID;
根據攝像機位置,使用採樣點幾率的物體id列表進行渲染;
採樣精度和烘焙效率問題;
6.Alpha混合 (Alpha Blending)
經過前面所有的測試才能進入Alpha混合階段,一個測試過不了都到不了Alpha混合;
Alpha混合實現物體半透明效果,渲染順利非常重要,可能需要手動改Queue(渲染隊列)的值;
- 渲染順序:
先渲染不透明物體,從前往後渲染;——不透明物體渲染前先進行深度檢測,先後近的物體,遠處物體通不過深度檢測,就不用進行深度寫入操作;
再渲染半透明物體,從後往前渲染;——半透明物體渲染需要知道前一層的顏色信息進行混合,先渲染遠處物體,在畫近處物體時可以通過顏色緩存獲取前一層顏色信息;
畫家算法:先畫物體會被後畫物體覆蓋;
Unity ShaderLab中渲染隊列設置;
Queue 其他預定義的值為:Background = 1000 , AlphaTest = 2450,Overlay = 4000。默認值是Geometry =2000;
ShaderLab默認開啟深度測試和深度寫入;
//將本 Shader 計算出的顏色值(源顏色值,即藍色) * 源Alpha值(0.6) + 目標顏色值(可以理解為背景色) * (1-0.6)
Blend SrcAlpha OneMinusSrcAlpha
// Transparent (透明) = 3000,值越小越先渲染,而後渲染( Queue 值大)的物體會覆蓋先渲染的物體
Tags {"Queue" = "Transparent"}
ZTest LEqual //小於等於
ZWrite On //打開
//ZTest 可取值為:Greater , GEqual , Less , LEqual , Equal , NotEqual , Always , Never , Off,默認是 LEqual,ZTest Off 等同於 ZTest Always
//ZWrite 可取值為:On , Off,默認是 On
- 順序無關半透明算法(Order-independent transparency,OIT)
雙向剝離(Dual Depth Peeling)——兩個方向剝離,一個從前往後,一個從後往前,兩個方向效率提高;