這次我們來(lái)從OpenGL ES的角度來(lái)學(xué)習(xí)一下頂點(diǎn)著色器和片元著色器。在這之前我們先來(lái)看一下在OpenGL ES 3.0下的圖形管線:
頂點(diǎn)著色器 Vertex Shader
頂點(diǎn)著色器,實(shí)現(xiàn)了一種通用的可編程方法操作頂點(diǎn)忘瓦。
頂點(diǎn)著色器可用于傳統(tǒng)的基于頂點(diǎn)操作榄鉴,例如通過(guò)矩陣變換位置编检、計(jì)算照明方程式以生成逐頂點(diǎn)的顏色以及生成或者變換紋理坐標(biāo)。
在編程中仙辟,頂點(diǎn)著色器的輸入主要有:
1.著色器程序 ShaderProgram---描述頂點(diǎn)上執(zhí)行操作的頂點(diǎn)著色器程序編程源代碼或者可執(zhí)行文件檀轨。
2.頂點(diǎn)著色器輸入屬性Attributes---用于頂點(diǎn)數(shù)組提供每個(gè)頂點(diǎn)的數(shù)據(jù)。
3.統(tǒng)一變量Uniforms---頂點(diǎn)著色器和片元著色器使用的常量數(shù)據(jù)欺嗤。
4.采樣器Samplers---代表頂點(diǎn)著色器使用的紋理的特殊統(tǒng)一變量類型参萄,被Uniforms使用的特殊類型,在頂點(diǎn)著色器的貼圖中使用煎饼,且是可選的讹挎。
相應(yīng)的,頂點(diǎn)著色器的輸出叫做varying變量吆玖。
在最初的光柵化階段筒溃,這些變量被計(jì)算,作為片段著色器的輸入沾乘,從頂點(diǎn)著色器的矩陣使用插補(bǔ)的方法產(chǎn)生片段著色器的變量怜奖,輸入和輸出如下圖所示:
gl_Position:浮點(diǎn)型輸出變量,用于輸出頂點(diǎn)位置的裁剪坐標(biāo)翅阵,該值在裁剪和視口階段用于執(zhí)行相應(yīng)的圖元裁剪以及從裁剪坐標(biāo)到屏幕坐標(biāo)的頂點(diǎn)位置變換歪玲,頂點(diǎn)著色器沒(méi)有寫入gl_Position時(shí)其值是未定義的。
glPointSize:浮點(diǎn)輸出變量掷匠,用于寫入以像素表示的點(diǎn)尺寸滥崩,在渲染點(diǎn)時(shí)使用,頂點(diǎn)著色器輸出的這個(gè)變量值被限定在OpenGL ES 3.0實(shí)現(xiàn)支持的非平滑點(diǎn)大小范圍之內(nèi)讹语。
頂點(diǎn)著色器的業(yè)務(wù):
1.矩陣變換位置
2.計(jì)算光照公式生成逐頂點(diǎn)顏色
3.生成或者變換紋理坐標(biāo)
下面來(lái)看一下頂點(diǎn)著色器的代碼片段:
attribute vec4 position; //四維向量
attribute vec2 textCoordinate; // 紋理坐標(biāo)
uniform mat4 rotateMatrix; //旋轉(zhuǎn)4X4矩陣
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
vec4 vPos = position;
vPos = vPos * rotateMatrix;
gl_Position = vPos;
}
頂點(diǎn)著色器中的內(nèi)建變量
頂點(diǎn)著色器的內(nèi)建變量可以分為:特殊變量(頂點(diǎn)著色器的輸入/輸出)钙皮,統(tǒng)一狀態(tài)(深度范圍)以及規(guī)定的最大值(屬性數(shù)量,頂點(diǎn)著色器變量數(shù)量和統(tǒng)一變量數(shù)量)的常量。
內(nèi)建特殊變量
gl_VertexID,是一個(gè)輸入變量短条,用于保存頂點(diǎn)的整數(shù)索引导匣,這個(gè)整數(shù)型變量,用highp精度限定符聲明茸时。
gl_InstanceID,是一個(gè)輸入變量逐抑,用于保存實(shí)例化繪圖調(diào)用中圖元的實(shí)例編號(hào),對(duì)于常規(guī)調(diào)用的繪圖調(diào)用屹蚊,該值為0; gl_InstanceID是一個(gè)整數(shù)型變量,用highp精度限定符聲明进每。
gl_Position, 用于輸出頂點(diǎn)位置的裁剪坐標(biāo)汹粤,該值在裁剪和視口變換用于執(zhí)行相應(yīng)的圖元裁剪,以及從裁剪坐標(biāo)到屏幕坐標(biāo)的頂點(diǎn)位置變換田晚。如果頂點(diǎn)著色器未寫入gl_Position嘱兼,則gl_Position的值未定義,gl_Position是浮點(diǎn)型變量贤徒,用highp精度限定符聲明芹壕。
gl_PointSize,可以寫入像素表示點(diǎn)精靈的尺寸,在渲染點(diǎn)精靈時(shí)使用接奈,頂點(diǎn)著色器輸出的gl_PointSize值被限定在OPenGL ES 3.0實(shí)現(xiàn)支持的非平滑點(diǎn)大小范圍之內(nèi)踢涌,gl_PointSize是一個(gè)浮點(diǎn)型變量,用highp精度限定符聲明序宦。
gl_FrontFacing,是一個(gè)特殊變量睁壁,但是不是由頂點(diǎn)著色器直接寫入的,而是根據(jù)頂點(diǎn)著色器生成的位置值和渲染圖元的類型生成的互捌,它是一個(gè)布爾值潘明。
內(nèi)建統(tǒng)一變量
在頂點(diǎn)著色器內(nèi)可用的唯一內(nèi)建Uniform狀態(tài)是窗口坐標(biāo)中的深度范圍,這些由內(nèi)建統(tǒng)一變量名gl_DepthRange給出秕噪。
struct gl_DepthRangeParameters
{
highp float near; //near z
highp float far; //near far
highp float diff; //far - near
}
uniform gl_DepthRangeParameters gl_DepthRange;
內(nèi)建常量
·
const mediump int gl_MaxVertexAttribs = 16;
const mediump int gl_MaxVertexUniformVectors = 256;
const mediump int gl_MaxVertexOutputVectors = 16;
const mediump int gl_MaxVertexTextureImageUnits = 16;
const mediump int gl_MaxCombinedTextureImageUnits = 32;
gl_MaxVertexAttribs: 可以指定的頂點(diǎn)數(shù)據(jù)最大的數(shù)量钳降。
gl_MaxVertexUniformVectors:頂點(diǎn)著色器可以使用的vect4 Uniform 變量最大數(shù)量。
gl_MaxVertexOutputVectors:表示輸出向量的最大數(shù)量腌巾。
gl_MaxVertexTextureImageUnits:頂點(diǎn)著色器可用紋理單元的最大數(shù)量遂填。
gl_MaxCombinedTextureImageUnits:頂點(diǎn)/片元著色器中的可用紋理單元的最大數(shù)量的總和。
頂點(diǎn)著色器中的矩陣變換
MVP矩陣(模型--視圖--投影矩陣)頂點(diǎn)著色器的位置輸入保存的是物體坐標(biāo)澈蝙,而輸出的坐標(biāo)保存為裁剪坐標(biāo)城菊,MVP矩陣是3D圖形中進(jìn)行這種變換的3個(gè)非常重要的變換矩陣的乘積:模型矩陣,視圖矩陣碉克,投影矩陣凌唬。
模型矩陣:---將物體坐標(biāo)變換為世界坐標(biāo)
視圖矩陣:---將世界坐標(biāo)變換為眼睛坐標(biāo)(觀察坐標(biāo))
投影矩陣:---將眼睛坐標(biāo)(觀察坐標(biāo))變換為裁剪坐標(biāo)
在頂點(diǎn)著色器之后進(jìn)入圖元裝配(Primitive Assembly)階段和光柵化(Rasterization)階段,此過(guò)程開發(fā)者無(wú)法干預(yù),(有關(guān)圖元裝配和光柵化的內(nèi)容請(qǐng)參見(jiàn):OpenGL 下的坐標(biāo)系和著色器渲染流程)
片元著色器Fragment Shader
片元著色器是用于處理片元值及相關(guān)數(shù)據(jù)的可編程單元客税,其可以執(zhí)行紋理的采樣况褪,顏色的匯總等操作。
片元著色器主要功能為通過(guò)重復(fù)執(zhí)行(每片元一次)將3D物體中的圖元光柵化后產(chǎn)生的每個(gè)片元的顏色等屬性計(jì)算出來(lái)送入后繼階段更耻;
片元著色器對(duì)光柵化階段產(chǎn)生的每個(gè)片元進(jìn)行操作测垛,需要的輸入數(shù)據(jù)如下:
1.著色器程序Shader program:片元著色器的源碼或可執(zhí)行文件,描述了將對(duì)片元執(zhí)行的操作秧均。
2.輸入變量Varying variables:頂點(diǎn)著色器輸出varying變量經(jīng)過(guò)光柵化插值計(jì)算后產(chǎn)生的作用于每個(gè)片元的值食侮。
3.統(tǒng)一變量Uniforms:片元著色器使用的常量數(shù)據(jù)
4.采樣器Samplers:一種特殊的uniforms,表示片元著色器使用的紋理目胡。
gl_FragColor值的是計(jì)算后此片元的顏色锯七。一般在片元著色器的最后都需要對(duì)gl_FragColor進(jìn)行賦值。
片元著色器的業(yè)務(wù):
1.計(jì)算顏色
2.獲取紋理值
3.往像素點(diǎn)中填充顏色值(顏色值或者紋理值)
下面來(lái)看一下片元著色器的代碼片段:
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap; //采樣器
void main()
{
//texture2D方法有兩個(gè)參數(shù):(紋理采樣器,紋理坐標(biāo)),取得紋素(紋理的像素值)俩块。
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
片元著色器中的內(nèi)建變量
內(nèi)建特殊變量
gl_FragCoord:片元著色器中的一個(gè)只讀變量裆操,這個(gè)變量保存片元的窗口相對(duì)坐標(biāo)。
gl_FrontFacing:片元著色器中的一個(gè)只讀變量,這個(gè)布爾變量是正面圖元時(shí)為true,否則為false。
gl_PointCoord:只讀變量袱蜡,可以在渲染點(diǎn)精靈的時(shí)候使用,保存了點(diǎn)精靈的紋理坐標(biāo)慢宗,這個(gè)坐標(biāo)在點(diǎn)精靈光柵化期間自動(dòng)生成戒劫,處于(0,1)區(qū)間婆廊。
gl_FragDepth:一個(gè)只寫輸出變量迅细,在片元著色器寫入時(shí),覆蓋片元的固定功能深度值淘邻,盡量減少手動(dòng)實(shí)現(xiàn)深度值寫入茵典,這個(gè)功能需要謹(jǐn)慎使用,因?yàn)樗赡芙迷S多GPU的深度優(yōu)化宾舅,例如:許多GPU都有“Early-z”的功能统阿,在執(zhí)行片元著色器之前進(jìn)行深度測(cè)試,使用“Early-z”的好處就是不能通過(guò)深度測(cè)試的片元就不會(huì)被著色(從而降低了著色器的調(diào)用次數(shù)筹我,提高了性能)但是使用gl_FragDepth,就必須禁用該功能扶平,因?yàn)镚PU在執(zhí)行著色器之前不知道深度值。
內(nèi)建常量
const mediump int gl_MaxFragmentInputVectors = 15;
const mediump int gl_MaxTextureImageUnits = 16;
const mediump int gl_MaxFragmentUniformVectors = 224;
const mediump int gl_MaxDrawBuffers = 4;
const mediump int gl_MinProgramTexelOffset = -8;
const mediump int gl_MaxProgramTexelOffset = 7;
gl_MaxFragmentInputVectors: 片元著色器輸入的最大數(shù)量蔬蕊。
gl_MaxFragmentUniformVectors:可用紋理圖像單元的最大數(shù)量
gl_MaxFragmentUniformVectors:片元著色器可用vec4 Uniform變量最大數(shù)量
gl_MaxDrawBuffers:多重渲染目標(biāo)最大支持?jǐn)?shù)量
多重紋理混合
//?片元著?色器器代碼
attribute vec2 v_texCoord;
uniform sampler2D s_baseMap;
uniform sampler2D s_SecondMap;
void main()
{
vec4 baseColor;
vec4 secondColor;
baseColor = texture(s_baseMap ,v_texCoord);
secondColor = texture(s_SecondMap ,v_texCoord);
gl_FragColor = baseColor * secondColor;
}
//客戶端代碼: 將各個(gè)紋理理對(duì)象綁定到紋理理單元0和1,為采樣器器設(shè)置數(shù) 值,將采集器器綁定到對(duì)應(yīng)的紋理理單元
glActiveTexutre(GL_TEXTURE0);
glBindTeture(GL_TEXTURE_2D ,baseMapTexId);
glUniformli(baseMapTexId,0);
glActiveTexutre(GL_TEXTURE1);
glBindTeture(GL_TEXTURE_2D ,secondMapTexId);
glUniformli(secondMapTexId,1);
逐片段操作Per-Fragment Operations
先來(lái)看一下逐片段操作的流程:像素歸屬測(cè)試:確定幀緩存區(qū)中位置[Xw,Yw]的像素目前是不是歸屬于OpenGL ES所有结澄。例如:如果一個(gè)顯示OpenGL ES幀緩存區(qū)View被另一個(gè)View所遮蔽,則窗口系統(tǒng)可以確定被遮蔽的像素不屬于OpenGL ES上下文,從而不全部顯示這些像素麻献。而像素歸屬測(cè)試是OpenGL ES的一部分们妥,它不由開發(fā)者人為控制而是由OpenGL ES內(nèi)部進(jìn)行。
裁剪測(cè)試:裁剪測(cè)試確定[Xw,Yw]是否位于作為OpenGL ES狀態(tài)的一部分裁剪矩形范圍內(nèi)勉吻,如果該片段位于裁剪區(qū)域之外监婶,則被拋棄。
深度測(cè)試:輸入片段的深度值進(jìn)行比較齿桃,確定片段是否拒絕測(cè)試惑惶。
混合:混合將新生成的片段顏色與保存在幀緩存區(qū)的位置的顏色值組合起來(lái)。(這里的混合與片元著色器的混合要區(qū)別開來(lái)短纵。)
抖動(dòng):抖動(dòng)可用于最小化因?yàn)槭褂糜邢蘧仍趲彺鎱^(qū)中保存顏色值而產(chǎn)生的偽像带污。
EGL(作為了解)
OpenGL ES 命令需要渲染上下文和繪制表面才能完成圖形圖像的繪制。
渲染上下文:存儲(chǔ)的是相關(guān)OpenGL ES 狀態(tài)
繪制表面: 是用于繪制圖元的表面,它指定渲染所需要的緩存區(qū)類型,例如顏?緩存區(qū),深度緩沖區(qū)和模板緩存區(qū).
OpenGL ES API 并沒(méi)有提供如何創(chuàng)建渲染上下文或者上下文如何連接到原生窗口系統(tǒng). EGL 是Khronos 渲染API(如OpenGL ES) 和原生窗口系統(tǒng)之間的接口.唯一?持 OpenGL ES 卻不支持EGL 的平臺(tái)是iOS. Apple 提供?自?的EGL API的iOS實(shí)現(xiàn),稱為EAGL.
因?yàn)槊總€(gè)窗口系統(tǒng)都有不同的定義,所以EGL提供基本的不透明類型—EGLDisplay, 這個(gè)類型封裝了所有系統(tǒng)相關(guān)性,?于和原生窗?系統(tǒng)接口.
由于OpenGL ES是基于C的API踩娘,因此它非常便攜且受到?泛支持。作為C API喉祭,它與
Objective-C Cocoa Touch應(yīng)?程序?縫集成养渴。OpenGL ES規(guī)范沒(méi)有定義窗口層; 相
反,托管操作系統(tǒng)必須提供函數(shù)來(lái)創(chuàng)建一個(gè)接受命令的OpenGL ES 渲染上下文和一個(gè)幀緩沖區(qū)泛烙,其中寫入任何繪圖命令的結(jié)果理卑。在iOS上使?用OpenGL ES需要使?iOS類來(lái)設(shè)置和呈現(xiàn)繪圖表面,并使?平臺(tái)中立的API來(lái)呈現(xiàn)其內(nèi)容蔽氨。
EGL的主要功能如下:
- 和本地窗?系統(tǒng)(native windowing system)通訊;
- 查詢可用的配置;
- 創(chuàng)建OpenGL ES可用的“繪圖表面”(drawing surface);
- 同步不同類別的API之間的渲染藐唠,?如在OpenGL ES和OpenVG之間同步,或者在
OpenGL和本地窗口的繪圖命令之間; - 管理“渲染資源”鹉究,?比如紋理映射(rendering map)宇立。