每一個OpenGL ES 3.0程序要求一個頂點着色器和一個片斷着色器去渲染一個圖形。着色器概念是API 的中心,本篇將介紹着色器語言部分包含下面幾項編程
一、變量和變量類型
二、矢量和矩陣建立及選擇
三、常量
四、結構和陣列
五、運算符、流控制和函數
六、屬性、只讀變量和變量
七、預處理和指令
八、只讀變量和變量壓縮
九、精度控制和不變性數組
1、變量和變量類型編程語言
計算機圖形學中,轉換有兩種基本的數據類型:矢量和矩陣。下圖是OpenGL ES 着色器編程語言數據類型ide
變量能夠在聲明時初始化,或之後初始化,初始化是經過構造函數進行,也可作類型轉換。函數
float myFloat = 1.0; float myFloat2 = 1; // ERROR: invalid type conversion bool myBool = true; int myInt = 0; int myInt2 = 0.0; // ERROR: invalid type conversion myFloat = float(myBool); // Convert from bool -> float myFloat = float(myInt); // Convert from int -> float myBool = bool(myInt); // Convert from int -> bool
矢量一樣能夠轉換工具
vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, // 1.0} vec3 myVec3 = vec3(1.0,0.0,0.5); // myVec3 = {1.0, 0.0, 0.5} vec3 temp = vec3(myVec3); // temp = myVec3 vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x, // myVec3.y} myVec4 = vec4(myVec2, temp); // myVec4 = {myVec2.x, // myVec2.y, // temp.x, temp.y}
矩陣轉換優化
mat3 myMat3 = mat3(1.0, 0.0, 0.0, // First column 0.0, 1.0, 0.0, // Second column 0.0, 1.0, 1.0); // Third column
2、矢量和矩陣元素spa
矩陣元素可以經過兩種方式獲取,使用「.」操做符或者數組下標。依據被給的元素的組成,每一個被給的矩陣都能使用{x, y, z, w}, {r, g, b, a},或{s, t, r, q}表示。使用三種不一樣的命名錶是由於有三種座標頂點、顏色和貼圖。x, r, 或s 表示矩陣裏的第一個元素,不一樣的命名方式僅僅是爲了使用方便。或者說你可使用矩陣時混合使用矩陣命名方式,(但不能使用.xgr,只能一次使用一種命名規則)。當使用「.」時,你也能夠從新排列一個矩陣。例如,scala
vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0} vec3 temp; temp = myVec3.xyz; // temp = {0.0, 1.0, 2.0} temp = myVec3.xxx; // temp = {0.0, 0.0, 0.0} temp = myVec3.zyx; // temp = {2.0, 1.0, 0.0}
矩陣也可使用[]操做符,在這種下標模式[0]表明x, [1]表明y。矩陣被認爲是多個矢量組成的,例如mat2 被考慮是兩個vec2s,mat3 是3 個vec3s。對矩陣,單獨的列被使用列下標[]選中。下面是例子:code
mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0 (identity) vec4 colO = myMat4[0]; // Get colO vector out of the matrix float ml_l = myMat4[1][1]; // Get element at [1][1] in matrix float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix
3、常量
常量是在着色器中不可改變的數據類型。使用const修飾,必須在聲明時初始化。
const float zero = 0.0; const float pi = 3.14159; const vec4 red = vec4(1.0, 0.0, 0.0, 1.0); const mat4 identity = mat4(1.0);
4、結構和數組
結構
像C 語言同樣,能夠集合幾種變量成爲結構。在OpenGL ES 結構以下:
struct fogStruct { vec4 color; float start; float end; } fogVar;
這將產生新的變量類型fogStruct,和新的變量名fogVar。使用可以初始化構造函數變量。定義一個結構類型,定義一個結構,一個同名的構造函數也被定義,必須是一對一的。上面的結構可以被使用下面的語法初始化:
fogVar = fogStruct(vec4(0.0, 1.0, 0.0, 0.0), // color 0.5, // start 2.0); // end
結構的構造基於結構的類型,它把每一個成員作輸入參數。訪問結構中的元素和C 語言相同。
vec4 color = fogVar.color; float start = fogVar.start; float end = fogVar.end;
數組
OpenGL ES 數組語法和C 語言很是相似,索引以0 開始。下面是建立數組的例子:
float floatArray[4]; vec4 vecArray[2];
float a[4] = float[](1.0, 2.0, 3.0, 4.0);
float b[4] = float[4](1.0, 2.0, 3.0, 4.0);
vec2 c[2] = vec2[2](vec2(1.0), vec2(1.0));
5、操做符
這些運算符使用和C 語言很是相似。但OpenGL ES 語法有嚴格的語法限制,執行運算符的變量必須有相同的類型,二進制運算符(*, /, +, -)必須是浮點變量或者是整型變量。乘運算符可以在浮點、矢量、矩陣的組合中運行。例如:
float myFloat; vec4 myVec4; mat4 myMat4; myVec4 = myVec4 * myFloat; // Multiplies each component of myVec4 // by a scalar myFloat myVec4 = myVec4 * myVec4; // Multiplies each component of myVec4 // together (e.g., myVec4 ^ 2 ) myVec4 = myMat4 * myVec4; // Does a matrix * vector multiply of // myMat4 * myVec4 myMat4 = myMat4 * myMat4; // Does a matrix * matrix multiply of // myMat4 * myMat4 myMat4 = myMat4 * myFloat; // Multiplies each matrix component by // the scalar myFloat
比較運算符(==, !=, <, etc.)僅可以執行標量,矢量有專門的比較函數
6、函數
函數聲明和C 語言同樣,函數使用前,必須定義,它的原型必須給出。使用時很是相似C 語言。不一樣是參數使用上,提供特殊的變量限定詞,指示變量是否可以被函數修改。那些限定詞以下表:
使用以下:
vec4 myFunc(inout float myFloat, // inout parameter out vec4 myVec4, // out parameter mat4 myMat4); // in parameter (default)
散射光計算函數。
vec4 diffuse(vec3 normal, vec3 light, vec4 baseColor) { return baseColor * dot(normal, light); }
OpenGL ES 函數不能遞歸,緣由是一些編譯工具執行這個函數時,這會讓這個函數在線執行,最後使GPU 產生問題。
着色器語言也提供了內置函數。下面的例子是,在片斷着色器中計算基本反射光的着色器代碼。
float nDotL = dot(normal , light); float rDotV = dot(viewDir, (2.0 * normal) * nDotL C light); float specular = specularColor * pow(rDotV, specularPower);
7、流控制聲明
控制語句語法和C 相似,if-then-else 邏輯也被使用,例如:
if(color.a < 0.25) { color *= color.a; } else { color = vec4(0.0); }
條件表達式的結果必須是布爾值。或者說表達式基於布爾值計算,或者計算結果是布爾值(例如比較運算)。
8、Uniforms
Uniform
是變量類型的一種修飾符。。uniform 是OpenGL ES 中被輸入着色器的只讀值。。uniform被使用存儲各類着色器須要的數據,例如:轉換矩陣、光照參數或者顏色。基本上各類輸入着色器的常量參數像頂點和片斷(但在編譯時並不知道)應該是uniform。uniform 應該使用修飾詞被聲明爲全局變量,以下:
uniform mat4 viewProjMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;
uniform 的空間被頂點着色器和片斷着色器分享。也就是說頂點着色器和片斷着色器被連接到一塊兒進入項目,它們分享一樣的uniform。所以一個在頂點着色器中聲明的uniform,至關於在片斷着色器中也聲明過了。當應用程序裝載uniform 時,它的值在頂點着色器和片斷着色器均可用。
另外一個須要注意的是,uniform 被存儲在硬件被稱爲常量存儲,這是一種分配在硬件上的存儲常量值的空間。由於這種存儲須要的空間是固定的,在程序中這種uniform 的數量是受限的。這個限制能經過讀gl_MaxVertexUniformVectors 和gl_MaxFragmentUniformVectors編譯變量得出。( 或者用GL_MAX_VERTEX_UNIFORM_VECTORS 或GL_MAX_FRAGMENT_UNIFORM_ VECTORS 爲參數調用glGetIntegerv)OpenGL ES 3.0必須至少提供256 個頂點着色器uniform 和224個片斷着色器uniform。但能夠更多,
Uniform Blocks
uniform TransformBlock { mat4 matViewProj; mat3 matNormal; mat3 matTexGen; }; layout(location = 0) in vec4 a_position; void main() { gl_Position = matViewProj * a_position; }
9、屬性
OpenGL ES 着色器語言的另外一個變量是屬性。屬性變量僅僅在頂點着色器中被使用,逐頂點的指定頂點着色器的輸入。典型的被用來儲存位置、法線、貼圖座標和顏色數據。關鍵是懂得屬性是每一個頂點被繪製的詳細數據。它其實是着色器的使用者決定什麼數據是屬性。
uniform mat4 u_matViewProjection; attribute vec4 a_position; attribute vec2 a_texCoord0; varying vec2 v_texCoord; void main(void) { gl_Position = u_matViewProjection * a_position; v_texCoord = a_texCoord0; }
像uniform 同樣,硬件對頂點着色器的屬性變量數量有限制。最大的屬性數經過工具編譯支持gl_MaxVertexAttribs 肯定。(或者使用GL_MAX_VERTEX_ATTRIBS 爲參數調用glGetIntegerv 查詢)最小數是8。若是爲確保你的程序能在任何OpenGL ES 2.0 工具上編譯,確保你的屬性不超過8。
變量被用來存儲頂點着色器的輸出和片斷着色器的輸入。基本上每一個頂點着色器把輸出數據轉變成一個或更多片斷着色器的輸入。那些變量也被片斷着色器聲明(類型需匹配),而且在光柵化階段被線性插補變成圖元。
varying vec2 texCoord;
varying vec4 color;
變量也有數量的限制(硬件上它們被用來作插補)。工具支持的最大數目是gl_MaxVaryingVectors ( 使用GL_MAX_VARYING_VECTORS 爲參數調用glGetIntegerv 查詢)。最大數目是8.
下面是頂點着色器和片斷着色器的變量如何匹配的聲明
// Vertex shader uniform mat4 u_matViewProjection; attribute vec4 a_position; attribute vec2 a_texCoord0; varying vec2 v_texCoord; // Varying in vertex shader void main(void) { gl_Position = u_matViewProjection * a_position; v_texCoord = a_texCoord0; }
// Fragment shader precision mediump float; varying vec2 v_texCoord; // Varying in fragment shader uniform sampler2D s_baseMap; uniform sampler2D s_lightMap; void main() { vec4 baseColor; vec4 lightColor; baseColor = texture2D(s_baseMap, v_texCoord); lightColor = texture2D(s_lightMap, v_texCoord); gl_FragColor = baseColor * (lightColor + 0.25); }
10、預處理指令
#define #undef #if #ifdef #ifndef #else #elif #endif
__LINE__ // 被着色器當前的行數取代
__FILE__ // 在 OpenGL ES 中老是0
__VERSION__ // OpenGL ES 着色器語言版本 (e.g., 100)
GL_ES // 被 ES 着色器定義爲值1
其餘
精度控制符,lowp,highp,mediump.對全部基於浮點的變量默認的精度是浮點值,基於整型的變量默認的精度是整型。在着色器中, 若是沒有定義指定精度,默認對int 和float 的精度都是高。換句話說在頂點着色器中沒有指定進度控制的變量將是高質量。而片斷着色器規則卻不一樣。對浮點值沒有默認的精度控制。每一個着色器必須聲明默認的着色器浮點精度或指定每一個浮點變量的精度。即OpenGL ES 2.0 不要求片斷着色器支持高精度。決定是否高精度被支持是片斷着色器是否認義了GL_FRAGMENT_PRECISION_HIGH 宏(和工具輸出OES_fragment_precision_high 擴展字符串)。
#ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif
最後要討論的題目是不變性。在OpenGL ES 着色器編程語言裏,invariant 是被用於任何頂點着色器的變量輸出的關鍵字。它意味着什麼和爲何須要他呢。着色器被編譯時可能進行優化,一些指令被從新整理。指令從新整理意味着兩個着色器之間平等的計算不保證產生相同的結果。這是個問題在多通道着色器特殊狀況下,依稀物體使用透明混合來繪製時。若是精度被使用來計算輸出位置是不許確同樣,精度的不一樣將致使artifacts。這在Z fighting狀況下常常發生。每一個像素很小的Z 精度不一樣引發了不一樣的反光。下面的例子顯示了進行多通道渲染時invariance 對獲得正確的結果是很是重要的。下面的圓環被繪製用兩種方式。片斷着色器先計算鏡面反射光,再計算環境光和散射光。頂點着色器不使用invariance,所以小的精度不一樣引發了Z 光在圖
invariant gl_Position;
invariant varying texCoord;
一旦輸出被宣佈爲invariance,一樣的計算輸入相同,編譯器保證輸出結果相同。例如你有兩個頂點着色器使用多通道圖像投射矩陣依據輸入計算輸出。你能保證那些位置是invariance。
uniform mat4 u_viewProjMatrix; attribute vec4 a_vertex; invariant gl_Position; void main { // gl_Position = u_viewProjMatrix * a_vertex; // Will be the same // value in all // shaders with the // same viewProjMatrix // and vertex }