OpenGL中shader使用
引自:http://blog.csdn.net/wl_soft50/article/details/7916720
http://blog.sina.com.cn/s/blog_923fdd9b0102vbe0.html
?與OpenGL ES1.x渲染管線相比,OpenGL ES 2.0渲染管線中“頂點(diǎn)著色器”取代了OpenGL ES 1.x渲染管線中的“變換和光照”;“片元著色器”取代了OpenGL ES 1.x渲染管線中的“紋理環(huán)境和顏色求和”、“霧”以及“Alpha測(cè)試”。?這使得開(kāi)發(fā)人員在使用OpenGL ES 2.0API進(jìn)行開(kāi)發(fā)時(shí),可以通過(guò)編寫(xiě)頂點(diǎn)及片元著色器程序,來(lái)完成一些頂點(diǎn)變換和紋理顏色計(jì)算工作,實(shí)現(xiàn)更加靈活、精細(xì)化的計(jì)算與渲染。
一、著色(Shader)語(yǔ)言
著色語(yǔ)言是一種類(lèi)C的編程語(yǔ)言,但不像C語(yǔ)言一樣支持雙精度浮點(diǎn)型(double)、字節(jié)型(byte)、短整型(short)、長(zhǎng)整型(long),并且取消了C中的聯(lián)合體(union)、枚舉類(lèi)型(enum)、無(wú)符號(hào)數(shù)(unsigned)以及位運(yùn)算等特性。?著色語(yǔ)言中有許多內(nèi)建的原生數(shù)據(jù)類(lèi)型以及構(gòu)建數(shù)據(jù)類(lèi)型,如:浮點(diǎn)型(float)、布爾型(bool)、整型(int)、矩陣型(matrix)以及向量型(vec2、vec3等)等。總體來(lái)說(shuō),這些數(shù)據(jù)類(lèi)型可以分為標(biāo)量、向量、矩陣、采樣器、結(jié)構(gòu)體以及數(shù)組等。?shader支持下面數(shù)據(jù)類(lèi)型:
float bool int 基本數(shù)據(jù)類(lèi)型 vec2 包含了2個(gè)浮點(diǎn)數(shù)的向量 vec3 包含了3個(gè)浮點(diǎn)數(shù)的向量 vec4 包含了4個(gè)浮點(diǎn)數(shù)的向量 ivec2 包含了2個(gè)整數(shù)的向量 ivec3 包含了3個(gè)整數(shù)的向量 ivec4 包含了4個(gè)整數(shù)的向量 bvec2 包含了2個(gè)布爾數(shù)的向量 bvec3 包含了3個(gè)布爾數(shù)的向量 bvec4 包含了4個(gè)布爾數(shù)的向量 mat2 2*2維矩陣 mat3 3*3維矩陣 mat4 4*4維矩陣 sampler1D 1D紋理采樣器 sampler2D 2D紋理采樣器 sampler3D 3D紋理采樣器1. 頂點(diǎn)著色器
1.1 頂點(diǎn)著色器示例代碼
uniform mat4 uMVPMatrix; // 應(yīng)用程序傳入頂點(diǎn)著色器的總變換矩陣 attribute vec4 aPosition; // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)位置 attribute vec2 aTextureCoord; // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)紋理坐標(biāo) attribute vec4 aColor // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)顏色變量 varying vec4 vColor // 用于傳遞給片元著色器的頂點(diǎn)顏色數(shù)據(jù) varying vec2 vTextureCoord; // 用于傳遞給片元著色器的頂點(diǎn)紋理數(shù)據(jù) void main() {gl_Position = uMVPMatrix * aPosition; // 根據(jù)總變換矩陣計(jì)算此次繪制此頂點(diǎn)位置vColor = aColor; // 將頂點(diǎn)顏色數(shù)據(jù)傳入片元著色器vTextureCoord = aTextureCoord; // 將接收的紋理坐標(biāo)傳遞給片元著色器 }1.2 頂點(diǎn)著色器介紹
頂點(diǎn)著色器是一個(gè)可編程的處理單元,執(zhí)行頂點(diǎn)變換、紋理坐標(biāo)變換、光照、材質(zhì)等頂點(diǎn)的相關(guān)操作,每頂點(diǎn)執(zhí)行一次。替代了傳統(tǒng)渲染管線中頂點(diǎn)變換、光照以及紋理坐標(biāo)的處理,開(kāi)發(fā)人員可以根據(jù)自己的需求自行開(kāi)發(fā),大大增加了程序的靈活性。
頂點(diǎn)著色器主要是傳入相應(yīng)的Attribute變量、Uniforms變量、采樣器以及臨時(shí)變量,經(jīng)過(guò)頂點(diǎn)著色器后生成Varying變量。如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(1)attribute變量(屬性變量)只能用于頂點(diǎn)著色器中,不能用于片元著色器。?一般用該變量來(lái)表示一些頂點(diǎn)數(shù)據(jù),如:頂點(diǎn)坐標(biāo)、紋理坐標(biāo)、顏色等。
(2)uniforms變量(一致變量)用來(lái)將數(shù)據(jù)值從應(yīng)用程其序傳遞到頂點(diǎn)著色器或者片元著色器。?該變量有點(diǎn)類(lèi)似C語(yǔ)言中的常量(const),即該變量的值不能被shader程序修改。一般用該變量表示變換矩陣、光照參數(shù)、紋理采樣器等。
(3)varying變量(易變變量)是從頂點(diǎn)著色器傳遞到片元著色器的數(shù)據(jù)變量。頂點(diǎn)著色器可以使用易變變量來(lái)傳遞需要插值的顏色、法向量、紋理坐標(biāo)等任意值。?在頂點(diǎn)與片元shader程序間傳遞數(shù)據(jù)是很容易的,一般在頂點(diǎn)shader中修改varying變量值,然后片元shader中使用該值,當(dāng)然,該變量在頂點(diǎn)及片元這兩段shader程序中聲明必須是一致的?。例如:上面代碼中應(yīng)用程序中由頂點(diǎn)著色器傳入片元著色器中的vColor變量。
(4)gl_Position?為內(nèi)建變量,表示變換后點(diǎn)的空間位置。?頂點(diǎn)著色器從應(yīng)用程序中獲得原始的頂點(diǎn)位置數(shù)據(jù),這些原始的頂點(diǎn)數(shù)據(jù)在頂點(diǎn)著色器中經(jīng)過(guò)平移、旋轉(zhuǎn)、縮放等數(shù)學(xué)變換后,生成新的頂點(diǎn)位置。新的頂點(diǎn)位置通過(guò)在頂點(diǎn)著色器中寫(xiě)入gl_Position傳遞到渲染管線的后繼階段繼續(xù)處理。
2. 片元著色器
2.1 片元著色器示例代碼
precision mediump float; // 設(shè)置工作精度 varying vec4 vColor; // 接收從頂點(diǎn)著色器過(guò)來(lái)的頂點(diǎn)顏色數(shù)據(jù) varying vec2 vTextureCoord; // 接收從頂點(diǎn)著色器過(guò)來(lái)的紋理坐標(biāo) uniform sampler2D sTexture; // 紋理采樣器,代表一幅紋理 void main() { gl_FragColor = texture2D(sTexture, vTextureCoord) * vColor;// 進(jìn)行紋理采樣 }???????此片元著色器的主要功能為根據(jù)接收的記錄片元紋理坐標(biāo)的易變變量中的紋理坐標(biāo),調(diào)用texture2D內(nèi)建函數(shù)從采樣器中進(jìn)行紋理采樣,得到此片元的顏色值。最后,將采樣到的顏色值傳給gl_FragColor內(nèi)建變量,完成片元的著色。
2.2 片元著色器介紹
片元著色器是一個(gè)處理片元值及其相關(guān)聯(lián)數(shù)據(jù)的可編程單元,片元著色器可執(zhí)行紋理的訪問(wèn)、顏色的匯總、霧化等操作,每片元執(zhí)行一次。片元著色器替代了紋理、顏色求和、霧以及Alpha測(cè)試,這一部分是需要開(kāi)發(fā)者自己開(kāi)發(fā)的。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??(1)varying指的是從頂點(diǎn)著色器傳遞到片元著色器的數(shù)據(jù)變量
(2)gl_FragColor為內(nèi)置變量,用來(lái)保存片元著色器計(jì)算完成的片元顏色值,此顏色值將送入渲染管線的后繼階段進(jìn)行處理。
???????二、加載著色器代碼示例
private int loadShader( int shaderType, String source) {// 創(chuàng)建一個(gè)新shaderint shader = GLES20.glCreateShader(shaderType);// 若創(chuàng)建成功則加載shaderif (shader != 0) {// 加載shader源代碼GLES20.glShaderSource(shader, source);// 編譯shaderGLES20.glCompileShader(shader);// 存放編譯成功shader數(shù)量的數(shù)組int[] compiled = new int[1];// 獲取Shader的編譯情況GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);if (compiled[0] == 0){//若編譯失敗則顯示錯(cuò)誤日志并刪除此shaderLog.e(TAG, "Could not compile shader " + shaderType + ":");Log.e(TAG, GLES20.glGetShaderInfoLog(shader));GLES20.glDeleteShader(shader);shader = 0;}}return shader; }上述示例代碼中主要用到了三個(gè)方法:
GLES20.glCreateShader(shaderType); GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader);1. GLES20.glCreateShader(),?創(chuàng)建一個(gè)容納shader的容器,稱為shader容器。
函數(shù)原型: int glCreateShader (int type) 方法參數(shù): GLES20.GL_VERTEX_SHADER (頂點(diǎn)shader) GLES20.GL_FRAGMENT_SHADER (片元shader)???????如果調(diào)用成功的話,函數(shù)將返回一個(gè)整形的正整數(shù)作為shader容器的id。?如果對(duì)c++熟悉的話,該函數(shù)的返回值理解為指針或者句柄更合適。
2. GLES20.glShaderSource(shader, source),添加shader的源代碼。源代碼應(yīng)該以字符串?dāng)?shù)組的形式表示。當(dāng)然,也可以只用一個(gè)字符串來(lái)包含所有的源代碼。
函數(shù)原型: void glShaderSource (int shader, String string) 參數(shù)含義: shader是代表shader容器的id(由glCreateShader返回的整形數(shù)); string是包含源程序的字符串?dāng)?shù)組。3. GLES20.glCompileShader(shader),對(duì)shader容器中的源代碼進(jìn)行編譯。
函數(shù)原型: void glCompileShader (int shader) 參數(shù)含義: shader是代表shader容器的id。????????4. 調(diào)試
????????調(diào)試一個(gè)shader是非常困難的。shader的世界里沒(méi)有printf,無(wú)法在控制臺(tái)中打印調(diào)試信息,?更沒(méi)有斷點(diǎn),甚至很多編輯器對(duì)shader程序關(guān)鍵字、變量等連高亮顯示都不支持。?但是可以通過(guò)一些OpenGL提供的函數(shù)來(lái)獲取編譯和連接過(guò)程中的信息。
????????在編譯階段使用glGetShaderiv獲取編譯情況,在連接階段使用glGetProgramiv獲取連接情況。當(dāng)錯(cuò)誤產(chǎn)生的時(shí)候,還可以從InfoLog中獲得更多的信息。InfoLog中存儲(chǔ)了關(guān)于上一個(gè)操作執(zhí)行時(shí)的相關(guān)信息,比如編譯階段的警告和錯(cuò)誤,以及連接階段產(chǎn)生的問(wèn)題。不幸的是對(duì)于錯(cuò)誤信息沒(méi)有統(tǒng)一的標(biāo)準(zhǔn),所以不同的硬件或驅(qū)動(dòng)程序?qū)⑻峁┎煌腻e(cuò)誤信息。
? ?4.1 編譯階段使用glGetShaderiv獲取編譯情況
函數(shù)原型: void glGetShaderiv (int shader, int pname, int[] params, int offset) 參數(shù)含義: shader是一個(gè)shader的id; pname使用GL_COMPILE_STATUS; params是返回值,如果一切正常返回GL_TRUE代,否則返回GL_FALSE。????4.2編譯階段使用glGetShaderInfoLog獲取編譯錯(cuò)誤
函數(shù)原型: String glGetShaderInfoLog (int shader) 參數(shù)含義: shader是一個(gè)頂點(diǎn)shader或者片元shader的id。? ?4.3 在連接階段使用glGetProgramiv獲取連接情況
函數(shù)原型: void glGetProgramiv (int program, int pname, int[] params, int offset) 參數(shù)含義: program是一個(gè)著色器程序的id; pname是GL_LINK_STATUS; param是返回值,如果一切正常返回GL_TRUE代,否則返回GL_FALSE。? 4.4 在連接階段使用glGetProgramInfoLog獲取連接錯(cuò)誤
函數(shù)原型: String glGetProgramInfoLog (int program) 參數(shù)含義: program是一個(gè)著色器程序的id。?4.5 清理shader的glDeleteShader方法
????????當(dāng)不再需要某個(gè)shader或某個(gè)程序的時(shí)候,需要對(duì)其進(jìn)行清理,以釋放資源。前面,提到過(guò)如何向一個(gè)程序中添加一個(gè)shader。這里可調(diào)用下面的函數(shù)來(lái)將一個(gè)shader從一個(gè)程序中清除掉。
函數(shù)原型: void glDeleteShader (int shader); 參數(shù)含義: shader是要被排除的頂點(diǎn)shader或者片元shader的id。????????如果,一個(gè)shader被刪除之前沒(méi)有從相應(yīng)的程序中排除,那么這個(gè)shader不會(huì)被實(shí)際刪除,而只是被標(biāo)記為被刪除;當(dāng)shader被從程序中排除的時(shí)候,才會(huì)被真正地刪除。
???????三、創(chuàng)建著色器程序代碼示例
private int createProgram(String vertexSource, String fragmentSource) {// 加載頂點(diǎn)著色器int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);if (vertexShader == 0) {return 0;}// 加載片元著色器int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);if (pixelShader == 0) {return 0;}// 創(chuàng)建著色器程序int program = GLES20.glCreateProgram();// 若程序創(chuàng)建成功則向程序中加入頂點(diǎn)著色器與片元著色器if (program != 0) {// 向程序中加入頂點(diǎn)著色器GLES20.glAttachShader(program, vertexShader);// 向程序中加入片元著色器GLES20.glAttachShader(program, pixelShader);// 鏈接程序GLES20.glLinkProgram(program);// 存放鏈接成功program數(shù)量的數(shù)組int[] linkStatus = new int[1];// 獲取program的鏈接情況GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);// 若鏈接失敗則報(bào)錯(cuò)并刪除程序if (linkStatus[0] != GLES20.GL_TRUE) {Log.e(TAG, "Could not link program: ");Log.e(TAG, GLES20.glGetProgramInfoLog(program));GLES20.glDeleteProgram(program);program = 0;}}// 釋放shader資源GLES20.glDeleteShader(vertexShader );GLES20.glDeleteShader(pixelShader);return program; }1. glCreateProgram,在連接shader之前,首先要?jiǎng)?chuàng)建一個(gè)容納程序的容器,稱為著色器程序容器。可以通過(guò)glCreateProgram函數(shù)來(lái)創(chuàng)建一個(gè)程序容器。
函數(shù)原型: int glCreateProgram () 如果函數(shù)調(diào)用成功將返回一個(gè)正整數(shù)作為該著色器程序的id。? 2. glAttachShader,接下來(lái),我們要將shader容器添加到程序中。這時(shí)的shader容器不一定需要被編譯,他們甚至不需要包含任何的代碼。我們要做的只是將shader容器添加到程序中。使用glAttachShader函數(shù)來(lái)為程序添加shader容器。
函數(shù)原型: void glAttachShader (int program, int shader) 參數(shù)含義: program是著色器程序容器的id; shader是要添加的頂點(diǎn)或者片元shader容器的id。????????如果你同時(shí)擁有了,頂點(diǎn)shader和片元shader,需要分別將他們各自的兩個(gè)shader容器添加的程序容器中。
?3. glLinkProgram,鏈接程序。
函數(shù)原型: void glLinkProgram (int program) 參數(shù)含義: program是著色器程序容器的id。在鏈接操作執(zhí)行以后,可以任意修改shader的源代碼,對(duì)shader重新編譯不會(huì)影響整個(gè)程序,除非重新鏈接程序。
?4. glUseProgram,加載并使用鏈接好的程序。
函數(shù)原型: void glUseProgram (int program) 參數(shù)含義: program是要使用的著色器程序的id。如果將program設(shè)置為0,表示使用固定功能管線。如果程序已經(jīng)在使用的時(shí)候,對(duì)程序進(jìn)行重新編譯,編譯后的應(yīng)用程序會(huì)自動(dòng)替代以前的那個(gè)被調(diào)用,這時(shí)你不需要再次調(diào)用這個(gè)函數(shù)。
四、?向著色器程序中傳遞數(shù)據(jù)
1. 獲取著色器程序內(nèi)成員變量的id,也可以理解為句柄、指針。glGetAttribLocation方法:獲取著色器程序中,指定為attribute類(lèi)型變量的id。glGetUniformLocation方法:獲取著色器程序中,指定為uniform類(lèi)型變量的id。如:// 獲取指向著色器中aPosition的indexmaPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");// 獲取指向著色器中uMVPMatrix的indexmuMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");2.?傳遞數(shù)據(jù)
????????使用上一節(jié)獲取的指向著色器相應(yīng)數(shù)據(jù)成員的各個(gè)id,就能將我們自己定義的頂點(diǎn)數(shù)據(jù)、顏色數(shù)據(jù)等等各種數(shù)據(jù)傳遞到著色器當(dāng)中了。
// 使用shader程序 GLES20.glUseProgram(mProgram); // 將最終變換矩陣傳入shader程序 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); // 設(shè)置緩沖區(qū)起始位置 mRectBuffer.position(0); // 頂點(diǎn)位置數(shù)據(jù)傳入著色器 GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 20, mRectBuffer); // 頂點(diǎn)顏色數(shù)據(jù)傳入著色器中 GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false, 4*4, mColorBuffer); // 頂點(diǎn)坐標(biāo)傳遞到頂點(diǎn)著色器 GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, 20, mRectBuffer); // 允許使用頂點(diǎn)坐標(biāo)數(shù)組 GLES20.glEnableVertexAttribArray(maPositionHandle); // 允許使用頂點(diǎn)顏色數(shù)組 GLES20.glDisableVertexAttribArray(maColorHandle); // 允許使用定點(diǎn)紋理數(shù)組 GLES20.glEnableVertexAttribArray(maTextureHandle); // 綁定紋理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); // 圖形繪制 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);2.1 glVertexAttribPointer,定義頂點(diǎn)屬性數(shù)組。
函數(shù)原型: void glVertexAttribPointer (int index, int size, int type, boolean normalized, int stride, Buffer ptr ) 參數(shù)含義:index 指定要修改的頂點(diǎn)著色器中頂點(diǎn)變量id;size 指定每個(gè)頂點(diǎn)屬性的組件數(shù)量。必須為1、2、3或者4。如position是由3個(gè)(x,y,z)組成,而顏色是4個(gè)(r,g,b,a));type 指定數(shù)組中每個(gè)組件的數(shù)據(jù)類(lèi)型。可用的符號(hào)常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT;normalized 指定當(dāng)被訪問(wèn)時(shí),固定點(diǎn)數(shù)據(jù)值是否應(yīng)該被歸一化(GL_TRUE)或者直接轉(zhuǎn)換為固定點(diǎn)值(GL_FALSE);stride 指定連續(xù)頂點(diǎn)屬性之間的偏移量。如果為0,那么頂點(diǎn)屬性會(huì)被理解為:它們是緊密排列在一起的。初始值為0。如果normalized被設(shè)置為GL_TRUE,意味著整數(shù)型的值會(huì)被映射至區(qū)間[-1,1](有符號(hào)整數(shù)),或者區(qū)間[0,1](無(wú)符號(hào)整數(shù)),反之,這些值會(huì)被直接轉(zhuǎn)換為浮點(diǎn)值而不進(jìn)行歸一化處理;ptr 頂點(diǎn)的緩沖數(shù)據(jù)。2.2 啟用或者禁用頂點(diǎn)屬性數(shù)組。?調(diào)用glEnableVertexAttribArray和glDisableVertexAttribArray傳入?yún)?shù)index。如果啟用,那么當(dāng)glDrawArrays或者glDrawElements被調(diào)用時(shí),頂點(diǎn)屬性數(shù)組會(huì)被使用。
2.3 glActiveTexture,?選擇活動(dòng)紋理單元。
函數(shù)原型: void glActiveTexture (int texture) 參數(shù)含義: texture指定哪一個(gè)紋理單元被置為活動(dòng)狀態(tài)。texture必須是GL_TEXTUREi之一,其中0 <<span class="title">= i < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,初始值為GL_TEXTURE0。glActiveTexture()確定了后續(xù)的紋理狀態(tài)改變影響哪個(gè)紋理,紋理單元的數(shù)量是依據(jù)該紋理單元所被支持的具體實(shí)現(xiàn)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/Anita9002/p/4929795.html
總結(jié)
以上是生活随笔為你收集整理的OpenGL中shader使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: LeetCode Implement Q
- 下一篇: 日记2015.11.5