掌握這些概念,不用擔心看不懂OpenGL ES着色語言了

OpenGL ES圖形學中,着色語言(Shading Language)是一門必修課。在看openGL代碼時,都會有着色語言的身影,它有自己的一套語法和格式。提供給我們進行編程的包括頂點着色器和片元着色器,它們都遵循着色語言語法,核心概念包括:向量、矩陣、變量、常量、運算符、採樣器、佈局限定符、精度限定符、插值限定符、輸入輸出限定符等。

在頂點着色器中,我們一般看到attribute修飾符,稱爲屬性變量;還有varying修飾符,稱爲易變變量,連接頂點着色器與片元着色器的橋樑。在片元着色器中,我們會看到uniform修飾符,稱爲統一變量。接下來,我們詳細探討下着色語言的相關概念。

1、向量

向量vec,分爲浮點向量、整數向量、無符號向量、布爾向量四種,默認爲浮點向量。向量又分爲二維向量vec2、三維向量vec3、四維向量vec4。向量的單獨分量可以用兩種方式訪問:使用“.”運算符或者通過數組下標。根據組成向量的分量數量,每個分量可以通過使用向量座標{x, y, z, w}、顏色分量{r, g, b, a}、紋理座標{s, t, p, q}三種方式訪問。如下表格是常見的向量類型:

浮點向量 vec2 vec3 vec4
整數向量 ivec2 ivec3 ivec4
無符號向量 uvec2 uvec3 uvec4
布爾向量 bvec2 bvec3 bvec4

下面是幾個例子,分別通過向量座標、顏色分量、紋理座標進行向量的訪問:

vec4 mVec = vec(1.0, 1.0, 1.0, 1.0);
vec4 cVec = mVec.rgba;
vec4 tVec = mVec.stpq;
vec4 nVec = mVec.xyzw;

2、矩陣

矩陣mat,和向量的維度類似,分爲二維矩陣mat2、三維矩陣mat3、四維矩陣mat4。不同的是,矩陣有多行多列,用m*n表示,而向量只有一列或者一行。矩陣可以進行平移、旋轉、縮放等操作。

3、變量

上面提及的向量和矩陣一般聲明爲變量,而且變量前面加上attribute、varying、uniform等修飾符。其中attribute是屬性變量,varying是易變變量,uniform是統一變量。openGL提供對應的API去加載attribute變量和uniform變量:

glGetAttribLocation(int program, String name);//加載attribute變量
glGetUniformLocation(int program, String name);//加載uniform變量

4、常量

在openGL定義常量需要加上const修飾符,必須在聲明時進行初始化。例如:

const float pi = 3.1415926;

5、運算符

openGL着色語言的運算符與C語言一樣。但是,着色語言對運算符有着嚴格的類型規則。運算符只能出現在有相同基本類型的變量之間,對於二元運算符加減乘除,變量的類型必須是float或者int。除了==和!=之外,比較運算符(<, <=, >, >=)只能用於標量。下面列舉着色語言常用的運算符:

openGL ES着色語言運算符
* ++,-- 遞增/遞減
/ >,==,< 比較運算符
+ && 邏輯與
- || 邏輯或
= 賦值 <<,>> 移位
+=,-=,/=,*= 算術賦值 &,^,| 按位運算

6、採樣器

採樣器一般分爲二維sampler2D和三維sampler3D,用於片元着色器中:

uniform sampler2D uTexture;
void main() {
    gl_FragColor = texture2D(uTexture, fCoord);
}

7、精度限定符

精度限定符是用來指定着色器變量的計算精度。它可以用於指定任何基於float或int變量的精度。精度分爲三種:低精度、中等精度和高精度,分別使用lowp、mediump、highp修飾符,並且前面要加上precision。示例代碼如下:

precision mediump float;//中等精度
precision highp float;//高精度
precision lowp int;//低精度

8、插值限定符

在沒有限定符時,openGL默認的插值 行爲是執行平滑着色。也就是說,來自頂點着色器的輸出變量在圖元中線性插值,片元着色器接收線性插值後的數值作爲輸入。我們也可以明確地請求平滑着色,如下所示:

//Vertex shader output
smooth out vec3 v_color;

//Input form vertex shader
smooth in vec3 v_color;

openGL ES 3.0還引入另一種插值——平面着色。在平面着色中,圖元中的值沒有進行插值,而是將其中一個頂點視爲驅動頂點,該頂點的值被用於圖元的所有片段。平面着色輸出/輸入如下:

//Vertex shader output
flat out vec3 v_color;

//Input form vertex shader
flat in vec3 v_color;

9、輸入輸出限定符

輸入輸出限定符是用於限定函數是否可以修改可變參數,類似Android的AIDL輸入輸出修飾符。限定符分爲三種:in、out、inout。如下表格所示:

openGL ES着色語言輸入輸出限定符
限定符 描述
in 指定參數按值傳遞,函數不能修改
out 該變量不被傳入函數,但在函數返回時被修改
inout 該變量按引用傳入,可以被函數修改

10、擴展庫

openGL着色語言允許使用擴展庫,使用extension關鍵字進行聲明。比如,使用SurfaceTexture進行渲染需要用到GL_OES_EGL_image_external庫,示例代碼如下:

#extension GL_OES_EGL_image_external : require

//using externalOES sampler
uniform samplerExternalOES vTexture;

11、不變性

openGL ES着色語言引入invariant關鍵字修飾頂點着色器變量,也就是不變性。與java的volatile關鍵字類似,禁止編譯器指令重排序。因爲編譯器在編譯期間可能對指令進行重排序優化,導致指令執行順序發生改變。如果用於計算輸出位置的數值精度不一樣,精度差異會導致僞像,表現爲“深度衝突”(Z fighting),每個像素的Z(深度)精度差異導致多遍着色互相之間的微小偏移。

結合上面概念,我們就容易編寫出頂點着色器和片元着色器了。示例代碼如下:

//Vertex shader
attribute vec4 vPosition;
attribute vec2 vCoord;
varying   vec2 fCoord;

void main() {
    gl_Position = vPosition;
    fCoord = vCoord;
}


//Fragment shader
precision mediump float;

varying vec2 fCoord;
uniform sampler2D vTexture;

void main() {
    gl_FragColor = texture2D(vTexture, fCoord);
}

參考書本:<OpenGL ES 3.0編程指南>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章