天天看點

OpenGLES(二)——渲染管線渲染管線

渲染管線

概述

    在OpenGL中,任何事物都在3D空間中,而螢幕和視窗卻是2D像素數組,這導緻OpenGL的大部分工作都是關于把3D坐标轉變為适應你螢幕的2D像素。

    3D坐标轉為2D坐标的處理過程是由OpenGL的圖形渲染管線(Graphics Pipeline,大多譯為管線,實際上指的是一堆原始圖形資料途經一個輸送管道,期間經過各種變化處理最終出現在螢幕的過程)管理的。

    圖形渲染管線可以被劃分為兩個主要部分:第一部分把你的3D坐标轉換為2D坐标,第二部分是把2D坐标轉變為實際的有顔色的像素。

2D坐标和像素也是不同的,2D坐标精确表示一個點在2D空間中的位置,而2D像素是這個點的近似值,2D像素受到你的螢幕/視窗分辨率的限制。

    圖形渲染管線接受一組3D坐标,然後把它們轉變為你螢幕上的有色2D像素輸出。圖形渲染管線可以被劃分為幾個階段,每個階段将會把前一個階段的輸出作為輸入。所有這些階段都是高度專門化的(它們都有一個特定的函數),并且很容易并行執行。

    正是由于它們具有并行執行的特性,當今大多數顯示卡都有成千上萬的小處理核心,它們在GPU上為每一個(渲染管線)階段運作各自的小程式,進而在圖形渲染管線中快速處理你的資料。這些小程式叫做着色器(Shader)。

    有些着色器允許開發者自己配置,這就允許我們用自己寫的着色器來替換預設的。這樣我們就可以更細緻地控制圖形渲染管線中的特定部分了,而且因為它們運作在GPU上,是以它們可以給我們節約寶貴的CPU時間。OpenGL着色器是用OpenGL着色器語言(OpenGL Shading Language, GLSL)寫成的。

    下面,你會看到一個圖形渲染管線的每個階段的抽象展示。要注意藍色部分代表的是我們可以注入自定義的着色器的部分。

OpenGLES(二)——渲染管線渲染管線

    圖形渲染管線包含很多部分,每個部分都将在轉換頂點資料到最終像素這一過程中處理各自特定的階段。

    首先,我們以數組的形式傳遞3個3D坐标作為圖形渲染管線的輸入,用來表示一個三角形,這個數組叫做頂點資料(Vertex Data);頂點資料是一系列頂點的集合。一個頂點(Vertex)是一個3D坐标的資料的集合。而頂點資料是用頂點屬性(Vertex Attribute)表示的,它可以包含任何我們想用的資料。

    圖形渲染管線的第一個部分是頂點着色器(Vertex Shader),它把一個單獨的頂點作為輸入。頂點着色器主要的目的是把3D坐标轉為另一種3D坐标(後面會解釋),同時頂點着色器允許我們對頂點屬性進行一些基本處理。

    圖元裝配(Primitive Assembly)階段将頂點着色器輸出的所有頂點作為輸入(如果是GL_POINTS,那麼就是一個頂點),并所有的點裝配成指定圖元的形狀;本節例子中是一個三角形。

    圖元裝配階段的輸出會傳遞給幾何着色器(Geometry Shader)。幾何着色器把圖元形式的一系列頂點的集合作為輸入,它可以通過産生新頂點構造出新的(或是其它的)圖元來生成其他形狀。例子中,它生成了另一個三角形。

    幾何着色器的輸出會被傳入光栅化階段(Rasterization Stage),這裡它會把圖元映射為最終螢幕上相應的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器運作之前會執行裁切(Clipping)。裁切會丢棄超出你的視圖以外的所有像素,用來提升執行效率。

渲染管線各個步驟詳解

1.頂點輸入

float 類型的數組作為頂點輸入到頂點着色器,其格式一般是這樣的:

頂點數組:

OpenGLES(二)——渲染管線渲染管線

因為是在 2D 架構中,是以這個的 Z 值一般直接寫 1 即可。

2. 頂點着色器處理

這個着色器是可程式設計着色器,可以對輸入的頂點進行一些自定義的處理,後面的文章會詳細講;

另外,如何對頂點資料做解析,後面的文章也會講到。總之,頂點着色器就是對輸入的頂點做一些自定義的處理并輸出給圖元裝配着色器;

3. 圖元裝配

圖形裝配着色器接收到頂點着色器的輸出之後,按照圖元參數對輸入的頂點做一些處理,包含裁剪、透視分割和視口變換等,最終裝配成指定的圖元。

比如裁剪,如果給入的頂點超過了可視範圍,那麼圖形裝配之後生成的頂點就不一定是原來的頂點了,而是處理過後的頂點。

即:将輸入的頂點處理成符合目前上下文的頂點;

圖元的位置、形狀等資訊在計算機中仍然是通過頂點 + 圖元類型來表示的,是以,這一步的輸出形式依然是頂點數組。而圖元的類型有點、線段、三角形,這個參數的傳遞是貫穿整個 pipline 的,在 draw calls 中傳入,比如 drawArray 方法;

