OpenGLES的術(shù)語很多,我想通過自己的語言講的簡單一些:(不知道效果怎樣,有意見的同學(xué)歡迎在評論區(qū)指教)
1、確定頂點(diǎn),紋理坐標(biāo):
任何的圖形: 點(diǎn)、線、幾何圖形、空間幾何圖形,都可以用幾個三角形來構(gòu)成。
三個頂點(diǎn)可以確定一個三角形,所以通過一系列頂點(diǎn)坐標(biāo)的組合,我們可以繪制出任何圖形。

紋理簡單來說就是一張圖片。
紋理坐標(biāo)就是圖片上的點(diǎn)的坐標(biāo)。
確定紋理坐標(biāo)是指:頂點(diǎn)對應(yīng)圖片資源上的點(diǎn)的坐標(biāo)。
2、頂點(diǎn)處理:
例如某個空間圖形旋轉(zhuǎn)90°,那旋轉(zhuǎn)前后該空間圖形的頂點(diǎn)的空間坐標(biāo)發(fā)生了變化,懶惰的程序員是不會重新列舉一遍頂點(diǎn)坐標(biāo)的,我們會通過一個矩陣和原來的頂點(diǎn)坐標(biāo)來計算新的頂點(diǎn)坐標(biāo)。這一步Opengl es 通過頂點(diǎn)著色器來實現(xiàn)。
3、繪制紋理:
你可以把圖片想象成塊布,紋理坐標(biāo)就是上面的特定位置加上圖釘??,最后把圖釘按到對應(yīng)的頂點(diǎn)坐標(biāo)上。
平面上的每一個點(diǎn)都會對應(yīng)圖片上某個點(diǎn),把對應(yīng)點(diǎn)的顏色繪制到平面上,整個空間圖形就有了色彩。
這一步Opengl es 通過片元著作色器實現(xiàn)。
記住流程,我們一步一步來調(diào)用具體api:
OpenGLES api
1、確定頂點(diǎn)坐標(biāo),紋理坐標(biāo):

