1. 程式人生 > >[Unity3d]Shader 著色器 學習前瞭解知識

[Unity3d]Shader 著色器 學習前瞭解知識

引言
shader到底是幹什麼用的?shader的工作原理是什麼? 
其實當我們對這個問題還很懵懂的時候,就已經開始急不可耐的要四處搜尋有關shader的資料,恨不得立刻上手寫一個出來。但看了一些資料甚至看了不少cg的語法之後,我們還是很迷茫,UNITY_MATRIX_MVP到底是個什麼矩陣?它和v.vertex相乘出來的又是什麼玩意?當這些問題困擾我們很久之後,我們才發現,原來我們是站在浮沙上築高臺,根基都沒有打牢當然不可能蓋得起高樓大廈了。
那根基是什麼呢?大牛曰,計算機圖形學。 
shader中文名叫著色器,顧名思義,它的作用可以先簡單理解為給螢幕上的物體畫上顏色。而什麼東西負責給螢幕上畫顏色?當然是GPU,所以我們寫shader的目的就是告訴GPU往螢幕哪裡畫、怎麼畫。說到這其實大家應該很明白了,如果我們連GPU的工作原理都不知道,何談指揮它?

說到計算機圖形學,包括我在內很多同學都非常害怕它,因為裡面包含了各種艱深的理論、變換,大量的公式什麼的。其實我們大可不必一開始就嚇倒自己,先從基本概念開始,慢慢來,總有一天我們也會成為大牛~!
最後,這篇文章不算是原創,最多算是摘要+讀後感,很多概念性文字都是我從書裡搬過來後再加上自己的理解,算是和大家一起學習,有理解不當之處還請多多指教。 
廢話不多說,讓我們來進入第一章的學習,GPU的渲染管線。

正文
所謂GPU的渲染管線,聽起來好像很高深的樣子,其實我們可以把它理解為一個流程,就是我們告訴GPU一堆資料,最後得出來一副二維影象,而這些資料就包括了”視點、三維物體、光源、照明模型、紋理”等元素。
在各種圖形學的書中,渲染管線主要分為三個階段:應用程式階段、幾何階段、光柵階段。 


1,應用程式階段。 
這個階段相對比較好理解,就比如我們在Unity裡開發了一個遊戲,其實很多底層的東西Unity都幫我們實現好了,例如碰撞檢測、視錐剪裁等等,這個階段主要是和CPU、記憶體打交道,在把該計算的都計算完以後,在這個階段的末端,這些計算好的資料(頂點座標、法向量、紋理座標、紋理)就會通過資料匯流排傳給圖形硬體,作為我們進一步處理的源資料。

2,幾何階段。 
主要負責頂點座標變換、光照、裁剪、投影以及螢幕對映,改階段基於GPU進行運算,在該階段的末端得到了經過變換和投影之後的頂點座標、顏色、以及紋理座標。簡而言之,幾何階段的主要工作就是“變換三維頂點座標”和“光照計算”。
問題隨之而來,為什麼要變換頂點座標?我是這麼理解的,比如你有一個三維遊戲場景,場景中的每個模型都可以用一個向量來確定它的位置,但如何讓計算機根據這些座標把模型正確的、有層次的畫在螢幕上?這就是我們需要變換三維頂點座標的原因,最終目的就是讓GPU可以將這些三維資料繪製到二維螢幕上。

根據頂點座標變換的先後順序,主要有如下幾個座標空間:Object space,模型座標空間;World space,世界座標空間;Eye space,觀察座標空間;Clip and Project space,螢幕座標空間。下圖就是GPU的整個處理流程,深色區域就是頂點座標空間的變換流程,大家瞭解一下即可,我們需要關注的是每個座標空間的具體含義和座標空間之間轉換的方法。

圖片:image1.png



2.1,從object space到world space 
object space有兩層核心含義,第一,object space中的座標值就是模型檔案中的頂點值,這些值是在建立模型時得到的,例如一個.max檔案,裡面包含的資料就是object space的座標。第二,object space的座標與其他物體沒有任何參照關係,這是object space和world space區分的關鍵。world space座標的實際意義就有有一個座標原點,物體跟座標原點相比較才能知道自己的確切位置。例如在unity中,我們將一個模型匯入到場景中以後,它的transform就是世界座標。

