圖形學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)——兩個方向剝離,一個從前往後,一個從後往前,兩個方向效率提高;