說(shuō)明:Android中1.x已經(jīng)過(guò)時(shí)了,且2.0并不兼容1.x。本文以2.0版本為準(zhǔn)!
1. 創(chuàng)建GLSurfaceView和Renderer
詳細(xì)解釋在代碼中用注釋標(biāo)明了,就不另作介紹了。
GLSurfaceView負(fù)責(zé)界面展示,GLSurfaceView.Renderer負(fù)責(zé)界面的渲染邏輯控制。
public class TriangleGLView extends GLSurfaceView {
public TriangleGLView(Context context) {
this(context,null);
}
public TriangleGLView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
//設(shè)置OpenGLES版本號(hào)2.0
setEGLContextClientVersion(2);
//設(shè)置渲染器
setRenderer(new TriangleRenderer(this));
//設(shè)置渲染模式
//①RENDERMODE_CONTINUOUSLY:主動(dòng)繪制,一定的時(shí)間間隔自動(dòng)調(diào)用onDrawFrame方法,進(jìn)行繪制
//②RENDERMODE_WHEN_DIRTY:被動(dòng)繪制,只有當(dāng)調(diào)用requestRender()方法是,才會(huì)進(jìn)行繪制。
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
private class TriangleRenderer implements Renderer {
private GLSurfaceView mView;
private Triangle mTriangle;
public TriangleRenderer(GLSurfaceView view){
this.mView = view;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//設(shè)置背景色
GLES20.glClearColor(05f,0.5f,0.5f,0.5f);
//開(kāi)啟OpenGLES的某些功能。GL_DEPTH_TEST:深度測(cè)試,當(dāng)繪制3D圖形的時(shí)候需要開(kāi)啟,我們現(xiàn)在繪制的是平面圖形不要開(kāi)啟。
// GLES20.glEnable(GLES20.GL_DEPTH_TEST);
mTriangle = new Triangle(mView);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//設(shè)置OpenGLES視口。
GLES20.glViewport(0,0,width,height);
float r = (float)width/height;
//設(shè)置平截頭體,并給投影矩陣賦值。(frustumM方法賦值的投影矩陣為透視投影,見(jiàn)圖透視投影)
Matrix.frustumM(Triangle.sProjMatirx,0,
-r,r,-1,1,1,10);
//設(shè)置平截頭體,并給投影矩陣賦值。(orthoM方法賦值的投影矩陣為正交投影,見(jiàn)圖:正交投影)
//Matrix.orthoM(Triangle.sProjMatirx,0,
// -r,r,-1,1,1,10);
//設(shè)置攝像機(jī)位置(觀察點(diǎn)位置),
Matrix.setLookAtM(Triangle.sVMatrix,0,
0,0,3,
0,0,0,
0,1,0);
}
@Override
public void onDrawFrame(GL10 gl) {
//清除顏色緩存,注釋部分為深度緩存。
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT/*|GLES20.GL_DEPTH_BUFFER_BIT*/);
mTriangle.draw();
}
}
}

透視投影.png

正交投影.png
2. 創(chuàng)建頂點(diǎn)著色器(triangle_ver.glsl)
uniform mat4 u_ProjMatirx;//投影矩陣,通過(guò)程序傳進(jìn)來(lái)。
uniform mat4 u_VMatirx;//攝像機(jī)(觀察點(diǎn))矩陣,通過(guò)程序傳進(jìn)來(lái)。
uniform mat4 u_MMatirx;//變換矩陣,通過(guò)程序傳進(jìn)來(lái)。
attribute vec4 a_position;//頂點(diǎn)數(shù)據(jù),通過(guò)程序傳進(jìn)來(lái)。
attribute vec4 a_color;//頂點(diǎn)顏色數(shù)據(jù),通過(guò)程序傳進(jìn)來(lái)。
varying vec4 v_color;//傳遞給片元著色器的頂點(diǎn)顏色數(shù)據(jù)。
void main() {
//這里解釋下總變換矩陣的作用:a_position是物體坐標(biāo),
//而展示到屏幕上需要把物體坐標(biāo)轉(zhuǎn)換為世界坐標(biāo)(下圖會(huì)解釋變換過(guò)程)。
//注意矩陣順序不能錯(cuò),矩陣叉乘的特性。
gl_Position = u_ProjMatirx * u_VMatirx * u_MMatirx * a_position;
//將頂點(diǎn)顏色數(shù)據(jù)通過(guò)共享變量,傳遞給片元著色器。
v_color = a_color;
}