2.2,從world space到eye space 
所謂eye space,就是以攝像機為原點,由視線方向、視角和遠近平面,共同組成的一個梯形體,如下圖,稱之為視錐(viewing frustum)。近平面,是梯形體較小的矩形面,也是靠近攝像機的平面,遠平面就是梯形體較大的矩形,作為投影平面。在這個梯形體的內的資料是可見的,超出的部分會被視點去除,也叫視錐剪裁。
例如在遊戲中的漫遊功能,螢幕的內容隨攝像機的移動而變化,這是因為GPU將物體的頂點座標從world space轉換到了eye space。 

圖片:image2.png


2.3,從eye space到project and clip space 
eye space座標轉換到project and clip space座標的過程其實就是一個投影、剪裁、對映的過程。因為在不規則的視錐體內剪裁是一件非常困難的事,所以前人們將剪裁安排到一個單位立方體中進行,這個立方體被稱為規範立方體(CCV),CVV的近平面(對應視錐體的近平面)的x、y座標對應螢幕畫素座標(左下角0、0),z代表畫面畫素深度。所以這個轉換過程事實上由三步組成:
(1),用透視變換矩陣把頂點從視錐體變換到CVV中; 
(2),在CVV內進行剪裁; 
(3),螢幕對映:將經過前兩步得到的座標對映到螢幕座標系上。 

2.4,primitive assembly(圖元裝配)和triangle setup(三角形處理) 
到目前為止我們得到了一堆頂點的資料,這一步就是根據這些頂點的原始連線關係還原出網格結構。網格由頂點和索引組成,這個階段就是根據索引將頂點連結到一起,組成線、面單元,然後進行剪裁,如果一個三角形超出螢幕以外,例如兩個頂點在螢幕內,一個頂點在螢幕外,這時我們在螢幕上看到的就是一個四邊形,然後把這個四邊形切成兩個小的三角形。
現在我們得到了一堆在螢幕座標上的三角形面片,這些面片是用於光柵化的。 

3,光柵化階段。 
經過上面的步驟之後,我們得到了每個點的螢幕座標值,和我們需要繪製的圖元,但此時還有兩個問題: 
(1)螢幕座標是浮點數,但畫素是用整數來表示的,如何確定螢幕座標值所對應的畫素? 
(2)如何根據已確定位置的點,在螢幕上畫出線段或者三角形? 
對於問題1,繪製的位置只能接近兩指定端點間的實際線段位置,例如,一條線段的位置是(10.48, 20.51),轉換為畫素位置就是(10,21)。 
問題2,涉及到具體的畫線和填充演算法,有興趣的話可以研究。 
這個過程結束後,頂點和圖元已經對應到畫素,之後的流程就是如何處理畫素,即給畫素賦予顏色值。 
給畫素賦予顏色的階段稱為Pixel Operation,是在更新幀快取之前,執行最後一系列針對每個片段的操作,其目的是計算出每個畫素的顏色值。在這個階段,被遮擋的面通過一個被稱為深度測試的過程消除。
pixel operation包含下面這些流程: 
(1)消除遮擋面; 
(2)Texture operation,紋理操作,根據畫素的紋理座標,查詢對應的紋理值; 
(3)Blending,通常稱為alpha blending,根據目前已經畫好的顏色,與正在計算的顏色的alpha值混合,形成新的顏色。 
(4)Filtering,將正在計算的顏色經過某種濾鏡後輸出。 

該階段之後,畫素的顏色值被寫入幀快取中。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

學習著色器,並理解著色器的工作機制,就要對OpenGL的固定功能管線有深入的瞭解。

首先要知道幾個OpenGL的術語

渲染(rendering):計算機根據模型(model)建立影象的過程。
模型(model):根據幾何圖元建立的物體(object)。
幾何圖元:包括點、直線和多邊形等,它是通過頂點(vertex)指定的。

最終完成了渲染的影象是由在螢幕上繪製的畫素組成的。在記憶體中,和畫素有關的資訊(如畫素的顏色)組織成位平面的形式,位平面是一塊記憶體區域,儲存了螢幕上每個畫素的一個位的資訊。例如,它指定了一個特定畫素的顏色中紅色成分的強度。位平面又可以組織成幀緩衝區(framebuffer)的形式,後者儲存了圖形硬體為了控制螢幕上所有畫素的顏色和強度所需要的全部資訊。

