著色器只能用在OpenGLES 2.X以上等可編程管道里,而在OpenGLES 1.X是不能使用的。
管線,Pipeline,顯卡執行的、從幾何體到最終渲染圖像的、數據傳輸處理計算的過程
OpenGLES1.X中它是固定管道,整體式封閉的,中間的各道工藝按固定的流程順序走。如圖所示:
從上圖可以看出,這些工藝順序是固定的,整個過程又分為:處理頂點,處理片元,驗證片元信息并存入內存
Rasterizer:光柵化處理,當頂點處理完,會交給rasterizer來進行光柵化處理,結果會吧頂點的坐標信息轉換成能在屏幕顯示的像素信息,即片元(Fragments)
生成片元后,接下來就是對fragments片元的各種驗證,即過濾掉無用的片元,裁剪掉不在視野內的片元,最終把有效片元存儲入內存中。
光柵化處理過程,就是把矢量圖轉化成像素點的過程。我們屏幕上顯示的畫面都是由像素組成,而三維物體都是點線面構成的。要讓點線面變成能在屏幕上顯示的像素,就需要Rasterizer這個過程。
OpenGLES2.X可編程管道,由兩VertexShader(頂點著色器)、FragmentsShader(片元著色器)組成,分別對應上圖中的Coordinates 和Texture等藍色塊
OpenGLES2.0可渲染管道圖:
VertexShader:頂點著色器
頂點著色器輸入包括:
著色器程序——描述頂點上執行操作的頂點著色器程序源代碼或者可執行文件
頂點著色器輸入(或屬性)——用頂點數組提供的每個頂點的數據
統一變量(uniform)——頂點(或片段)著色器使用的不變數據
采樣器——代表頂點著色器使用紋理的 特殊統一變量類型
頂點著色器的輸出在OpenGLES2.0稱作可變變量(varying),但在OpenGLES3.0中改名為頂點著色器輸出變量。
在光柵化階段,為每個生成的片段計算頂點著色器輸出值,并作為輸入傳遞給片段著色器。
插值:光柵器對從頂點著色器傳遞的變量進行插值
為了在屏幕上真正顯示,必須將頂點著色器vs的輸出變量設置為gl_Position,gl_Position是一個保存著頂點齊次坐標的4維向量。ZYZ分量被W分量分割(稱作視角分割)并且XYZ分量上超過單位化盒子([-1, 1])的部分會被裁剪掉。最終的結果會被轉換到屏幕坐標系然后三角形(或其它圖元類型)被光柵器生成對應的像素。
OpenGLES3.0新增了一個功能——變換反饋,使頂點著色器輸出可以選擇性地寫入一個輸出緩沖區(除了傳遞給片段著色器之外,也可用這種傳遞替代)
頂點著色器的輸入和輸出如下圖所示:
先看看腳本:
private final String mVertexShader =
"uniform mat4 uMVPMatrix;\n" +
"attribute vec4 aPosition;\n" +
“attribute vec4 a_color;\n” +
"attribute vec2 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n” +
“out vec4 v_color;\n"
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
"?vTextureCoord = aTextureCoord;\n" +
“ v_color = a_color;\n"
"}\n";
private final String mFragmentShader =
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform sampler2D sTexture;\n" +
"void main() {\n" +
"gl_FragColor = texture2D(sTexture, vTextureCoord);\n” +
"}\n”;
其中腳本語句關鍵字:
attribute:使用頂點數組封裝每個頂點的數據,一般用于每個頂點都各不相同的變量,如頂點位置、顏色等
uniform:頂點著色器使用的常量數據,不能被著色器修改,一般用于對同一組頂點組成的單個3D物體中所有頂點都有相同的變量,如當前光源位置
sampler:這是可選的,一種特殊的uniform,表示頂點著色器使用的紋理
mat4:表示4x4浮點數矩陣,該變量存儲了組合模型視圖和投影矩陣
vec4:表示包含了4個浮點數的向量
varying:用于從頂點著色器傳遞到片元或FragmentsShader傳遞到下一步的輸出變量
uMVPMatrix * aPosition:通過4x4 的變換位置后,輸出給gl_Position,gl_Position是頂點著色器內置的輸出變量。
gl_FragColor:片元著色器內置的輸出變量
PrimitiveAssembly:圖元裝配
圖元即圖形,在OpenGL有幾個基本圖元:點、線、三角形,其他的復雜圖元都是基于這些基本圖元來繪制而成。
在圖元裝配階段,那些經過頂點著色器(VertexShader)處理過的頂點數組或緩沖區的數據(VertexArrays/BufferObjects),被組裝到一個個獨立的幾何圖形中(點,線,三角形)
對裝配好的沒個圖元,都必須確保它在世界坐標系中,而對于不在世界坐標系中的圖元,就必須進行裁剪,使其處于在世界坐標系中才能流到下一道工序(光柵化處理)
這里還有一個剔除操作(Cull),前提是這個功能的開關是打開的:GLES20.glEnable(GLES20.GL_CULL_FACE); 剔除的是圖元的背影,陰影,背面等。
Rasterization:光柵化
光柵化階段繪制對應的圖元(點、線、三角形),將圖元轉化為一組二維數組的過程,然后傳遞給片段著色器處理。這些二維數組代表屏幕上繪制的像素
(PS:上圖中的點精靈光柵化應該是點光柵化)
FragmentShader:片元著色器
片元著色器主要是對光柵化處理后生成的片元逐個進行處理。接收頂點著色器輸出的值,需要傳入的數據,以及它經過變換矩陣后輸出值存儲位置。
著色器程序——描述片元所執行的片元著色器程序源代碼
輸入變量——光柵器對頂點著色器插值后的輸出值
統一變量——片元(或頂點)著色器使用的不變的數據
采樣器——代表片元著色器所用紋理的一種特殊的統一變量類型
片元著色器輸入和輸出關系如下圖所示:
因為光柵化處理后,圖元只是在屏幕上有了像素,卻沒有進行顏色處理,還是看不到東西。
因此FragmentsShader主要的作用是告訴GPU如何處理光照、陰影、遮擋、環境等,然后將結果輸出到gl_FragColor變量中
FragmentsShader只輸出一個顏色值——gl_FragColor,是片元著色器內置的輸出變量
片元著色器腳本示例:
#version 300 es
precision mediump float; // 設置精度限定符
in vec4 v_color;
out vec4 fragColor;
void main()
{
fragColor = v_color;
gl_FragColor = fragColor;
}
光柵化階段生成的顏色、深度、模板和屏幕坐標位置(Xw, Yw)將會變成逐片元操作階段的輸入值
Pre-Fragment Operations:逐片元操作階段
在片元著色器對片元進行綜合的處理,并最終為片元生成一個顏色值,并儲存在gl_FragColor變量后,接下來就是逐個對片元進行一些列的測試。
光柵化處理時,它由于時把頂點從世界坐標系轉換到屏幕坐標系,因此在光柵處理后,每個片元在屏幕上都有個坐標(Xw, Yw)。且存儲在了幀緩沖區(FrameBuffer),
包括片元著色器也是對(Xw, Yw)這個坐標的片元進行處理
Pixel ownership test——像素歸屬測試,它決定FrameBuffer中某個(Xw, Yw)位置的像素是否屬于當前Context
Scissor test——裁剪測試,決定一個位置(Xw, Yw)的片元是否位于裁剪舉行內,如果不在,則被丟棄
Stencil test/Depth test——模版和深度測試,傳入片元的模板和深度值,決定是否丟棄片
Blending——混合,將FragmentShader 新產生的片元顏色值和FrameBuffer中某個位置(Xw, Yw)的片元存儲的顏色值進行混合
Dithering——抖動,對可用顏色較少的系統,可以犧牲分辨率為代價,通過顏色值的抖動來增加可用顏色值。抖動操作和硬件相關,OpenGL允許程序員所有的操作就只有打開或關閉都懂操作。默認情況下抖動是激活的
在逐片元操作階段的最后,片元要么被舍棄,要么將顏色、深度、模板值寫入到幀緩沖區(Xw, Yw)位置,寫入的值取決于啟用的寫入掩碼
寫入掩碼可以更精細地控制寫入的顏色、深度、模板值。
備注:Alpha測試和邏輯操作不再是逐片元操作的一部分,這兩個階段存在于OpenGL2.0盒OpenGL1.x中。