Unity shader學習筆記(一)—— 渲染流程

渲染流水線

爲了渲染一個模型,我們需要知道這個模型的每個頂點信息,有可能還需要知道由幾個頂點構成的每個片元的顏色,這個顏色可能是直接指定的頂點顏色然後做的插值,也可能是用一張紋理“貼”上去,然後還要經過頂點變換,裁剪,等等操作,最後才渲染到屏幕上。爲了加快這些步驟的執行,我們希望能夠將這些步驟拆分成具體的幾步,參考CPU指令流水線的思想,用流水線的思想加快執行。
在這些步驟中,可以分成兩個部分。一部分在CPU上執行,一部分在GPU上執行。一開始模型的信息都存儲在硬盤上,然後CPU要將其讀取到內存中,之後設置一些渲染的狀態(比如使用哪張紋理,頂點變換規則,顏色計算規則等等),然後將渲染狀態和模型的頂點信息發送到顯存,再發送一個指向該渲染狀態的渲染命令Draw Call,通知GPU讀取該渲染狀態和模型頂點信息,開啓第二部分的步驟繪製模型。而在第二部分中,又可以拆分出來兩部分。總共三部分的步驟,分別如下:

應用階段
幾何階段
光柵化階段

其中應用階段在CPU上執行,幾何階段和光柵化階段在GPU上執行。這三個部分裏面的十幾個步驟(下文提及)最終構成了整個渲染流水線的元素。
注意上文提到的渲染命令,CPU有可能會發送多個,這些命令都會發送到一個命令緩存中,再由GPU一一讀取。

OpenGL 和 DirectX

在這裏插入圖片描述
這張圖是《Untiy shader 入門精要》下的一張圖,非常清晰地解釋了CPU,GPU,OpenGl/DirectX之間的關係。CPU會執行應用程序的代碼,裏面可能會包含一些渲染指令(即所謂的Draw Call),這些渲染指令其實並不是真正地直接發送到命令緩存中,而是調用了OpenGL或者DirectX提供的接口,由這兩個計算機圖形處理庫去發送真正的渲染命令。渲染命令由顯卡驅動去翻譯成GPU能夠識別的語言,然後GPU讀取顯存中由渲染命令指定的數據,最後開始渲染,在此之前,CPU已經提前將數據從內存中拷貝到了顯存裏。
一個遊戲引擎,爲了能夠跨平臺開發,跨平臺發佈,需要同時支持OpenGL和DirectX兩個圖形處理庫。一般來說,我們在遊戲引擎中寫的代碼,就是上圖中的應用程序一項。不必直接接觸OpenGL和DirectX提供的接口,因爲遊戲引擎一般都已經提供了調用這兩個庫的接口,並且做了很好適配和封裝。但是有時候,我們仍然需要手動地去爲這兩個圖形處理庫的不同做一些處理。

GPU做的事情

第一張圖中,有兩個部分是GPU做的。一個是幾何階段,一個是光柵化階段。這其中又細分爲好幾個步驟,以下是《Unity shader 入門精要》的一張圖:
在這裏插入圖片描述
其中,頂點着色器,曲面細分着色器,幾何着色器和片元着色器是可編程的。在應用階段,CPU設置的渲染狀態時,可以設置分別使用哪幾個着色器。實際在Unity上實踐時,這部分是通過在shader中編寫着色器代碼(每個着色器對應一個函數)來實現的。
幾何階段從一開始接收物體的頂點數據,到最後的屏幕映射,輸出的是經過座標變換後的窗口座標系下的座標(關於具體的座標變換,可以參考關鍵詞 MVP座標變換)。這些新的頂點數據會傳給光柵化階段做下一步的處理。
在光柵化階段的片元着色器會對每個片元進行顏色填充或者紋理映射,計算片元的輸出顏色。

頂點着色器

頂點着色器接收來自模型的頂點數據,這些數據包括每個頂點原始的座標、紋理座標、頂點法線和頂點顏色等頂點屬性。我們可以在頂點着色器中進行一些光照計算,然而這樣的光照計算很粗糙,的出來的結果不是特別自然,所以一般不會這麼做。頂點着色器最主要的工作就是進行頂點座標變換(MVP矩陣變換),或者你可以指定頂點隨時間的變化規律,實現諸如水的波紋這樣的複雜的效果。一個頂點着色器必須輸出頂點在齊次裁剪空間座標系下的頂點座標以及各個頂點顏色。

曲面細分着色器

曲面細分着色器出現得比較晚,是渲染管線中可選的部分,有些渲染管線的相關書籍裏面並沒有對這方面進行介紹。曲面細分利用鑲嵌化技術對三角面進行細分,從而增加物體表面的三角面的數量。在這裏我們可以實現一些細節層次相關的技術,讓近處的物體具備更加豐富的細節,而遠處的物體減少細節上的渲染,優化性能。

幾何着色器

