The Graphics Renderring Pipeline

    開始學習計算機圖形經典Real-Time Rendering啦,自己的一些學習心得和總結,以及書上的效果會慢慢總結並在unity上實現以下。這篇文章主要是對數據Real-Time Rendering前三章的總結,同時文章標題使用的該書第二章的標題。

    渲染管線包括三部分,應用部分,幾何部分,光柵化。如下圖所示。  

    1.The Application Stage

    顧名思義,開發者擁有對這個階段完全控制的權力,因爲這個階段發生在CPU上。一般像Collision Detection(遮擋剔除)、動畫變幻、視口裁剪等會由開發者在此階段實現。從而優化後面階段的工作量。CPU並不是並行架構,對於CPU來說處理分支等指令更有效率,但不適合執行大量相同的計算邏輯。所以一般在應用層面會在CPU處理以model爲單位的各種邏輯或計算。(可能我這裏表述並不準確,我的理解舉例在unity中,是以一個Render爲最小單位的)。並將通過第一階段的model需要計算的頂點、三角形頂點序列、法線、貼圖以及其他相關參數傳入第二階段。

    2.The Geometry Stage

    第二各階段開始處理多邊形和頂點級別的數據。在這裏又被細分幾個階段,如下圖所示。

    首先將所有頂點、法線等信息統一在一個唯一的座標系即世界座標系中,在這裏我們對頂點做從模型自身座標系到世界座標系的轉換,即Unity中的轉換矩陣M(UNITY_MATRIX_M)。對於結果來說,只有在能被攝像機照射到的纔有可能會被渲染。所以之後還有從世界座標系轉換到攝像機自身座標系中。即Unity中的轉換矩陣V(UNITY_MATRIX_V)。

    然後會做一次頂點着色操作。在這裏就是Vertex Shader發揮的空間了。通常會進行逐頂點的相關操作。

    接下來渲染管線會執行投影操作。將上述頂點信息轉換到一個大小爲1的單元立方體中。爲接下來的裁剪做鋪墊。即Unity中的轉換矩陣P(UNITY_MATRIX_P)。

    基礎幾何圖元如點、線、三角、多邊形幾何體只有完全在視野內或部分在視野內,才應該進入接下來的光柵化處理階段。舉個例子,一條線段一個頂點在視錐體外面一個頂點在視錐體內,我們會保留視錐體內的頂點,並將線段與視錐體相交處的點作爲新頂點取代在視錐體外部的頂點。

    在第二階段的最後做屏幕映射工作。能走到這一步的圖元將會將自身座標轉換爲屏幕座標。之前在三維控件的z座標將被縮放處理到(-1 <= z <= 1)。

    3.The Rasterizer Stage

    光柵化階段同樣被劃分出幾個階段,如下圖所示。

    在TriangleSetup和TriangleTraversal階段中。三角形面的不同和一些其他的信息被計算後,每個三角形面所覆蓋的像素點被檢測併產生一個片元。這個過程通常被稱爲Triangle Traversal or Scan Conversion。每個三角形片元的屬性在三個頂點間使用內插值生成。

    接下來進入到片元着色階段。此階段便是Pixel Shader(片元着色器發揮的時候)。

    每個像素的信息被存放在Color Buffer中。通過一系列計算最終生成顏色值輸出到Back Buffer,這個緩衝區的意義是爲了防止人們看到正在被光柵化和送向屏幕的圖元。一旦屏幕圖像在Back Buffer中渲染完畢,就會與之前顯示在屏幕上的Front Buffer交換。

    GPU緩衝區當然不止上述部分。現代GPU還有諸如用於檢測深度的Z-Buffer,Stencil Buffer等緩存,一般在渲染系統中他們被統稱爲Frame Buffer。

 

    接下來我們重點總結一下Shader部分的內容。隨着近些年渲染管線和Shader的發展。Shader Model標準從1.0發展到4.0,現在一般的設備都不再支持1.0。這裏列出其他幾個版本間的差別。

 

    在Shader Model4.0中定義了一個新的可編程階段 :The Geometry Shader。一般在這個shader中可以裁剪或生成新的圖元。同時還有一個流式輸出的概念,術語爲Stream Output。貌似做流動效果的時候比較有用。

    在光柵化的片元着色階段,最後輸出時有一個MRT的技術。可以並行渲染多個目標。我猜測Unity中使用RenderTexture實現各種後處理特效的過程就在此處進行。

    在光柵化的合成階段中,大家熟知的ZTest,Blend等會發生。在書中我看到這樣一句話,使用ZTest後,當一個片元沒有通過ZTest被捨棄,整個渲染管線的自動優化都會失效。不知道這是否意味着在Unity中對半透的處理上,Blend的性能是否強於CutOff。當然這應該要分畫面複雜度。當複雜度非常高時,使用Transparent導致OverDraw的直線上升性能損耗應該是更高的。

    第三章最後介紹了一個NVIDIA's FX的特效的一部分,稱爲Gooch Shading。內容和實現非常簡單。主要實現了隨着光源與物體位置的不同,物體身上的色調從冷色調到暖色調的變化。下面簡單說下Unity的效果實現。

    Shader部分:需要定義兩個Color表示冷色調和暖色調的具體顏色值,一個向量表示光源在世界空間下的座標,一個主貼圖。然後在頂點着色器中傳入uv,世界空間下的法向量,世界空間下頂點到光源位置的方向,經過MVP後的頂點座標。然後在片元着色器中通過點乘法向量方向和頂點到光源的方向得到一個-1到1的值。將該值變化到0-1的區間插值冷暖色調,最後與主貼圖採樣的顏色相乘即可。

    變量和結構體定義部分:

sampler2D _MainTex;
fixed4 _WarmColor;
fixed4 _CoolColor;
float3 _Light_Pos;

struct a2v {
	float4 vertex : POSITION;
	float2 uv : TEXCOORD0;
	float3 normal : NORMAL;
};

struct v2f {
	float4 pos : SV_POSITION;
	float2 uv : TEXCOORD0;
	float3 light_vec : TEXCOORD1;
	float3 world_normal : TEXCOORD2;
};

    頂點着色器部分:

v2f vert (a2v v) {
	v2f o;
	o.pos = UnityObjectToClipPos(v.vertex);
	o.uv = v.uv;
	float4 vertexInWorld = mul(unity_ObjectToWorld, v.vertex);
	o.light_vec = (_Light_Pos.xyz - vertexInWorld);
	o.world_normal = UnityObjectToWorldNormal(v.normal);
	return o;
}

    片元着色器部分:

fixed4 frag (v2f i) : Color {
    fixed4 color = tex2D(_MainTex, i.uv);
	float3 light_vec = normalize(i.light_vec);
	float3 world_normal = normalize(i.world_normal);
	float ldn = dot(light_vec, world_normal);
	float mixer = 0.5 * (ldn + 1.0);
	fixed4 res = lerp(_CoolColor, _WarmColor, mixer);
	return res * color;
}

      最後還需要一個腳本傳入光源位置,我直接就用unity提供的Directional Light來表示光的位置了。僅僅表示光的位置而已。代碼邏輯很簡單,每幀告訴Shader當前位置即可。

public class LightPosPara : MonoBehaviour
{
    private Transform mTrans;

    void Awake() {
        mTrans = transform;
    }

    void Update()
    {

        Shader.SetGlobalVector("_Light_Pos", mTrans.position);
    }
}

源碼地址:Git網連不上,一旦上傳了就會更新00

 

 

    

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章