前言
opengl es通過頂點著色器程序和片段著色器程序來實現(xiàn)可編程的渲染,著色器程序也有自己的編程語言簡稱為GLSL,GLSL語言和C語言比較類似,接下來就是對GLSL語言特性的記錄。
GLSL(著色器編程語言)
- 1责掏、介紹
是一個和C語言語法比較類似的著色器變成語言 - 2咕晋、注釋
和C語言一樣
// this is a comment
/**
* this is muti comment
*/
備注:GLSL語言必須由ASCII碼字符組成涡拘,如果包括非ascii碼編譯會出錯
- 3、變量命名
GLSL的變量名必須以字母或者下劃線開頭亡蓉,由英文字母证芭,數(shù)字瞳浦,組成,且不能是gl或__開頭(都是系統(tǒng)預(yù)留的)废士,也不能是系統(tǒng)關(guān)鍵字 - 4叫潦、預(yù)處理指令
預(yù)處理指令以#開頭,#號之前不能有除了空白字符之外的任何字符官硝。每一個指令獨占一行矗蕊。內(nèi)置的預(yù)處理指令如下:
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
#error
#pragma
#extension
#version
#line
#pragma
// 編譯指示。用來控制編譯器的一些行為氢架,開發(fā)和調(diào)試時可以設(shè)置為off傻咖,默認設(shè)為on。
#pragma optimize(on)
#pragma optimize(off)
// 開發(fā)和調(diào)試時可以打開debug選項岖研,以便獲取更多的調(diào)試信息卿操。默認設(shè)為off。
#pragma debug(on)
#pragma debug(off)
#extension
// 如果想使用GLGL默認不支持的操作孙援,則必須啟用對應(yīng)的擴展害淤,啟用一個擴展可以使用下面的命令:
#extension : behavior
#extension all : behavior
其中,extension_name是擴展的名稱拓售,all是指所有的編譯器支持的擴展窥摄。
behavior是指對該擴展的具體操作。比如啟用邻辉、禁用等等溪王。詳情如下:
behavior 作用
require 啟用該擴展。如果不支持值骇,則報錯莹菱。
enable 啟用該擴展。如果不支持吱瘩,則會警告道伟。extension_name是all的時候會報錯。
warn 啟用該擴展使碾。但是會檢測到所有使用該擴展的地方蜜徽,提出警告。
disable 禁用該擴展票摇。如果該擴展不被支持拘鞋,則提出警告。
5矢门、預(yù)定義的變量
除此之外盆色,還預(yù)定義了一些變量:
_LINE_ :int類型,當(dāng)前的行號祟剔,也就是在Source String中是第一行
_FILE_ :int類型隔躲,當(dāng)前Source String的唯一ID標(biāo)識
_VERSION_ :int類型,GLGL的版本
GL_ES :對于嵌入式系統(tǒng)(Embed System物延,簡稱 ES)宣旱,它的值為1,否則為06叛薯、運算符及其優(yōu)先級
序號|運算符|描述|優(yōu)先級
--|:--:|--:|--:
| 1 | 小括號 |()| 從右往左 |
| 2 | 乘除求余 | * / % | 從左往右 |
| 3 | 加減法 | + - | 從左往右 |
| 4 | 位運算 移位 | 左移<< 和 右移 >> | 從左往右 |
| 5 | 大小關(guān)系 | < > <= >= | 從左往右 |
| 6 | 相等性判斷 | == != | 從左往右 |
| 7 | 位運算 與 | & | 從左往右 |
| 8 | 位運算 非 | ^ | 從左往右 |
| 9 | 位運算 或 | | | 從左往右 |
| 10 | 邏輯與 | && | 從左往右 |
| 11 | 邏輯或 | || | 從左往右 |7浑吟、關(guān)鍵詞
列舉一下GLSL中的關(guān)鍵詞,這些全部是系統(tǒng)保留的案训,不可私自篡改买置。
attribute const uniform varying
break continue do for while
if else
in out inout
float int void bool true false
lowp mediump highp precision invariant
discard return
mat2 mat3 mat4
vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4
sampler2D samplerCube
struct
asm
class union enum typedef template this packed
goto switch default
inline noinline volatile public static extern external interface flat
long short double half fixed unsigned superp
input output
hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4
sampler1D sampler3D
sampler1DShadow sampler2DShadow
sampler2DRect sampler3DRect sampler2DRectShadow
sizeof cast
namespace using
除此之外,所有的以__開頭的變量全部是預(yù)留的强霎,自定義的變量不能以__開頭忿项。
精度限定符
先看如下片段著色器的GLSL代碼
uniform sampler2D colorMap;
varying lowp vec2 varyingCoordPos;
void main() {
vec4 color = texture2D(colorMap,varyingCoordPos);
gl_FragColor = color;
}
這段代碼編譯時會提示"declaration must include a precision qualifier for type",意思就是聲明變量時沒有指定精度限定符
- 1城舞、精度范圍
對于單個變量使用精度限定符的語法為
存儲限定符 精度限定符 數(shù)據(jù)類型 變量名轩触,比如:
lowp float color;
varying mediump vec2 Coord;
highp mat4 m;
浮點型變量的精度范圍
highp (-2的62次方, 2的62次方);
mediump (-2的14次方, 2的14次方);
lowp (-2,2);
整型變量的精度范圍
highp (-2的16次方, 2的16次方);
mediump (-2的10次方, 2的10次方);
lowp (-2的8次方, 2的8次方);
字符型和布爾型是沒有精度范圍的
所以對于高精度和中精度,整型類變量(包括標(biāo)量和向量)可以準(zhǔn)確的的轉(zhuǎn)化為同樣精度修飾符修飾的浮點型類變量家夺,例如highp int 可以轉(zhuǎn)換為 highp float 脱柱,mediump int 可以轉(zhuǎn)換為 mediump float;但是lowp int 缺不可以轉(zhuǎn)化為 lowp float拉馋,因為會產(chǎn)生數(shù)據(jù)溢出榨为。
- 2惨好、默認精度
通過precision關(guān)鍵字來指定默認精度,這樣就不用每一個變量前面都聲明精度限定符了随闺,具體語法如下:
precision highp|mediump|lowp type;
那么從該語句之后所有的沒有精度限定符修飾的type數(shù)據(jù)類型的變量都將采用此默認精度日川,包括全局變量聲明、函數(shù)返回值聲明矩乐、函數(shù)參數(shù)聲明以及本地變量聲明等龄句,只要是沒有精度修飾符的變量將使用和它最近的precision語句中的精度。
例如:
precision highp float;
代表之后所有和float有關(guān)的沒有精度限定符修飾的變量的精度都為float
- 3散罕、預(yù)定義精度
GLSL為我們預(yù)先設(shè)定了一些數(shù)據(jù)類型的默認精度分歇,頂點著色器的預(yù)定義全局默認精度語句:
precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;
片元著色器預(yù)定義全局默認精度:
precision mediump int;
precision lowp sampler2D;
precision lowp samplerCube;
所以片元著色器對于浮點型變量是沒有設(shè)置預(yù)定義精度的,需要用前面的方式手動指定欧漱,這里也解釋了為什么前面的代碼編譯會出錯的原因职抡。
GLSL中矩陣和向量相關(guān)運算
引用GLSL官方文檔中的例子,這里以三維向量和三維矩陣為例硫椰,二維和四維類似
- 例子1
向量與單個變量相加繁调,將向量中的每個元素分別于這個變量相加,結(jié)果仍然為向量靶草,且不區(qū)分向量和單個變量的順序即:
vec3 v, u;
float f;
v = u + f;
等價于
v.x = u.x + f;
v.y = u.y + f;
v.z = u.z + f;
v = f + u;
等價于
v.x = f + u.x;
v.y = f + u.y;
v.z = f + u.z;
備注:向量與單個變量的乘法一樣蹄胰。減法和除法則可以換成等價的加法和乘法
- 例子2
兩個向量相加,分別將兩個向量對應(yīng)位置的元素相加奕翔,結(jié)果仍然為向量裕寨,且不區(qū)分向量和單個變量的順序即:
vec3 v, u, w;
w = v + u;
等價于
w.x = v.x + u.x;
w.y = v.y + u.y;
w.z = v.z + u.z;
乘法一樣
- 例子3
向量乘以矩陣,分別將向量與矩陣的每一列相乘派继,結(jié)果為行向量(這里向量只有一行)
vec3 v, u;
mat3 m;
u = v * m;
等價于
u.x = v.x * m[0].x + v.y * m[0].y + v.z * m[0].z;
u.y = v.x * m[1].x + v.y * m[1].y + v.z * m[1].z;
u.z = v.x * m[2].x + v.y * m[2].y + v.z * m[2].z;
- 例子4
矩陣乘以向量宾袜,分別矩陣的每一行與向量相乘,結(jié)果為列向量(這里向量只有一列)
vec3 v, u;
mat3 m;
u = m * v;
等價于
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
這里與例子3的區(qū)別就是它的最終結(jié)果變成了列向量
- 例子5
矩陣乘以矩陣驾窟,滿足高度代數(shù)中矩陣相乘的算法
mat m, n, r;
r = m * n;
等價于
r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
r[2].z = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;