OpenGL的固定功能管線

理清了基本的概念,下面瞭解了一些關於OpenGL渲染管線的知識.看了這個之後對於OpenGL的學習我想應當是很有幫助.關於這麼一篇的原文則是GLSL-LIGHTSOURCE 教程一個開篇部分.點選這裡訪問原文。原文是英文的,以下是中文的翻譯,點選訪問下文的原文地址

關於渲染管線將什麼呢?無非就是在OpenGL的管道當中各個部分的功能以及如何在管道當中形成了我們想要的最終的一幅圖.(畫素).而管線當中的操作可分為以下幾個部分:

階段1. 指定幾何物件.

如:點 線 三角形.等一些幾何圖元..OpenGL繪製幾何圖元的方法有以下三種:

  • <1> 一次一個頂點.即使用glBegin()  glVertex() glEnd() 指定幾何物件.
  • <2> 使用頂點陣列..如glDrawArrays.glDrawElements.等.一次性的繪製大量圖元.

上面這兩種模式則是立即模式.即指定完圖元之後會被立即渲染.即將所有資料發往渲染管線後立即被渲染.

  • <3>顯示列表模式.它儲存於OpenGL服務端 (接收OpenGL命令的一端),操作函式有 glNewList、 glEndList、 glCallList .

階段2   頂點處理操作:

不管以上的幾何物件是如何指定的,所有的幾何資料都將會經過這個階段,這個階段負責的則是逐個頂點的操作.

在這個階段能做的工作則是:

  1.  頂點變換:根據模型檢視和投影矩陣變換
  2. 光照計算和法線變換(法線矩陣 是模型矩陣的左上角3*3的逆矩陣)和法線規格化
  3.  紋理座標變換.(紋理矩陣)
  4. 材質狀態:紋理座標生成

而最重要的則是變換以及光照. 每個頂點在這個階段分別是單獨處理的.

這個階段所接收到的資料則是每個頂點的屬性特徵..輸出則是變換後的頂點資料.

階段3  圖元組裝

在頂點處理之後,頂點的全部屬性都已經被確定。在這個階段頂點將會根據應用程式送往的圖元規則如GL_POINTS 、GL_TRIANGLES 等將會被組裝成圖元。

階段4 圖元處理(裁剪 消隱)

  • <1>這個步驟第一個所做的應當是裁剪操作,會將圖元與使用者定義的裁剪平面,即glClipPlane 和模型投影矩陣所建立的視景比較. 這將會裁剪且丟棄位於視景和裁剪平面外部的圖元.不在予以處理.
  • <2> 其次.若是採用透視投影 那麼.將會對每個頂點的x,y z座標分別除以w.
  • <3>緊接著,則是由視口變換將頂點座標變換至視窗座標.
  • <4> 執行消隱操作

階段5  柵格化操作

  • <1>由圖元處理傳遞過來的圖元資料.在此將會被分解成更小的單元並對應幀緩衝區的各個畫素.這些單元被稱之為片元. 一個片元可能包含視窗左邊、深度、顏色、紋理座標等屬性.
  • <2> 片元的屬性則是圖元上頂點資料等經過插值而確定的..這裡生成的片元將會包含主顏色和次顏色.   glShadeMode() 函式的作用將會這裡體現.即使用插值(平滑著色) 或者使用最後一個頂點顏色(平面著色)
  • <3> 點寬 線寬.多邊形模式,正面背面等一些特徵也將是這階段發生作用.
  • <4> 反走樣也是這個階段起作用.

階段6 片元處理

  • <1>上紋理:通過紋理座標取得紋理記憶體中相對應的顏色。
  • <2> 霧化:通過片元距離當前視點位置修改顏色.
  • <3> 顏色彙總..這個與混合完全不同概念.將紋理,主定義的顏色,霧化的顏色,次顏色光照階段計算的顏色 彙總一起.

階段7  逐個片元的操作

  • <1> 所有的一些測試 畫素所有權 剪下(glScissor) Alpha測試(glAlphaFunc) 模版測試(glStencilFunc) 深度測試 (glDephtFunc) 混合(glBlendFunc)

這些操作將會最後影響其在幀緩衝區的顏色值.

