(五)unity自帶的着色器源碼剖析之——————前向渲染和延遲渲染

一、前向渲染概述

傳統的渲染方式下所做的光照計算流程稱爲前向渲染。這是一種十分直接的方式,在頂點着色器中對所有待渲染對象的頂點進行一系列的變換,這些變換通常是將頂點的法線和位置變換到裁剪空間。如果採用逐片元光照的方式,在渲染每一幀時,每個片元都要執行一次片元着色器的代碼,這時需要將所有的光照信息都傳遞到片元着色器中。雖然在大部分情況下的光源都趨向於小型化,而且其照亮的區域也不大,但即便是光源離這個像素所對應的世界空間中的位置很遠,但當片元着色器中計算光照時,還是會把所有的光源都考慮進去。例如場景中有n個光源,那麼在每一個片元執行着色器代碼時,都必須把這n個光源都傳遞進着色器去執行光照計算。

前向渲染的方式比較容易理解,但卻有一些性能上的缺陷:

1.首先不能很好地支持場景中存在大量的光源,試考慮場景中有幾十上百個光源時,片元着色器要做的運算量非常大。

2.其次,如果待渲染的場景十分複雜,裏面包含了大量的待渲染對象,將不可避免地產生“在屏幕中同一個像素中,其實會被多個物體所對應覆蓋”的情況,即有很大的深度複雜度,我們會浪費很多GPU資源。例如,如果某像素的深度複雜度值爲n,表示在每一幀中,這個像素上將有n個待渲染對象,進行了n次光照計算,而後繪製(即寫入該像素的顏色值)。但其實有n-1次的光照計算及繪製是無用的,因爲只有最靠近攝像機的那個待渲染對象的計算及繪製纔會被最終顯示到屏幕上。

二、延遲渲染概述

延遲渲染的出現就是爲了解決上述問題,關鍵思想是把大部分光照計算等計算量很大的操作,延遲或者推遲到儘可能後的階段中進行。與在前向渲染中總是將所有的待渲染對象從頂點緩衝區一路線性地渲染到最後的顏色緩衝區的流程不同,延遲渲染將這個過程拆分成了兩個處理通路(pass)。

1.第一個處理通路稱爲幾何處理通路。在此處理通路中,首先將場景渲染一次,獲取到待渲染對象的各種幾何信息,如位置向量、顏色向量、法線向量、深度值等,並且會把這些幾何信息存儲到名爲幾何緩衝區。即G-buffer緩衝區(geometry buffer)中,這些緩衝區將會在之後用做更復雜的光照計算。由於有深度測試,所以最終寫入G-buffer中的各個數據都是離攝像機最近的片元的幾何屬性。這意味着所有不會出現最終屏幕上的片元都在深度測試中被丟棄,最後會被留在G-buffer中的片元都是必定要進行光照計算的。下圖是G-buffer在某一幀的畫面:

G-buffer在本質上和一個普通紋理類似,因此可以使用對紋理進行採樣的着色器內置函數來獲得G-buffer的每一個紋素

第二個處理通路稱爲光照處理通路(lighting pass)。在此處理通路中,將會遍歷所有G-buffer中的每一紋素。這些紋素的內容要傳給執行光照計算的片元着色器所用到的位置、顏色、法線等參數,這些參數在前向渲染模式下,要麼通過片元着色器的uniform變量從CPU傳遞進來,要麼通過頂點着色器經過光柵化處理階段插值生成之後傳遞進來。最關鍵的是,每一個可渲染物體都要執行一次片元着色器中的光照計算,而在延遲着色器(deferred shader)中只需要執行一次光照計算即可,因爲G-buffer中的每一個紋素中對應的光照信息都是最終可視的。而延遲渲染器可以在一個和屏幕大小相等的矩形圖元上執行之,並最終顯示在屏幕上。延遲渲染的流程圖如下圖所示:

每一個待繪製的對象都不在他們自身的片元着色器中執行光照計算。光照計算集中在一個片元着色器中,該片元着色器由一個大小覆蓋住屏幕的矩形圖元執行,這個矩形圖元的渲染效果就是最終要渲染的畫面。

延遲渲染可以不需要消耗大量的性能去執行光照計算,但它本身並不能支持非常大量的光源,因爲即使把原來分散到每一個待渲染物體的片元着色器中執行的光照計算集中到最後的片元着色器中去計算,依然是場景中有n個光源,就要在最後的片元着色器內用n個光源去對每一個片元進行光照計算。因此,要真正能支持大量光源去進行延遲渲染,需要對此技術進行優化

