Android Opengles開篇
前言:opengles感覺算是一個很"古老"的技術(shù)了酱固,從1.0-2.0-3.0經(jīng)歷 了三個版本了
本篇及以后講述的都是建立在opengles2.0基礎(chǔ)上進行demo演示的。可能有些人
對opengl比較陌生府怯,但在Android手機當(dāng)中和圖像相關(guān)的東西,都會涉及到Opengl矗烛。
一.什么是Opengles玫镐?
Opengl這東西是有的倒戏,為什么要在后面加上一個ES呢?它主要是針對于嵌入式設(shè)備恐似,對原本Opengl
進行了裁剪的縮減庫.Opengles的功能比Opengl要少一些杜跷。說白了就是Opengles中你要寫的代碼就更多了,
因為在Opengl中有的一些基本圖形被縮減到只有點矫夷,線葛闷,三角形了。
二.廢話就不. 多說了双藕,開篇就用opengles畫個三角形淑趾,對其中的基本概念進行敘述。
開發(fā)工具就用是Android studio2.1
在AndroidStudio中有個GLSL的插件忧陪,可以識別著色器語言的.
GLSL是opengles中一個關(guān)鍵的文件扣泊,它主要是分為Vertex(頂點)Shader的glsl以及Fragment(片段)Shader的glsl.對它們的編寫就決定最終物體相關(guān)屬性是怎么設(shè)置的了。
對于GLES的說明會放到后面進行嘶摊。這里畫三角形延蟹,對GLSL編寫也是很簡單的,就直接在代碼里加注釋說明更卒。
以建造房子為例子來闡述一個三角形的繪制
三.首先是GLES的簡單編寫
1.畫筆準(zhǔn)備:
Vertex和Frament在opengles中是以字符串的形式被載入等孵,所以在assets里面創(chuàng)建兩個資源文件
vertex.glsl和frag.glsl
vertex.glsl
uniform mat4 uMVPMatrix; //頂點最終變換矩陣
attribute vec3 aPostion; //頂點坐標(biāo)值(x,y,z)
attribute vec4 aColor; //頂點的顏色(R,G,B,A)
varying vec4 vColor; //傳遞給片段著色器的顏色值,varying聲明的變量都是要傳遞給fragment的
void main(){
//gl_Position是glsl的內(nèi)置變量稚照,記錄頂點最終的位置 蹂空。
//vec4(aPostion,1)為是矩陣想成匹配
gl_Position = uMVPMatrix * vec4(aPostion,1);
vColor = aColor;//將頂點顏色值傳遞給fragment
}
frag.glsl
precision mediump float; //聲明float的精度俯萌,一般情況下都是用mediump的
varying vec4 vColor; //接收從頂點glsl傳過來的顏色參數(shù)
//對片段顏色的具體處理
void main(){
//直接將頂點傳過來的顏色參數(shù)賦值給了內(nèi)置變量gl_fragColor,也就給fragment上色了
gl_FragColor = vColor;
}
2.將shader(glsl)載入到系統(tǒng)中做畫畫準(zhǔn)備:
幫助類的編寫,這個是固定的模板上枕,以后都可以直接進行復(fù)用的.
這個幫主類的簡單流程如下:
- 將asset中的vertext.glsl和frag.glsl
- 方法名為:public static String loadFromAssetsFile(String fname, Resources r);
- 獲取vertex的ID和fragment的ID
- 方法名為:public static int loadShader(int shaderType, String source )
- 通過上面獲得的ID,將vertext和fragment組合成programe
- public static int createProgram(String vertexSource, String fragmentSource)
幫助類ShaderUtil的具體代碼
package com.bn.Sample3_1;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;
//加載頂點Shader與片元Shader的工具類
public class ShaderUtil {
// 加載制定shader的方法
public static int loadShader(int shaderType, // shader的類型
// GLES20.GL_VERTEX_SHADER(頂點)
// GLES20.GL_FRAGMENT_SHADER(片元)
String source // shader的腳本字符串
) {
// 創(chuàng)建一個新shader
int shader = GLES20.glCreateShader(shaderType);
// 若創(chuàng)建成功則加載shader
if (shader != 0) {
// 加載shader的源代碼
GLES20.glShaderSource(shader, source);
// 編譯shader
GLES20.glCompileShader(shader);
// 存放編譯成功shader數(shù)量的數(shù)組
int[] compiled = new int[1];
// 獲取Shader的編譯情況
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {// 若編譯失敗則顯示錯誤日志并刪除此shader
Log.e("ES20_ERROR", "Could not compile shader " + shaderType
+ ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
// 創(chuàng)建shader程序的方法
public static int createProgram(String vertexSource, String fragmentSource) {
// 加載頂點著色器
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)建成功則向程序中加入頂點著色器與片元著色器
if (program != 0) {
// 向程序中加入頂點著色器
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
// 向程序中加入片元著色器
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
// 鏈接程序
GLES20.glLinkProgram(program);
// 存放鏈接成功program數(shù)量的數(shù)組
int[] linkStatus = new int[1];
// 獲取program的鏈接情況
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
// 若鏈接失敗則報錯并刪除程序
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
// 檢查每一步操作是否有錯誤的方法
public static void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
// 從sh腳本中加載shader內(nèi)容的方法
public static String loadFromAssetsFile(String fname, Resources r) {
String result = null;
try {
InputStream in = r.getAssets().open(fname);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((ch = in.read()) != -1) {
baos.write(ch);
}
byte[] buff = baos.toByteArray();
baos.close();
in.close();
result = new String(buff, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
準(zhǔn)備工作做完咐熙,就需要開始繪制三角形了。
3.Trangle 三角形類編寫
注意事項:1.opengles采用的是左手坐標(biāo)系x->大拇指辨萍,y->中指棋恼,z->食指
2.其中涉及到的一些矩陣,后續(xù)會講到锈玉。
//三角形
public class Triangle
{
public static float[] mProjMatrix = new float[16];//4x4矩陣 投影用
public static float[] mVMatrix = new float[16];//攝像機位置朝向9參數(shù)矩陣
public static float[] mMVPMatrix;//最后起作用的總變換矩陣
int mProgram;//自定義渲染管線程序id
int muMVPMatrixHandle;//總變換矩陣引用id
int maPositionHandle; //頂點位置屬性引用id
int maColorHandle; //頂點顏色屬性引用id
String mVertexShader;//頂點著色器
String mFragmentShader;//片元著色器
static float[] mMMatrix = new float[16];//具體物體的移動旋轉(zhuǎn)矩陣爪飘,旋轉(zhuǎn)、平移
FloatBuffer mVertexBuffer;//頂點坐標(biāo)數(shù)據(jù)緩沖
FloatBuffer mColorBuffer;//頂點著色數(shù)據(jù)緩沖
int vCount=0;
float xAngle=0;//繞x軸旋轉(zhuǎn)的角度
public Triangle(MyTDView mv)
{
//初始化頂點坐標(biāo)與著色數(shù)據(jù)
initVertexData();
//初始化shader
initShader(mv);
}
public void initVertexData()
{
//頂點坐標(biāo)數(shù)據(jù)的初始化
vCount=3;
final float UNIT_SIZE=0.2f;
float vertices[]=new float[]
{
-4*UNIT_SIZE,0,
0,0,-4*UNIT_SIZE,
0,4*UNIT_SIZE,0,0,
-4*UNIT_SIZE,0,0
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
float colors[]=new float[]
{
1,0,0,1,
0,0,1,0,
0,1,0,0
};
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
}
//初始化shader
public void initShader(MyTDView mv)
{
//加載頂點著色器的腳本內(nèi)容
mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
//加載片元著色器的腳本內(nèi)容
mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
//基于頂點著色器與片元著色器創(chuàng)建程序
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//獲取程序中頂點位置屬性引用id
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
//獲取程序中頂點顏色屬性引用id
maColorHandle= GLES20.glGetAttribLocation(mProgram, "aColor");
//獲取程序中總變換矩陣引用id
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
int i=0;
public void drawSelf()
{
//制定使用某套shader程序
GLES20.glUseProgram(mProgram);
//初始化變換矩陣
Matrix.setRotateM(mMMatrix,0,0,0,1,0);
i=1;
if(i == 1){
for(int j=0;j<mMMatrix.length;j++){
Log.i("liang.chen","item"+j+" is:"+mMMatrix[j]);
}
}
i++;
//設(shè)置沿Z軸正向位移1
Matrix.translateM(mMMatrix,0,0,0,-1);
//設(shè)置繞x軸旋轉(zhuǎn)
Matrix.rotateM(mMMatrix,0,xAngle,0,0,1);
//
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);
//為畫筆指定頂點位置數(shù)據(jù)
GLES20.glVertexAttribPointer(
maPositionHandle,
3,
GLES20.GL_FLOAT,
false,
3*4,
mVertexBuffer
);
GLES20.glVertexAttribPointer
(
maColorHandle,
4,
GLES20.GL_FLOAT,
false,
4*4,
mColorBuffer
);
//允許頂點位置數(shù)據(jù)數(shù)組
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maColorHandle);
//繪制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
}
public static float[] getFianlMatrix(float[] spec)
{
mMVPMatrix=new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
}
現(xiàn)在對代碼的==關(guān)鍵點==進行說明:
-
四個重要的矩陣mMMatrix拉背,mVMatrix师崎,mProjMatrix,mMVPMatrix
- mMMatrix指的是物體本身位置椅棺,角度的狀態(tài)矩陣
- mVMatrix指的是人眼看物體時候的位置犁罩,角度的狀態(tài)矩陣
- mProjMatrix指的是物體成像所處的透視環(huán)境狀態(tài)
- mMVPMatrix指的是mMMatrix,mVMatrix,mProjMatrix三者結(jié)合的矩陣
-
其它參數(shù)說明:
- mVertexBuffer指的是vertex buffer ,將頂點數(shù)據(jù)燦在vertex buffer里面后后續(xù)的管線操作就可以直接從buffer取的數(shù)據(jù),這樣加快了數(shù)據(jù)的讀取
- mColorBuffer 類似
-
局部代碼說明:
float vertices[]=new float[] { -4*UNIT_SIZE,0, 0,0,-4*UNIT_SIZE, 0,4*UNIT_SIZE,0,0, };
頂點的坐標(biāo)的两疚,前面說過了opengl采用的是左手坐標(biāo)系
float colors[]=new float[] { 1,0,0,1, 0,0,1,0, 0,1,0,0 };
三個頂點的顏色值采用的是RGBA
Matrix.setRotateM(mMMatrix,0,0,1,0,0)床估,是將mMMatrix初始化為單位矩陣
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); //為畫筆指定頂點位置數(shù)據(jù) GLES20.glVertexAttribPointer( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3*4, mVertexBuffer ); GLES20.glVertexAttribPointer ( maColorHandle, 4, GLES20.GL_FLOAT, false, 4*4, mColorBuffer );
這兩個的作用是對頂點屬性值進行賦值的,以第一個glVertexAttribPointer為例
==maPositionHandle ==是vertex shader中對應(yīng)的位置屬性值诱渤,==3==指的是坐標(biāo)點是以頂點數(shù)組中三個值為一個頂點丐巫,==GLES20.GL_FLOAT==是頂點數(shù)組中數(shù)據(jù)類型,==34==值的是Buffer中一個頂點的大小因為三個float組成一個頂點所以是34,mVertexBuffer就是頂點坐標(biāo)數(shù)組轉(zhuǎn)成的buffer了5.GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
Android的openlgles的圖元有點勺美,線鞋吉,三角形,所有的圖形就靠它們來組成励烦,因為這里
是直接畫三角形谓着,所以就用GLES20.GL_TRAINGLES這篇就直觀上講解了opengles繪制一個簡單圖形是怎么繪制的。下篇開始講知識點坛掠。
代碼地址:http://pan.baidu.com/s/1dFcjdZj