4. 圖元的概念

說說自己對圖元和圖元枚舉的概念的了解。

首先是 draw call 中的圖元枚舉:

OpenGLES(二)——渲染管線渲染管線

這個枚舉是告訴着色器,需要按照怎樣的方式處理資料。比如裝配階段,如果輸入是 3 個頂點且有一個頂點超出坐标系,繪制圖元的類型是線段和三角形時,最終生成的頂點就會有差別:

線段時,仍然輸出 3 個頂點;

三角形時,輸出 4 或 5 個頂點;

圖元的概念:圖元其實就是一個可視化的概念,目的是友善了解 pipline,對于計算機而言,圖元本質上仍然是由(頂點資料 + 圖元類型)組成;

比如提供三個點繪制三角形,且有一個點超出範圍時,圖元裝配階段需要進行裁剪。圖形裝配之後,假如輸出輸出 5 個點:

OpenGLES(二)——渲染管線渲染管線

最終其實是生成了 3 個三角形的圖元,但是這三個圖元本質上是由 5 個頂點 + Triangle 這個枚舉來辨別;

另外,OpenGL ES 是 OpenGL 的子版本,用于嵌入式裝置,在各個方面進行了精簡。OpenGL ES 中隻有點、線、三角形,所有的圖形最終會轉換成三角形來進行處理,沒有 OpenGL 中的矩形、多邊形等概念;

5. 幾何着色器處理

幾何着色器階段是對上一步的圖元進行再加工,可能會生成新的頂點和圖元。

最開始的 pipline 圖中,一個三角形變成了兩個,需要注意的是,這裡變成兩個三角形并不是某些渲染流程相關的潛規則,而是完全由幾何着色器的代碼決定的。

因為幾何着色器是可程式設計的,是以這裡開發者可以編寫幾何着色器對圖元進行多樣化的處理的。

是以,各種各樣的幾何着色器可以了解成實作各種功能的 Api,也就是傳入頂點資料之後自動生成對應的圖中繼資料。比如上述小房子的幾何着色器就是實作傳入一個頂點,進而生成一個小房子圖元的功能。

6. 光栅化

OpenGL 中的一個片段(Fragment)是 OpenGL 渲染一個像素所需的所有資料。

幾何着色器的輸出會被傳入光栅化階段(Rasterization Stage),光栅化階段會把圖元映射為最終螢幕上相應的像素,生成供片段着色器(Fragment Shader) 使用的片段(Fragment)。

這一步說白了就是圖元到硬體(像素)的轉換。

因為螢幕是由很多個像素點組成,一個圖形要展示在螢幕上,就需要知道哪些像素點需要亮起來,且要用什麼強度的信号來展現出怎樣的顔色。光栅化這一步就是将上下文坐标系中的圖元轉化成硬體層面上真正要展示的像素點,即:光栅化就是計算出哪些像素需要展示。而像素具體需要展示成什麼顔色則在下一步的片段着色器中計算;

一種簡單的劃分就是根據中心點,如果像素的中心點在圖元内部,那麼這個像素就屬于這個圖元。如上圖所示,深藍色的線就是圖元資訊所建構出的三角形;而通過是否覆寫中心點,可以周遊出所有屬于該圖元的所有像素;

OpenGLES(二)——渲染管線渲染管線

如上圖,淺藍色部分就是光栅化計算出來需要展示的像素點;

之前的步驟都相當于美術中的構圖,隻不過電腦世界的構圖是以三角形作為基本圖形。而光栅化這一步就相當于美術中的素描,這一步完成後就意味着将形狀畫到紙上了。素描完>成了,接下來就是上色了,也就是美術中的水彩等階段。

另外,在片段着色器運作之前會執行裁切(Clipping)。裁切會丢棄超出你的視圖以外的所有像素,用來提升執行效率。

7.片段着色器

片段着色器(Fragment Shader)也叫做像素着色器(Pixel Shader),這個階段的目的是給每一個像素 Pixel 賦予正确的顔色。

計算像素點的顔色需要頂點 + 場景資料。頂點可以從前面的步驟中擷取。通常,片段着色器包含 3D 場景的資料(比如光照、陰影、光的顔色等等),這些資料可以被用來計算最終像素的顔色由于需要處理紋理、光照等複雜資訊,是以該階段通常是整個系統的性能瓶頸。

8. 混合階段

在所有對應顔色值确定以後,最終的對象将會被傳到最後一個階段,我們叫做Alpha測試和混合(Blending)階段。這個階段檢測片段的對應的深度(和模闆(Stencil))值(後面會講),用它們來判斷這個像素是其它物體的前面還是後面,決定是否應該丢棄。這個階段也會檢查alpha值(alpha值定義了一個物體的透明度)并對物體進行混合(Blend)。是以,即使在片段着色器中計算出來了一個像素輸出的顔色,在渲染多個三角形的時候最後的像素顔色也可能完全不同。

繼續閱讀