三、Unity3D中的各種渲染路徑

Unity支持不同的渲染路徑,渲染路徑就是指在着色器代碼中應用光照計算的方式。不同的渲染途徑具有不同的性能特徵,對光照和陰影計算也有不同的處理方式。

要對整個項目設置某一種渲染途徑,可選擇Editor|Project Settings|Graphics命令,在彈出的GraphicsSettings面板中,單擊Tier Settings面板中的Rendering Path屬性項就是項目整體的渲染途徑設置項,如下圖:

Unity3D允許在一個工程中使用多個渲染路徑,如攝像機A使用前向渲染途徑進行渲染,攝像機B使用延遲渲染途徑進行渲染。這時,可在每個攝像機對象的Camera組件中的Rendering Path屬性選項中設置攝像機專門使用的渲染途徑,如下圖:

如果選擇使用Use Graphics Settings類型,則此攝像機將會使用整個工程的設置,否則就使用專門爲它指定的設置。但如果當前顯卡不支持所指定的渲染路徑,Unity引擎會自動往下降級渲染路徑的類型,直至顯卡支持爲止。

Unity有4種渲染路徑,分別爲延遲渲染(deferred rendering)、前向渲染(forward rendering)、舊式延遲渲染(legacy deferred rendering)、舊式頂點照明(legacy vertex lit)。

延遲渲染途徑具有光照和陰影效果的保真度最高特點,如果場景中有大量的實時光源,且硬件性能足以支持,則使用這種路徑最爲合適。

前向渲染路徑是引擎默認使用的渲染路徑,此渲染路徑支持所有典型的圖形功能。如法線貼圖、逐像素光照、陰影等。但是在默認設置下,即使指定使用逐片元光照模式進行光照計算,前向渲染路徑中也僅僅是少數光源會使用逐片元光照模式,其餘光源則是逐頂點計算的。

舊式延遲渲染路徑類似於延遲渲染路徑,只是使用不同的技術實現,舊式延遲渲染路徑不支持基於物理渲染(physically based rendering)的標準着色器(standard shader)。

舊式頂點照明途徑是一種光照效果保真度最低的渲染路徑,此路徑不支持實時陰影,可以認爲它是前向渲染路徑的一個子集。

3.1 Unity的前向渲染路徑細節

根據光源對待繪製對象的作用方式,前向渲染路徑技術在一個或者多個渲染通路中渲染每一個待繪製對象,光源本身也會依據它們自身的設置和強度在前向渲染路徑中得到不同的處理。

1.實現細節

在前向渲染路徑中,若干個最亮的且能照亮每個物體的光源執行逐片元光照計算。剩下的光源中,最多4個光源執行逐頂點光照計算,剩下的光源則以球面調諧(spherical harmonics,簡稱球諧函數)的方式計算光照。球諧函數的方式要快得多,但是是一種近似的模擬。一個光源是否以逐片元光照的模式去計算光照效果,主要取決於以下幾個條件

(1)光源遊戲對象的Light組件中,如果Render Mode選項設置爲Not Important,就必定不使用逐片元光照模式計算光照效果。

(2)亮度值最高的,且不滿足條件1的有向平行光源使用逐片元光照模式計算光照效果。

(3)如果光源遊戲對象的Light組件的Render Mode選項設置爲Important,就使用逐片元光照模式計算光照效果。

經過上述條件篩選之後,如果執行逐片元光照模式的光源個數不大於Quality Setting面板中的指明逐片元光源個數上限的Pixel Light Count選項值,則可以有更多的光源以逐片元光照模式計算光照效果。

着色器的光照計算在其渲染通路中進行。前向渲染的渲染通路有兩種,分別是基本通路和附加通路。基本通路中,對默認的有向平行光源進行逐片元的光照計算。並且當默認的有向平行光源的Light組件中的Shadow Type屬性爲Soft Shadows或者Hard Shadows時,將會啓動陰影效果。所有其他的基於頂點或者基於球諧光照的光源也在基本通路中進行光照計算,物體的自發光和環境光計算也在基本通路中進行。

其他逐片元光照計算的光源則在附加通路中進行光照計算,且每個這樣的光源執行一次。