Opengl es 坐標(biāo)系中(0,0)指的是畫布的中心點(diǎn),對于GLSufaceView就是View的中心點(diǎn)。x=1指的是View最右的位置,y=1指的是view最上的位置。(1,1)就是右上角的點(diǎn)。
以繪制一個平面圖形為例,一個正方形由兩個三角形組成,所以坐標(biāo)為:
{
-1, 1//三角形1號
-1,-1
1, 1
1, 1//三角形2號
-1,-1
1,-1
}
OpenGL有個更簡便的寫法:
{
-1, 1//三角形1號,1號點(diǎn)
-1,-1//2號點(diǎn)
1, 1 //3號點(diǎn)
1,-1//三角形2號 ,4號點(diǎn)
}
方式1:太直觀了,6個頂點(diǎn)。
方式2:前三個頂點(diǎn)組成一個三角形,后面每一個頂點(diǎn)代表一個三角形。實際上方式2的頂點(diǎn)數(shù)組會由OpenGL轉(zhuǎn)換成方式1。
偶數(shù)點(diǎn)三角形的頂點(diǎn)為 [n-1 ,n-2 ,n],奇數(shù)點(diǎn)三角形的頂點(diǎn)為 [n-2 ,n-1 ,n]
方式2最后一個頂點(diǎn)是第四個,偶數(shù)點(diǎn),三角形坐標(biāo):(1,1),( -1,-1),(1,-1)和方式1一樣。
這里必須要注意的是:頂點(diǎn)數(shù)組的書寫順序是由要求的
先提個問:一個三角形有幾個面?1個?再想想。
兩個,正反面,OpenGL中使用頂點(diǎn)順序來標(biāo)識正反面。在沒有通過api進(jìn)行設(shè)置的情況下,OpenGL認(rèn)為逆時針的頂點(diǎn)排序為正面,順時針的頂點(diǎn)排序為反面。對于平面圖形來說,渲染到反面的圖像是看不見的。
所以注意,頂點(diǎn)的書寫順序為逆時針。
上圖可以明顯的看到頂點(diǎn)坐標(biāo)對應(yīng)的紋理坐標(biāo),以方法二為例:
{//頂點(diǎn)坐標(biāo)
-1, 1//三角形1號,1號點(diǎn)
-1,-1//2號點(diǎn)
1, 1 //3號點(diǎn)
1,-1//三角形2號 ,4號點(diǎn)
}
{//紋理坐標(biāo)
0, 0//三角形1號,1號點(diǎn)
0,1//2號點(diǎn)
1, 0 //3號點(diǎn)
1,1//三角形2號 ,4號點(diǎn)
}
2、頂點(diǎn)處理:
上面說到OpenGL中這一步通過頂點(diǎn)著色器實現(xiàn)。那么什么是頂點(diǎn)著色器呢?
著色器是運(yùn)行在OpenGL里通過GLSL(OpenGL Shading Language)語言編寫的程序。
頂點(diǎn)著色器 Vertex Shader :則是一個用來操作頂點(diǎn)數(shù)據(jù)的著色器程序。
//這是一個最普通的頂點(diǎn)著色器
String vStr = "attribute vec4 vPosition;" +
"attribute vec2 vCoordinate;" +
"varying vec2 aCoordinate;" +
"uniform mat4 vMatrix;" +
"void main() { " +
" gl_Position = vMatrix*vPosition;" +
" aCoordinate=vCoordinate;" +
"}";
attribute vec4:attribute聲明的變量需要開發(fā)者從外部程序輸入一個坐標(biāo)數(shù)組,這個坐標(biāo)數(shù)組構(gòu)成一個區(qū)域,而OpenGL會把區(qū)域中的一個點(diǎn)用一個多維向量(vec4就是四維(x,y,z,w),vec2就是二維(x,y))賦值給該變量。OpenGL會反復(fù)調(diào)用程序,對區(qū)域內(nèi)的每一個點(diǎn)進(jìn)行計算
uniform:聲名一個值從程序外傳入的變量。
mat4:表示變量是個4x4浮點(diǎn)矩陣,同理mat3——3x3
varying:varying變量是頂點(diǎn)著色器和片元著色器之間做數(shù)據(jù)傳遞用的。
gl_Position :頂點(diǎn)著色器的輸出,是個vec4變量。其值決定了畫筆的位置。
既然著色器是另外一種語言,想在android中運(yùn)行另一種語言編寫的程序,其中必有一番曲折:
1、創(chuàng)建一個空的OpenGLES程序
int mProgram = GLES20.glCreateProgram();
返回值是個句柄,大部分OpenGL方法的返回值都是句柄。
2、編譯著色器
變成語言語言變成可運(yùn)行程序必然需要編譯啦。
//vStr是上面的頂點(diǎn)著色器程序代碼,以String形式輸入
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vStr);
------------------------------------------------------------------------------------
private int loadShader(int type, String str) {
//1:創(chuàng)建Shader glCreateShader
int id = GLES20.glCreateShader(type);
//2:指定Shader源代碼 glShaderSource
GLES20.glShaderSource(id, str);
// 3:編譯Shader glCompileShader
GLES20.glCompileShader(id);
// 4:獲取shader狀態(tài) glGetShaderiv
private int[] compile = new int[1];//入?yún)? GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, compile, 0);
// 5:如果出錯 就 獲取shader日志信息 glGetShaderInfoLog
if (compile[0] == GLES20.GL_FALSE) {
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(id));
GLES20.glDeleteShader(id);
return -1;
} else {
return id;
}
}
參數(shù)str指的是我們用GLSL語言書寫的著色器程序文本字符串。
GLES20.GL_VERTEX_SHADER:指的是當(dāng)前編譯的著色器類型——為頂點(diǎn)著色器。
3、將頂點(diǎn)著色器加入到程序
GLES20.glAttachShader(mProgram, vertexShader);
4、指定當(dāng)前使用的Program
GLES20.glLinkProgram(mProgram);
5、為著色器中的變量賦值
glPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");//獲取句柄,獲取一次即可
glHCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
——————————————————————————————————————
GLES20.glEnableVertexAttribArray(glPosition);//激活,繪制開始時調(diào)用
GLES20.glVertexAttribPointer(glPosition, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer); //輸入頂點(diǎn)坐標(biāo)數(shù)據(jù)
GLES20.glEnableVertexAttribArray(glHCoordinate);
GLES20.glVertexAttribPointer(glHCoordinate, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);//輸入紋理坐標(biāo)數(shù)據(jù)
——————————————————————————————
GLES20.glDisableVertexAttribArray(glPosition);//繪制完成后調(diào)用
GLES20.glDisableVertexAttribArray(glHCoordinate);//繪制完成后調(diào)用
說一下 glVertexAttribPointer:
第一個參數(shù)為目標(biāo)參數(shù)句柄。
第二個參數(shù)2表示的是:坐標(biāo)的維數(shù),2就是2維(x,y)。
第三個參數(shù)GLES20.GL_FLOAT表示輸入數(shù)據(jù)的精度。
第四個參數(shù)定義是否希望數(shù)據(jù)被標(biāo)準(zhǔn)化(歸一化)。
第五個參數(shù)數(shù)是步長(Stride),指定在連續(xù)的頂點(diǎn)屬性之間的間隔。如果傳1取值方式為0123、1234、2345……
最后一個即頂點(diǎn)坐標(biāo)數(shù)組數(shù)據(jù),以 java.nio.Buffer形式輸入。還有另外一種輸入方式這里暫不討論
vertices={
-1, 1//三角形1號,1號點(diǎn)
-1,-1//2號點(diǎn)
1, 1 //3號點(diǎn)
1,-1//三角形2號 ,4號點(diǎn)
}
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
3、繪制紋理:
繪制紋理OpenGL 通過片元著色器實現(xiàn),下面是一個簡單的片元著色器:
String fStr =
" precision mediump float; " +
"uniform sampler2D vTexture; " +
"varying vec2 aCoordinate; " +
"void main() {" +
" gl_FragColor=texture2D(vTexture,aCoordinate);" +
"}";
sampler2D:一個紋理引用
aCoordinate:由頂點(diǎn)作色器傳過來的紋理坐標(biāo)
texture2D()函數(shù):從紋理上取紋理坐標(biāo)對應(yīng)點(diǎn)的顏色。
gl_FragColor:紋理著色器的輸出,也就是畫筆要畫的顏色
同樣片元著色器也需要經(jīng)過編譯綁定到OpenGL的program中
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fStr);
GLES20.glAttachShader(mProgram, fragmentShader);
需要賦值的參數(shù)只有一個uniform sampler2D vTexture
glTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");//獲取句柄
---------------------------------------------------------------------------------------------------
int[] texture = new int[1];
if (bitmap != null && !bitmap.isRecycled()) {
//生成紋理
GLES20.glGenTextures(1, texture, 0);
//激活0號紋理單元
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//綁定紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
//設(shè)置縮小過濾為使用紋理中坐標(biāo)最接近的一個像素的顏色作為需要繪制的像素顏色
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
//設(shè)置放大過濾為使用紋理中坐標(biāo)最接近的若干個顏色,通過加權(quán)平均算法得到需要繪制的像素顏色
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//設(shè)置環(huán)繞方向S,截取紋理坐標(biāo)到[1/2n,1-1/2n]。將導(dǎo)致永遠(yuǎn)不會與border融合
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
//設(shè)置環(huán)繞方向T,截取紋理坐標(biāo)到[1/2n,1-1/2n]。將導(dǎo)致永遠(yuǎn)不會與border融合
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
//根據(jù)以上指定的參數(shù),生成一個2D紋理
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
return texture[0];
}
-----------------------------------------------------------------------------------------------------
GLES20.glUniform1i(glTexture, 0);//輸入紋理,使用0號紋理單元
-----------------------------------------------------------------------------------------------------
這里說一下紋理單元,有點(diǎn)像一個類,它里面包含很多類型的紋理成員變量,比如說常用的GL_TEXTURE_2D,GL_TEXTURE_EXTERNAL_OES。
//打個比方,不是實際代碼
TextureUnit{
GL_TEXTURE_2D textureA;
GL_TEXTURE_EXTERNAL_OES textureB;
}
我們只有通過glActiveTexture激活了紋理單元后才能通過glBindTexture方法對其中的成員變量進(jìn)行賦值。
以上GLSL的相關(guān)流程就結(jié)束了,完成了OpenGL管線的配置,但是要開始實際的繪制,我們還要調(diào)用繪制的api
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, indexBuffer);//繪制調(diào)用方式1
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);//繪制調(diào)用方式2,參數(shù)4代表輸入頂點(diǎn)數(shù)組的長度
glDrawElements 需要額外輸入索引數(shù)組 (上面的indexBuffer),其內(nèi)容指的是頂點(diǎn)的繪制順序。
{//頂點(diǎn)坐標(biāo)
-1, 1//三角形1號,1號點(diǎn)
-1,-1//2號點(diǎn)
1, 1 //3號點(diǎn)
1,-1//三角形2號 ,4號點(diǎn)
}
{
0,1,2,
1,3,2
}
以上接口都需要在GLThread中調(diào)用,通過GLSufaceVIew可以簡單創(chuàng)建GLThread環(huán)境:
OpenGL簡介:簡單的使用GLSufaceView
我在學(xué)習(xí)android 的OpenGL時寫的小栗子
https://github.com/UniqueKenzhang/AllAboutVideo