幾何着色器也是一個可選的階段。不像頂點着色器每次只能處理一個頂點,而且不能破壞和創造頂點。幾何着色器的優點是能夠輸入多個頂點進行處理,並且破壞和創建頂點。幾何着色器最常見的應用是將一個頂點拓展成一個三角面或四方形。這使得幾何着色器能夠完成許多絢麗的效果。
注:儘管幾何着色器能夠一次處理多於一個的頂點數據,但是官方並不建議幾何着色器處理太多頂點,否則會對性能產生較大的影響。

裁剪

經過頂點着色器的座標變換之後,我們就可以判斷物體在經過最後的投影變換後哪些部分被投影在屏幕之外。一個物體如果完全在屏幕之外,那麼直接放棄對它的渲染,如果一個物體一部分在屏幕外,一部分在屏幕內,就需要進行裁剪操作。

屏幕映射

下圖來自https://blog.csdn.net/wangdingqiaoit/article/details/51589825
在這裏插入圖片描述
上面講到,頂點座標系主要完成的是前面三個變化(Modeling Transformation, View Transformation, Porjection Transformation,統稱MVP矩陣變換),經過這三個步驟之後,物體的座標被轉化成虛擬攝像機的膠片上的二維座標。你可以設置虛擬相機的觀察範圍和膠片大小,在Unity中,這些一般跟四個參數有關:Field of View, Clipping Planes, Viewport Rect以及Game視圖的預定義屏幕分辨率(可以設置是比例,也可以設置絕對值)。你可以在Unity上調節這四個參數看看效果。之後,在屏幕映射階段還會做兩個操作:透視除法和視口變換。屏幕座標系以整數爲單位,具體座標範圍卻跟設備分辨率息息相關。爲了規避設備相關性,需要定義一個與具體設備無關的座標系,這個座標系座標範圍一般從0到1,或者-1到1 。之後再根據視口變換轉換到具體設備的屏幕座標系上去。

三角形設置

這一步驟接收上一個部分處理好的頂點,根據頂點序列產生三角形。在一些渲染管線的教程中,這一部分又叫圖元裝配,可能更貼切一點。它將輸入的頂點組裝成基本的圖元供片元着色器處理。一般來說,這些圖元就是三角形——最簡單的多邊形。

三角形遍歷

這一階段其實是一個光柵化處理。頂點的座標是浮點類型的,但是屏幕像素是離散的,整型的。這一階段會遍歷每個三角形,判斷每個三角形覆蓋了哪些像素,然後輸出片元序列。你可以將片元暫時跟像素一一對應,但它不是像素。再經過片元着色器的處理之後,片元纔會變成真正的像素信息。

片元着色器

在編寫Unity Shader時,除了頂點着色器,另一個可編程且必須指明的着色器就是片元着色器(Fragment Shader)。片段着色器在DirectX中也被稱爲像素着色器(Pixel Shader)。片段着色器用來決定屏幕上像素的最終顏色。在這個階段會逐片元地進行光照計算,陰影處理甚至一些顏色變換,從而產生一些高級的效果。

逐片元操作

終於來到最後的階段。這個階段亦即測試混合階段,包括裁切測試、Alpha測試、模板測試和深度測試。沒有經過測試的片元都將被丟棄,而不會被顯示在屏幕中。比如兩個片元都佔據同一個像素,但是最終只會顯示深度信息高的像素(在3D場景中,深度一般意味着離相機更近)。

紋理座標

再提一下紋理座標這個概念,它將一張紋理的每個像素映射到一個座標系中,這個座標系的原點(OpenGL)(-1,-1)爲紋理的左下角,而紋理的右上角固定爲座標(1,1)。即每個像素的橫座標映射到(-1,1)的某一點,縱座標同理。不過在DirectX中,原點是在紋理的左上角。然後,紋理映射將紋理的紋理座標映射到模型的每個點,以此在模型上顯示出紋理。由於紋理座標水平方向上一般叫U座標,豎直方向上一般叫V座標,故紋理座標又稱UV座標。在輸入到GPU渲染管線的頂點信息中,每個頂點都會包含各自的紋理座標(如果你使用了紋理的話),GPU會將紋理上對應座標的像素顏色賦給頂點,然後在光柵化階段對頂點顏色進行插值產生每個三角形的顏色。

總結

最後,總結一下一個模型是如何被渲染到屏幕上。渲染一個模型需要什麼信息?首先是模型的網格,即頂點信息,它決定一個模型具體長什麼樣,其次,由頂點構成的面元具體如何被繪製?一種可以在頂點信息中包含每個頂點的顏色,然後每個面元由構成它的頂點的顏色插值得到。另一種,指定一張紋理給它,通過紋理映射技術將紋理“貼”在模型表面。那我們定好了形狀的物體以什麼樣的姿態呈現在屏幕上的(比如你可以指定它總是以哈哈鏡的效果呈現在屏幕上),然後顏色如何插值,紋理具體怎麼“貼”,那就需要自己書寫頂點着色器,片元着色器等,然後指定使用你寫的着色器。這些東西一併打包發送至顯存中,然後發送一條渲染命令,通知GPU渲染模型。

歡迎大佬批評指正。

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