假設光源A——H的顏色和亮度都一樣,其Light組件的Render Mode屬性值也都爲Auto。即他們採用何種模式進行光照計算將由引擎決定。如下圖,從圓球的角度來看,離圓球越近的光源越亮,所以最亮的4個光源A-D將使用逐片元光照,光源D—G使用逐頂點光照,光源G—H使用球諧光照:

然後從下圖我們可以看到,光源D參與了逐片元光照和逐頂點光照兩種模式的計算,光源G參與了逐頂點光照和球諧光照兩種模式的計算,這樣處理是爲了光照效果能平滑地變化而不至於變得很突兀

2.聲明一個渲染通路爲前向渲染路徑中基本通路的設置 

我們使用LightMode標籤的值用來指定着色器的渲染通路類型,引擎會根據該標籤的值決定光照計算方式。在基本通路中,如果啓用了OnlyDirectional標識符,則只對默認的有向平行光源、環境光、光照探針和光照貼圖進行計算,原本在基本通路中計算的頂點光照和球諧光照不再計算。另外,除了設置LightMode爲ForwardBase之外,還需要使用編譯指示符multi_compile_fwdbase指令,這樣纔會激活基本通路中進行光照計算所需要的一些宏,如光照衰減值等。

3.聲明一個渲染通路爲前向渲染路徑中附加通路的設置

把LightMode的類型指定爲ForwardAdd已聲明本渲染通路是前向渲染路徑中的基本通路,同時也要啓用multi_compile_fwdadd編譯指示符相關的宏、變量和函數。默認地其他通路是沒有陰影效果的,即使光源的Light組件中shadow Type屬性設置爲Soft Shadows或者Hard Shadows也是沒有效果的。要使用陰影效果,要改爲multi_compile_fwdadd_fullshadows,才能令點光源和聚光燈光源開啓陰影效果。另外,還需要設置混合模式,一般採用Blend One One。這是因爲其他通路是每一個光源執行一次,而大多數情況下是希望每個光源產生的照明效果是疊加再一次的,如果不採用Blend One One,則執行本次additional pass的光照結果會覆蓋上一次的執行結果,這樣看起來好像只受到最後一次執行additional pass的光源影響

3.2 Unity3D的延遲渲染路徑細節

Unity支持兩種延遲渲染路徑,即5.0版本之前的舊式延遲渲染路徑和之後版本的延遲渲染路徑。兩者差異不大,主要區別是舊式延遲渲染路徑不支持基於物理渲染的標準着色器。

使用延遲渲染時,能夠對待渲染物體進行照明的光源格式在理論上是無限的。所有的光源都執行逐片元光照,因此所有光源都能使用cookie紋理和產生陰影,在光照計算中可以使用諸如法線貼圖等逐片元光照技術,當然延遲渲染缺點也比較明顯:

(1)不支持真正的反走樣(anti-aliasing)功能。

(2)無法處理半透明物體的渲染。

(3)對顯卡的性能有較高的要求。顯卡必須支持多渲染目標功能,必須使用shader model 3.0及以上版本,支持深度紋理和雙面模板緩衝。

(4)如果執行延遲渲染的攝像機是正交的,即攝像機Camera組件的Projection屬性設置爲Orthographic,則攝像機執行的渲染路徑將會回退(fall back)到使用前向渲染路徑。

延遲渲染中實時光源的渲染開銷與該光源所能照射到的像素數成正比,而與場景本身的複雜度無關,所以照射範圍很小的光源的光照計算是很高效的。如果它們完全或被場景中的物體遮擋住,性能代價更爲低廉。當然要產生陰影效果的光源比不產生的性能消耗要大得多。在延遲渲染中,投射陰影到場景中的物體仍然需要對產生陰影的光源執行一次或多次渲染計算

當使用延遲渲染時,Unity3D引擎要求提供兩個渲染通路:第一個渲染通路用於渲染G-buffer,會把待渲染物體的漫反射顏色、鏡面反射顏色、平滑度(smoothness)、法線、自發光顏色及深度值信息渲染到基於屏幕空間的G-buffer中。每一個待渲染物體僅執行一次此通路。

默認的G-buffer包含以下幾個可渲染紋理(RT)和一些緩衝區,如下:

在引擎 提供的Internal-DeferredShading.shader文件中預定了_CameraGBufferTexture0~2這3個着色器變量,分別對應RT1~3這3個可渲染紋理。

第二個渲染通路用於執行真正的光照計算,這個通路會使用第一個渲染通路得到的技術來計算最終的光照顏色,然後存儲到一個幀緩衝區中。

 

 

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