Android Opengles開篇
前言:opengles感覺算是一個很"古老"的技術了,從1.0-2.0-3.0經歷 了三個版本了
本篇及以后講述的都是建立在opengles2.0基礎上進行demo演示的。可能有些人
對opengl比較陌生,但在Android手機當中和圖像相關的東西,都會涉及到Opengl。
一.什么是Opengles?
Opengl這東西是有的,為什么要在后面加上一個ES呢?它主要是針對于嵌入式設備,對原本Opengl
進行了裁剪的縮減庫.Opengles的功能比Opengl要少一些。說白了就是Opengles中你要寫的代碼就更多了,
因為在Opengl中有的一些基本圖形被縮減到只有點,線,三角形了。
二.廢話就不. 多說了,開篇就用opengles畫個三角形,對其中的基本概念進行敘述。
開發工具就用是Android studio2.1
在AndroidStudio中有個GLSL的插件,可以識別著色器語言的.
GLSL是opengles中一個關鍵的文件,它主要是分為Vertex(頂點)Shader的glsl以及Fragment(片段)Shader的glsl.對它們的編寫就決定最終物體相關屬性是怎么設置的了。
對于GLES的說明會放到后面進行。這里畫三角形,對GLSL編寫也是很簡單的,就直接在代碼里加注釋說明。
以建造房子為例子來闡述一個三角形的繪制
三.首先是GLES的簡單編寫
1.畫筆準備:
Vertex和Frament在opengles中是以字符串的形式被載入,所以在assets里面創建兩個資源文件
vertex.glsl和frag.glsl
vertex.glsl
uniform mat4 uMVPMatrix; //頂點最終變換矩陣
attribute vec3 aPostion; //頂點坐標值(x,y,z)
attribute vec4 aColor; //頂點的顏色(R,G,B,A)
varying vec4 vColor; //傳遞給片段著色器的顏色值,varying聲明的變量都是要傳遞給fragment的
void main(){
//gl_Position是glsl的內置變量,記錄頂點最終的位置 。
//vec4(aPostion,1)為是矩陣想成匹配
gl_Position = uMVPMatrix * vec4(aPostion,1);
vColor = aColor;//將頂點顏色值傳遞給fragment
}
frag.glsl
precision mediump float; //聲明float的精度,一般情況下都是用mediump的
varying vec4 vColor; //接收從頂點glsl傳過來的顏色參數
//對片段顏色的具體處理
void main(){
//直接將頂點傳過來的顏色參數賦值給了內置變量gl_fragColor,也就給fragment上色了
gl_FragColor = vColor;
}
2.將shader(glsl)載入到系統中做畫畫準備:
幫助類的編寫,這個是固定的模板,以后都可以直接進行復用的.
這個幫主類的簡單流程如下:
- 將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的腳本字符串
) {
// 創建一個新shader
int shader = GLES20.glCreateShader(shaderType);
// 若創建成功則加載shader
if (shader != 0) {
// 加載shader的源代碼
GLES20.glShaderSource(shader, source);
// 編譯shader
GLES20.glCompileShader(shader);
// 存放編譯成功shader數量的數組
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;
}
// 創建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;
}
// 創建程序
int program = GLES20.glCreateProgram();
// 若程序創建成功則向程序中加入頂點著色器與片元著色器
if (program != 0) {
// 向程序中加入頂點著色器
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
// 向程序中加入片元著色器
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
// 鏈接程序
GLES20.glLinkProgram(program);
// 存放鏈接成功program數量的數組
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內容的方法
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;
}
}
準備工作做完,就需要開始繪制三角形了。
3.Trangle 三角形類編寫
注意事項:1.opengles采用的是左手坐標系x->大拇指,y->中指,z->食指
2.其中涉及到的一些矩陣,后續會講到。
//三角形
public class Triangle
{
public static float[] mProjMatrix = new float[16];//4x4矩陣 投影用
public static float[] mVMatrix = new float[16];//攝像機位置朝向9參數矩陣
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];//具體物體的移動旋轉矩陣,旋轉、平移
FloatBuffer mVertexBuffer;//頂點坐標數據緩沖
FloatBuffer mColorBuffer;//頂點著色數據緩沖
int vCount=0;
float xAngle=0;//繞x軸旋轉的角度
public Triangle(MyTDView mv)
{
//初始化頂點坐標與著色數據
initVertexData();
//初始化shader
initShader(mv);
}
public void initVertexData()
{
//頂點坐標數據的初始化
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)
{
//加載頂點著色器的腳本內容
mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
//加載片元著色器的腳本內容
mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
//基于頂點著色器與片元著色器創建程序
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++;
//設置沿Z軸正向位移1
Matrix.translateM(mMMatrix,0,0,0,-1);
//設置繞x軸旋轉
Matrix.rotateM(mMMatrix,0,xAngle,0,0,1);
//
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);
//為畫筆指定頂點位置數據
GLES20.glVertexAttribPointer(
maPositionHandle,
3,
GLES20.GL_FLOAT,
false,
3*4,
mVertexBuffer
);
GLES20.glVertexAttribPointer
(
maColorHandle,
4,
GLES20.GL_FLOAT,
false,
4*4,
mColorBuffer
);
//允許頂點位置數據數組
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;
}
}
現在對代碼的==關鍵點==進行說明:
-
四個重要的矩陣mMMatrix,mVMatrix,mProjMatrix,mMVPMatrix
- mMMatrix指的是物體本身位置,角度的狀態矩陣
- mVMatrix指的是人眼看物體時候的位置,角度的狀態矩陣
- mProjMatrix指的是物體成像所處的透視環境狀態
- mMVPMatrix指的是mMMatrix,mVMatrix,mProjMatrix三者結合的矩陣
-
其它參數說明:
- mVertexBuffer指的是vertex buffer ,將頂點數據燦在vertex buffer里面后后續的管線操作就可以直接從buffer取的數據,這樣加快了數據的讀取
- mColorBuffer 類似
-
局部代碼說明:
float vertices[]=new float[] { -4*UNIT_SIZE,0, 0,0,-4*UNIT_SIZE, 0,4*UNIT_SIZE,0,0, };
頂點的坐標的,前面說過了opengl采用的是左手坐標系
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); //為畫筆指定頂點位置數據 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中對應的位置屬性值,==3==指的是坐標點是以頂點數組中三個值為一個頂點,==GLES20.GL_FLOAT==是頂點數組中數據類型,==34==值的是Buffer中一個頂點的大小因為三個float組成一個頂點所以是34,mVertexBuffer就是頂點坐標數組轉成的buffer了5.GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
Android的openlgles的圖元有點,線,三角形,所有的圖形就靠它們來組成,因為這里
是直接畫三角形,所以就用GLES20.GL_TRAINGLES這篇就直觀上講解了opengles繪制一個簡單圖形是怎么繪制的。下篇開始講知識點。
代碼地址:http://pan.baidu.com/s/1dFcjdZj