階段8  幀緩衝操作

  • <1>這個階段執行幀緩衝的寫入等操作等..最後產生了顯示出來的畫素.

glColorMask、glStrncilMask、glDepthMask、glClearDepht、glClearStencil、glClearColor 等.將在這個階段影響寫入的值.

以上只是討論OpenGL 圖元繪製的基本過程 那麼基於畫素影象繪製.幾乎形同之上..只是在光柵化處理前的操作不一樣.即經過畫素解碼 畫素傳輸.柵格化 最後形成片元...片元之後的處理完全一樣..

可程式設計管線可以替換的功能

在著色器程式設計領域..你將可實現

  • Vertex Shader(頂點著色器) 替換 頂點處理階段
  • Fragment Shader(片元著色器,又叫畫素著色器) 替換 片元處理階段
  • Geometry Shader(幾何著色器) 替換 圖元組裝階段..

因為這三個階段所決定都是最重要效果的階段..對於這些的可程式設計將帶來非常大的好處以及可控制的渲染!!

在前面的固定功能管線提到了,在階段5:柵格化操作 過程中, 片元的屬性會由圖元上頂點資料等經過插值而確定。在頂點著色器處理完畢後,OpenGL都會將頂點與頂點之間的片元(基本上可以理解為畫素)的屬性(如位置座標、紋理座標)進行線性插值。所以,在紋理座標為(1,0)和(0,0)中間的片元會得到一個(0.5,0)的紋理座標,在紋理座標為(0,0)和(1,1)之間的片元會得到一個(0.5,0.5)的紋理座標。然後將這些經過差值處理之後的片元交給片元著色器處理。片元著色器確定最終的片元顏色。

-----------------------------------------------------------------------------------------------------------------------------

  渲染管線過程:

       3D 物體經過一系列的變化(過程),最終顯示在玩家的眼簾的過程。

  3d(面片),3D Object ,經過一系列複雜的過程,這個過程硬體變化是看不到的

  輸入的是一個面片,輸出的是一個形狀

  貼圖:要分uv,要對uv做紋理座標的對映。

  拿到美術資源,有貼圖,是錯覺 ,美術為了便於設計,在製作時候是有貼圖,分uv,及對映,

  提交給引擎是圖片和模型是分離的,還有一個材質球是索引所有圖片的,材質球裡面帶有引數。

  黑盒裡麵包含的步驟:

   頂點處理:

       通過一系列的座標系轉換,講模型的頂點在攝像機前進行位移,並最終投影到攝像機的投影螢幕上。

  本地座標  

  世界座標

  觀察座標

  投影座標

   本地--世界 

        將每個物體模型的位置從以自己為中心的座標系,擺放到整體遊戲世界的具體某一個位置中

  到世界座標系後才能形成一個場景。

  世界--觀察

       將每個物體模型的位置以世界為參照系,轉換到以觀察者為參考系。

  觀察--投影

       將每個物體模型的位置從觀察者所在的座標體系中,轉到觀察者投影平面上

       最特殊的一次座標系轉換(非線性,模型倍扭曲)

  光照: 在觀察座標系下進行。 (觀察可以從不同的角度去觀察object)

       

-------------------------------------------------------------------------------------------------

   面處理:通常有引擎來處理

    面的組裝

    point list  Triangle List  Line List Triangle strip Line strip Triangle Fan

    面擷取

    面剔除

      U3D Shader可以設定

      HRESULT SetRenderState(D3DRS_CULLMODE,value)

      可選擇的值有:

      D3DCULL_NONE:對任何頂點排列方式都不進行剔除

      D3DCULL_CW:  順時針排列的頂點被認為是反面,對他們進行剔除

      D3DCULL_CCW: 逆時針排列的頂點被認為是反面,對他們進行剔除

    注:正反面是根據法線

    視錐剔除:去掉在視錐外的面的部分

    在擷取過程中,落在螢幕外面的面的部分已經被去除,視錐剔除階段主要處理落

在近端截除平面和遠端截除平面之外的面的部分。

   解決方式:通過硬體提供的深度快取Depth Buffer或者z-Buffer來判讀。

 這裡的深度指面距離鏡頭所在平面的距離。  

-------------------------------------------------------------------------------------------------

   光柵化

   畫素處理