矩陣變換過(guò)程.png
上圖摘自OpenGL坐標(biāo)系統(tǒng)
創(chuàng)建片元著色器
//片元著色器中需要制定精度
precision mediump float;
varying vec4 v_color;
void main() {
//將頂點(diǎn)顏色數(shù)據(jù)賦給片元著色器內(nèi)部,進(jìn)行柵格化。
gl_FragColor = v_color;
}
3.創(chuàng)建三角形類(lèi)(Triangle)
內(nèi)容比較多,簡(jiǎn)單說(shuō)一下步驟:
- 初始化頂點(diǎn)數(shù)據(jù)
- 初始化著色器(包含-獲取著色器屬性(可選))
- 使用OpenGL ES 2.0繪制三角形(包含-變換矩陣初始化,著色器屬性賦值)
package com.linuxpara.gles20example.shader;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import com.linuxpara.gles20example.util.ShaderUtils;
import java.nio.FloatBuffer;
/**
* Date: 2017/12/11
* *************************************************************
* Auther: 陳占洋
* *************************************************************
* Email: zhanyang.chen@gmail.com
* *************************************************************
* Description:
*/
public class Triangle {
private static final String TAG = "Triangle";
//投影矩陣
public static float[] sProjMatirx = new float[16];
//攝像機(jī)矩陣
public static float[] sVMatrix = new float[16];
//變化矩陣
public static float[] sMMatrix = new float[16];
private FloatBuffer mVerBuffer;
private int mVerSize;
private FloatBuffer mColorBuffer;
private int mProgram;
private int a_position;
private int a_color;
private int u_mMatirx;
private int u_projMatrix;
private int u_vMatirx;
public Triangle(GLSurfaceView view) {
initVerData();
initShader(view);
}
/**
* 初始化頂點(diǎn)數(shù)據(jù)
*/
private void initVerData() {
float[] ver = {
//三角形一共三個(gè)頂點(diǎn)
-0.5f, -1f, -3f,//第一個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
0.5f, -1f, -3f,//第二個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
0f, 1f, -3f,//第三個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
};
//頂點(diǎn)個(gè)數(shù)
mVerSize = ver.length / 3;
//頂點(diǎn)緩存
mVerBuffer = ShaderUtils.getFloatBuffer(ver);
float[] color = {
//頂點(diǎn)的顏色
1, 0, 0, 1,//第一個(gè)頂點(diǎn)的顏色RGBA
0, 1, 0, 1,//第二個(gè)頂點(diǎn)的顏色RGBA
0, 0, 1, 1,//第三個(gè)頂點(diǎn)的顏色RGBA
};
//頂點(diǎn)顏色緩存
mColorBuffer = ShaderUtils.getFloatBuffer(color);
}
/**
* 初始化著色器
* @param view
*/
private void initShader(GLSurfaceView view) {
String verSource = ShaderUtils.getSourceFromAsset("triangle_ver.glsl", view.getResources());
String fragSource = ShaderUtils.getSourceFromAsset("triangle_frag.glsl", view.getResources());
mProgram = createProgram(verSource, fragSource);
if (mProgram == 0){
Log.i(TAG, "initShader: 創(chuàng)建著色器程序失?。。?!");
}
//獲取shader中的投影矩陣變量。
u_projMatrix = GLES20.glGetUniformLocation(mProgram, "u_ProjMatirx");
//獲取shader中的攝像機(jī)矩陣變量
u_vMatirx = GLES20.glGetUniformLocation(mProgram, "u_VMatirx");
//獲取shader中的變換矩陣變量
u_mMatirx = GLES20.glGetUniformLocation(mProgram, "u_MMatirx");
//獲取shader中的頂點(diǎn)數(shù)據(jù)變量
a_position = GLES20.glGetAttribLocation(mProgram, "a_position");
//獲取shader中的頂點(diǎn)顏色變量
a_color = GLES20.glGetAttribLocation(mProgram, "a_color");
}
/**
* 創(chuàng)建著色器程序。
* @param verSource
* @param fragSource
* @return
*/
protected int createProgram(String verSource,String fragSource){
int verShader = loadShader(GLES20.GL_VERTEX_SHADER, verSource);
if (verShader == 0){
return 0;
}
int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragSource);
if (fragShader == 0){
return 0;
}
int program = GLES20.glCreateProgram();
if (program != 0){
//依賴
GLES20.glAttachShader(program,verShader);
checkGLError("glAttachShader");
GLES20.glAttachShader(program,fragShader);
checkGLError("glAttachShader");
//鏈接
GLES20.glLinkProgram(program);
int[] linked = new int[1];
GLES20.glGetProgramiv(program,GLES20.GL_LINK_STATUS,linked,0);
if (linked[0] == 0){
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
/**
* 加載著色器
* @param shaderType
* @param shaderSource
* @return
*/
private int loadShader(int shaderType,String shaderSource){
//創(chuàng)建shader
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0){
//加載shader源碼
GLES20.glShaderSource(shader,shaderSource);
//編譯源碼
GLES20.glCompileShader(shader);
int[] complied = new int[1];
//獲取shader信息
GLES20.glGetShaderiv(shader,GLES20.GL_COMPILE_STATUS,complied,0);
if (complied[0] == 0){
Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
//刪除shader
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public void checkGLError(String op) {
int error = 0;
if ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR){
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
public void draw() {
//指定使用著色器程序。
GLES20.glUseProgram(mProgram);
//初始化變換矩陣
Matrix.setRotateM(sMMatrix,0,
0,
0,0,-1f);
//給shader中u_ProjMatrix變量賦值
GLES20.glUniformMatrix4fv(u_projMatrix,1,
false,sProjMatirx,0);
//給shader中u_VMatirx變量賦值
GLES20.glUniformMatrix4fv(u_vMatirx,1,
false,sVMatrix,0);
//給shader中u_MMatirx變量賦值
GLES20.glUniformMatrix4fv(u_mMatirx,1,
false,sMMatrix,0);
//給shader中a_position變量賦值
GLES20.glVertexAttribPointer(a_position,3,GLES20.GL_FLOAT,
false,0,mVerBuffer);
//給shader中a_color變量賦值
GLES20.glVertexAttribPointer(a_color,4,GLES20.GL_FLOAT,
false,0,mColorBuffer);
//開(kāi)啟頂點(diǎn)數(shù)據(jù)
GLES20.glEnableVertexAttribArray(a_position);
GLES20.glEnableVertexAttribArray(a_color);
//繪制頂點(diǎn)數(shù)據(jù)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,mVerSize);
//關(guān)閉頂點(diǎn)數(shù)據(jù)
GLES20.glDisableVertexAttribArray(a_position);
GLES20.glDisableVertexAttribArray(a_color);
}
}
用到的ShaderUtils工具類(lèi)
代碼比較簡(jiǎn)單,不做過(guò)多說(shuō)明
package com.linuxpara.gles20example.util;
import android.content.res.Resources;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
/**
* Date: 2017/12/11
* *************************************************************
* Auther: 陳占洋
* *************************************************************
* Email: zhanyang.chen@gmail.com
* *************************************************************
* Description:
*/
public class ShaderUtils {
/**
* 獲取浮點(diǎn)類(lèi)型緩存
*
* @param buffer
* @return
*/
public static FloatBuffer getFloatBuffer(float[] buffer) {
FloatBuffer floatBuffer = ByteBuffer.allocateDirect(buffer.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(buffer);
floatBuffer.position(0);
return floatBuffer;
}
/**
* 獲取int類(lèi)型緩存
*
* @param buffer
* @return
*/
public static IntBuffer getIntBuffer(int[] buffer) {
IntBuffer intBuffer = ByteBuffer.allocateDirect(buffer.length * 4)
.order(ByteOrder.nativeOrder())
.asIntBuffer()
.put(buffer);
intBuffer.position(0);
return intBuffer;
}
/**
* 獲取字節(jié)類(lèi)型緩存
*
* @param buffer
* @return
*/
public static ByteBuffer getByteBuffer(byte[] buffer) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(buffer.length)
.order(ByteOrder.nativeOrder())
.put(buffer);
byteBuffer.position(0);
return byteBuffer;
}
/**
* 從資產(chǎn)目錄中獲取shader代碼
*
* @param fileName
* @return
*/
public static String getSourceFromAsset(String fileName, Resources resources) {
InputStream in = null;
ByteArrayOutputStream baos = null;
String result = "";
try {
in = resources.getAssets().open(fileName);
baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
baos.write(buf, 0, len);
}
String source = baos.toString("UTF-8");
result = source.replaceAll("\\r\\n", "\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
/**
* 打印數(shù)組類(lèi)型的矩陣
* @param <T>
* @param matrix
* @param column
* @return
*/
public static <T> String printFloatMatrixArray(float[] matrix, int column) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < matrix.length; i++) {
if (i%column == 0){
result.append("\n");
}else {
result.append(",");
}
result.append(matrix[i]);
}
return result.toString();
}
}
補(bǔ)充:以下圖片摘自極客學(xué)院。
GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,mVerSize);中繪制類(lèi)型解釋。
GL_POINTS:繪制獨(dú)立的點(diǎn)。

gl-points.png
GL_LINES:頂點(diǎn)兩兩連接,為多條線段構(gòu)成。

gl_lines.png
GL_LINE_STRIP:繪制一系列線段。

gl_line_strip.png
GL_LINE_LOOP:類(lèi)同上,但是首尾相連,構(gòu)成一個(gè)封閉曲線。

gl_line_loop.png
GL_TRIANGLES:每隔三個(gè)頂點(diǎn)構(gòu)成一個(gè)三角形,為多個(gè)三角形組成。

gl_triangles.png
GL_TRIANGLE_STRIP:每相鄰三個(gè)頂點(diǎn)組成一個(gè)三角形,為一系列相接三角形構(gòu)成。

gl_triangle_strip.png
GL_TRIANGLE_FAN:以一個(gè)點(diǎn)為三角形公共頂點(diǎn),組成一系列相鄰的三角形。

gl_triangle_fan.png