OpenGL ES 著色器
OpenGL ES 著色器
1.著色器語言
著色器語言是一種高階圖形程式語言,和C/C++語言很類似,但存在很大差別,比如,不支援double,byte
,short,不支援unin,enum,unsigned以及位運算等,但其加入了很多原生的資料型別,如向量,矩陣等。
資料型別可分為標量、向量、矩陣、取樣器、結構體、陣列等
Variable Class | Types | Description |
---|---|---|
Scalars | float, int, bool | 標量資料型別浮點數、整形數、布林值 |
Floating-point Vectors | float, vec2, vec3, vec4 | 浮點型向量,1、2、3、4 維 |
Integer vector | int, ivec2, ivec3, ivec4 | 整形向量,1、2、3、4 維 |
Boolean vector | bool,bvec2,bvec3,bvec4 | 布林向量,1、2、3、4維 |
Matrices | mat2, mat3, mat4 | 浮點型別矩陣 2×2,3×3,4×4 |
向量
向量傳遞引數,如果只提供一個標量,這個值用於設定所有向量的值;如果輸入是多個標量或者是向量,從左到右設定向量變數的引數,如果多個向量作為引數,那麼至少要有和變數一樣多的分量
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}
矩陣
矩陣操作在OpenGL ES中的使用非常廣泛,涉及到圖形的平移縮放旋轉等操作都是由矩陣來實現的.
向矩陣傳遞引數:
- 提供的是一個標量,那麼標量複製給與矩陣的主對角線
- 一個矩陣能被多個向量賦值,如,mat2可以用兩個vec2賦值
- 一個的矩陣被多個標量賦值,按列賦值
向量和矩陣的分量
向量一般用來儲存位置、顏色紋理座標等包含不止一個的量,訪問向量中某個分量的方法為:<向量名.分量名>
- 將向量看做顏色對待,四個分量為r、g、b、a,分別代表紅、綠、藍、透明度
- 將向量看做位置對待,四個分量為x、y、z、w,分別代表x軸、y軸、z軸、w
- 將向量看做紋理座標對待,四個分量為s、t、p、q,分別代表紋理座標的不同分量
這三種不同的命名方案不能混合使用,除此之外還可以將向量當做陣列看待,用下表來訪問。
vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0}
float x = myVec3.x;
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}
vec4 temp2 = myVec3.xxyz; // temp2 = {0.0, 0.0, 1.0, 2.0}
對矩陣的訪問當成一個二維陣列即可,矩陣可以認為是由多個向量組成的
mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0 (identity)
vec4 col0 = myMat4[0]; // Get col0 vector out of the matrix
float m1_1 = myMat4[1][1]; // Get element at [1][1] in matrix
float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix
取樣器
取樣器專門用於進行紋理取樣的相關操作,一般情況下一個取樣器變數代表了衣服紋理切貼圖。
sampler2D/sampler3D/samplerCube
取樣器變數不是在著色器中初始化的,一般是由主程式傳遞進來的。
陣列
宣告陣列時指定陣列大小,反之,訪問陣列時的下表必須是編譯時常量,這樣的話,編譯器會自動建立適當大小的陣列
型別轉換
著色器語言沒有自動提升的功能,也不能強制轉換,只能用構造器完成型別轉換,每中內建變數型別都有一組相關的構造器。
float f = 1; // error
int i = 0.0; // error
float f = 1.0 // ok
bool b = bool(f) // ok,非0當做true
float f = float(b) // ok,bool轉為浮點數,true轉為1.0,false轉為0.0
int i = 0; //ok
bool b = bool(i) // ok,int轉為bool
變數限定符
const:常量,編譯時常量,其值不可變,可以提高執行效率
attribute:屬性變數,僅僅用在頂點著色器,用該限定符修飾的變數用來接受從宿主程式傳進渲染管線的變數。一般用於每個頂點都不相同的量,比如頂點位置,顏色,法線等
uniform:統一變數,一般用於對同一組頂點組成的一個物體所有頂點都相同的量,比如光源位置,轉換矩陣,顏色,光照等
varying:變數被用來儲存頂點著色器的輸出和片元著色器的輸入,每個頂點著色器把輸出資料轉變成一個或更多片元著色器的輸入,在光柵化階段就會插值生成一系列變數
varying變數的原理
線上段上進行混合插值
在三角形上進行混合插值
獲取著色器變數
獲取attribute型別變數。對於attribute限定符修飾的變數的值是由宿主程式傳入渲染管線的,使用glGetAttribLocation函式獲得著色器中某屬性變數的引用
public static native int glGetAttribLocation(
int program, // 建立的程式物件
String name // 著色器中變數名
);
然後使用glVertexAttribPointer函式將資料傳遞到glGetAttribLocation返回的著色器變數引用所代表的變數中去
public static void glVertexAttribPointer(
int indx, // 屬性變數的引用
int size, //每個頂點的資料個數,比如x、y、z就是3
int type, // 資料型別,如GLES20.GL_FLOAT
boolean normalized, // 是否規格化,只有使用整形資料才有意義
int stride, // 跨距,一個數組儲存多個屬性才有意義,指的是兩個點之間有多少個位元組
java.nio.Buffer ptr // 存放頂點資料緩衝
)
獲取uniform型別的變數。使用glGetUniformLocation函式獲得著色器中某統一變數的引用
public static native int glGetUniformLocation(
int program,
String name
);
然後使用glUniformXXX函式將資料傳遞到著色器中,比如glUniformMatrix4fv函式
public static native void glUniformMatrix4fv(
int location, // 統一變數的引用
int count, // 指明要更改的元素個數。如果變數不是陣列,這個值應該設為1
boolean transpose, // 是否要轉置矩陣,並將它作為uniform變數的值。必須為false
float[] value, // 傳遞給統一變數的陣列元素
int offset // 偏移,取0
);
glUniformNf/glUniformNfv:將N個浮點數傳入管線
glUniformNi/glUniformNiv:將N個整數傳入管線
glUniformMatrixNfv:將N個整數傳入管線,將N*N矩陣傳入管線
內建變數
內建變數不需要宣告即可使用,內建變數分為兩種,輸入與輸出變數。
輸入變數負責將渲染管線中固定功能部分生成的資訊傳遞進著色器以供程式設計師使用,輸出變數負責將著色器產生的資訊傳遞給渲染管線中的固定功能。
頂點著色器
頂點著色器的內建變數主要是輸出變數,即將著色器產生的值傳遞給渲染管線,因此在頂點著色器中要對這些內建變數賦值,包括gl_Position、gl_PointSize等。
- gl_Position:在頂點著色器對獲取到的定點原始資料進行平移縮放旋轉等變換後,生成新的位置,新的頂點位置通過該變數傳遞給渲染管線的後續操作。
- gl_PointSize:頂點著色器中可以計算一個點的大小,單位為畫素,預設值為1,一般對點繪製方式有意義。
片元著色器
片元著色器中的內建輸入變數,gl_FragCoord、gl_FrontFacing,並且還是隻讀的,是由渲染管線片元著色器之前階段生成的。
- gl_FragCoord:vec4型別資料,含有當前片元相對視窗位置的座標。
- gl_FrontFacing:bool型別的內建輸入變數,該值表明當前正在處理的片元是否屬於在光柵化階段生成此片元對應圖元的正面。點、線段沒有正反面之分的圖元。其生成的偏遠都會被預設為是正面,三角形圖元其正面取決於程式中隊卷繞的設定及圖元中頂點的具體卷繞情況。
片元著色器中的內建輸出變數gl_FragColor、gl_FragData,在片元著色器中給這兩個內建變數寫入值。
- gl_FragColo:vec4變數,用來傳入由片元著色器計算出來的片元顏色值。
函式
和其他語言一樣,差別在於引數可以指定用途,具體的有in,out,inout修飾符表明該引數是入參還是出參。
片元著色器浮點變數精度
片元著色器中的浮點型別資料必須制定精度,不指定精度可能引起編譯錯誤。有三種精度型別:lowp、mediump、highp,一般使用mediump型別即可。如果在開發中同一個片元著色器中浮點型別變涼都是同一種精度型別,可以整個指定著色器中浮點型別預設精度。
precision <精度> <型別>
precision mediump float;
2.著色器程式
需要建立兩個物件才能用著色器進行渲染:著色器物件和程式物件。
著色器原始碼被編譯成一個目標形式(類似obj檔案),編譯之後,著色器物件可以連線到一個程式物件,程式物件可以連線多個著色器物件。
獲得連線後的著色器物件的過程:
- 建立一個頂點著色器和一個片元著色器:
- 將原始碼連線到每個著色器物件
- 編譯著色器物件
- 建立一個程式物件
- 將編譯後的著色器物件連線到程式物件
- 連線程式物件
如果沒有出錯,就可以在後面使用這個程式了,如從程式獲取某個著色器變數,接下來為其傳遞值等操作。
- 建立著色器物件
public static native int glCreateShader(
int type // 著色器型別,GLES20.GL_VERTEX_SHADER或GLES20.GL_FRAGMENT_SHADER
);
- 連線原始碼到著色器物件
public static native void glShaderSource(
int shader,
String string // 著色器原始碼
);
- 編譯著色器物件
public static native void glCompileShader(
int shader
);
- 建立程式物件
mProgram = GLES20.glCreateProgram();
- 將編譯後的著色器物件連線到程式物件
public static native void glAttachShader(
int program,
int shader
);
- 連線程式物件
public static native void glLinkProgram(
int program
);