前言
這是入門篇,讓大家簡單了解OpenGL ES,并且保證你能繪制出三角形。
能動(dòng)手就不嗶嗶
大家都是小學(xué)生,手把手教學(xué)吧!下面直接上代碼。
MainAcitivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//創(chuàng)建一個(gè)GLSurfaceView
GLSurfaceView glSurfaceView = new GLSurfaceView(this);
glSurfaceView.setEGLContextClientVersion(2);
//設(shè)置自己的Render.Render 內(nèi)進(jìn)行圖形的繪制
glSurfaceView.setRenderer(new GLRenderer());
setContentView(glSurfaceView);
}
GLRenderer
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import com.example.openglcampaign.deep.Constant;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class GLRenderer implements GLSurfaceView.Renderer {
public static final int BYTES_PER_FLOAT = 4;//每個(gè)浮點(diǎn)數(shù):坐標(biāo)個(gè)數(shù)* 4字節(jié)
private FloatBuffer vertexBuffer;//頂點(diǎn)緩沖
private final String vertexShaderCode =//頂點(diǎn)著色代碼
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =//片元著色代碼
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
//頂點(diǎn)的坐標(biāo)系
private static float TRIANGLE_COORDS[] = {
//Order of coordinates: X, Y, Z
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
private int mProgramObjectId;
//在數(shù)組中,一個(gè)頂點(diǎn)需要3個(gè)來描述其位置,需要3個(gè)偏移量
private static final int COORDS_PER_VERTEX = 3;
private static final int COORDS_PER_COLOR = 0;
//在數(shù)組中,描述一個(gè)頂點(diǎn),總共的頂點(diǎn)需要的偏移量。這里因?yàn)橹挥形恢庙旤c(diǎn),所以和上面的值一樣
private static final int TOTAL_COMPONENT_COUNT = COORDS_PER_VERTEX + COORDS_PER_COLOR;
//一個(gè)點(diǎn)需要的byte偏移量。
private static final int STRIDE = TOTAL_COMPONENT_COUNT * BYTES_PER_FLOAT;
// 顏色,rgba 更換顏色
float TRIANGLE_COLOR[] = {0.5176471f, 0.77254903f, 0.9411765f, 1.0f};
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//初始化頂點(diǎn)字節(jié)緩沖區(qū)
ByteBuffer bb = ByteBuffer.allocateDirect(TRIANGLE_COORDS.length * 4);//每個(gè)浮點(diǎn)數(shù):坐標(biāo)個(gè)數(shù)* 4字節(jié)
bb.order(ByteOrder.nativeOrder());//使用本機(jī)硬件設(shè)備的字節(jié)順序
vertexBuffer = bb.asFloatBuffer();// 從字節(jié)緩沖區(qū)創(chuàng)建浮點(diǎn)緩沖區(qū)
vertexBuffer.put(TRIANGLE_COORDS);// 將坐標(biāo)添加到FloatBuffer
vertexBuffer.position(0);//設(shè)置緩沖區(qū)以讀取第一個(gè)坐標(biāo)
//0.簡單的給窗口填充一種顏色
GLES20.glClearColor(1.0f, 0f, 0f, 0f);//rgba
//在創(chuàng)建的時(shí)候,去創(chuàng)建這些著色器
//1.根據(jù)String進(jìn)行編譯。得到著色器id
int vertexShaderObjectId = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShaderObjectId = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
//3.繼續(xù)套路。取得到program
mProgramObjectId = GLES20.glCreateProgram();
//將shaderId綁定到program當(dāng)中
GLES20.glAttachShader(mProgramObjectId, vertexShaderObjectId);
GLES20.glAttachShader(mProgramObjectId, fragmentShaderObjectId);
//4.最后,啟動(dòng)GL link program
GLES20.glLinkProgram(mProgramObjectId);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//在窗口改變的時(shí)候調(diào)用
GLES20.glViewport(0, 0, width, height);//GL視口
}
@Override
public void onDrawFrame(GL10 gl) {
//0.glClear()的唯一參數(shù)表示需要被清除的緩沖區(qū)。當(dāng)前可寫的顏色緩沖
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(mProgramObjectId);
//1.根據(jù)我們定義的取出定義的位置
int vPosition = GLES20.glGetAttribLocation(mProgramObjectId, "vPosition");
//2.開始啟用我們的position
GLES20.glEnableVertexAttribArray(vPosition);
//3.將坐標(biāo)數(shù)據(jù)放入
GLES20.glVertexAttribPointer(
vPosition, //上面得到的id
COORDS_PER_VERTEX, //告訴他用幾個(gè)偏移量來描述一個(gè)頂點(diǎn)
GLES20.GL_FLOAT, false,
STRIDE, //一個(gè)頂點(diǎn)需要多少個(gè)字節(jié)的偏移量
vertexBuffer);
//取出顏色
int uColor = GLES20.glGetUniformLocation(mProgramObjectId, "vColor");
//開始繪制
//設(shè)置繪制三角形的顏色
GLES20.glUniform4fv(
uColor,
1,
TRIANGLE_COLOR,
0
);
//繪制三角形.
//draw arrays的幾種方式
//GL_TRIANGLES三角形
//GL_TRIANGLE_STRIP三角形帶的方式(開始的3個(gè)點(diǎn)描述一個(gè)三角形,后面每多一個(gè)點(diǎn),多一個(gè)三角形)
//GL_TRIANGLE_FAN扇形(可以描述圓形)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, TRIANGLE_COORDS.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
/**
* 加載作色器
*
* @param type 著色器類型
* 頂點(diǎn)著色 {@link GLES20.GL_VERTEX_SHADER}
* 片元著色 {@link GLES20.GL_FRAGMENT_SHADER}
* @param shaderCode 著色代碼
* @return 作色器
*/
private static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);//創(chuàng)建著色器
if (shader == 0) {//加載失敗直接返回
return 0;
}
GLES20.glShaderSource(shader, shaderCode);//加載著色器源代碼
GLES20.glCompileShader(shader);//編譯
return shader;
}
}
到目前位置,你的代碼就可以繪制出來三角形了。
拆解
- loadShader 方法。
/**
* 加載作色器
*
* @param type 著色器類型
* 頂點(diǎn)著色 {@link GLES20.GL_VERTEX_SHADER}
* 片元著色 {@link GLES20.GL_FRAGMENT_SHADER}
* @param shaderCode 著色代碼
* @return 作色器
*/
private static int loadShader(int type, String shaderCode) {
//1.根據(jù)類型,創(chuàng)建著色器
int shader = GLES20.glCreateShader(type);
if (shader == 0) {
//加載失敗直接返回
return 0;
}
//2.加載著色器源代碼
GLES20.glShaderSource(shader, shaderCode);
//3.編譯(往下還有一個(gè)驗(yàn)證,我們先不處理)
GLES20.glCompileShader(shader);
return shader;
}
- onSurfaceCreated 方法
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//初始化頂點(diǎn)字節(jié)緩沖區(qū)
ByteBuffer bb = ByteBuffer.allocateDirect(TRIANGLE_COORDS.length * 4);//每個(gè)浮點(diǎn)數(shù):坐標(biāo)個(gè)數(shù)* 4字節(jié)
bb.order(ByteOrder.nativeOrder());//使用本機(jī)硬件設(shè)備的字節(jié)順序
vertexBuffer = bb.asFloatBuffer();// 從字節(jié)緩沖區(qū)創(chuàng)建浮點(diǎn)緩沖區(qū)
vertexBuffer.put(TRIANGLE_COORDS);// 將坐標(biāo)添加到FloatBuffer
vertexBuffer.position(0);//設(shè)置緩沖區(qū)以讀取第一個(gè)坐標(biāo)
//0.簡單的給窗口填充一種顏色
GLES20.glClearColor(1.0f, 0f, 0f, 0f);//rgba
//在創(chuàng)建的時(shí)候,去創(chuàng)建這些著色器
//1.根據(jù)String進(jìn)行編譯。得到著色器id
int vertexShaderObjectId = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShaderObjectId = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
//3.繼續(xù)套路。取得到program
mProgramObjectId = GLES20.glCreateProgram();
//將shaderId綁定到program當(dāng)中
GLES20.glAttachShader(mProgramObjectId, vertexShaderObjectId);
GLES20.glAttachShader(mProgramObjectId, fragmentShaderObjectId);
//4.最后,啟動(dòng)GL link program
GLES20.glLinkProgram(mProgramObjectId);
}
總結(jié):初始化相應(yīng)的頂點(diǎn)數(shù)據(jù),創(chuàng)建一個(gè)程序?qū)ο蟛㈡溄又鳌?/p>
- onSurfaceChanged 方法
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//在窗口改變的時(shí)候調(diào)用
GLES20.glViewport(0, 0, width, height);//GL視口
}
- onDrawFrame 方法
@Override
public void onDrawFrame(GL10 gl) {
//0.glClear()的唯一參數(shù)表示需要被清除的緩沖區(qū)。當(dāng)前可寫的顏色緩沖
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(mProgramObjectId);
//1.根據(jù)我們定義的取出定義的位置
int vPosition = GLES20.glGetAttribLocation(mProgramObjectId, "vPosition");
//2.開始啟用我們的position
GLES20.glEnableVertexAttribArray(vPosition);
//3.將坐標(biāo)數(shù)據(jù)放入
GLES20.glVertexAttribPointer(
vPosition, //上面得到的id
COORDS_PER_VERTEX, //告訴他用幾個(gè)偏移量來描述一個(gè)頂點(diǎn)
GLES20.GL_FLOAT, false,
STRIDE, //一個(gè)頂點(diǎn)需要多少個(gè)字節(jié)的偏移量
vertexBuffer);
//取出顏色
int uColor = GLES20.glGetUniformLocation(mProgramObjectId, "vColor");
//開始繪制
//設(shè)置繪制三角形的顏色
GLES20.glUniform4fv(
uColor,
1,
TRIANGLE_COLOR,
0
);
//繪制三角形.
//draw arrays的幾種方式
//GL_TRIANGLES三角形
//GL_TRIANGLE_STRIP三角形帶的方式(開始的3個(gè)點(diǎn)描述一個(gè)三角形,后面每多一個(gè)點(diǎn),多一個(gè)三角形)
//GL_TRIANGLE_FAN扇形(可以描述圓形)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, TRIANGLE_COORDS.length / 3);
//禁止頂點(diǎn)數(shù)組的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
總結(jié):設(shè)置窗口和清除顏色緩沖區(qū),加載幾何形狀和繪制圖元。
擴(kuò)展
openGL使用右手坐標(biāo)。Z軸朝向屏幕外。我們看一下X和Y軸。

//頂點(diǎn)的坐標(biāo)系
private static float TRIANGLE_COORDS[] = {
//Order of coordinates: X, Y, Z
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
原本認(rèn)為是等腰的三角形,最后發(fā)現(xiàn)不是,就是因?yàn)樽鴺?biāo)系的問題。
我們看一下歸一化坐標(biāo)系:

我們下一步借助矩陣也可以在手機(jī)上繪制出這樣的三角形。
知識延伸
private final String vertexShaderCode =//頂點(diǎn)著色代碼
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
為什么 vPosition是使用vec4 ,這是一個(gè)比較有意思的問題,一下是百度的答案。
由于3d圖形用到了 4x4的矩陣(4行4列),矩陣乘法要求 nxm * mxp(n行m列 乘 m行p列)才能相乘,注意m是相同的,所以 1x4 * 4x4 才能相乘。
所以是vec4而不是vec3。
至于為什么 4x4 看下那些 投影矩陣的演算過程就知道了。
至于你說的多出來的那一位,
如果是點(diǎn)坐標(biāo)的話是 1.0
position 是位置所以應(yīng)該是 (x,y,z,1.0f)
如果是 方向向量 ,也就是 代表的不是點(diǎn) 而是一個(gè)方向 則是 0.0 ,也就是 (x,y,z,0.0f)。
這也是 要與矩陣進(jìn)行乘法所決定的。
建議學(xué)一些矩陣的運(yùn)算,既然要用到 3d ,學(xué)矩陣是很有必要的,很多計(jì)算用到了矩陣
而且現(xiàn)在的cpu 也提供了 mmx來做矩陣的運(yùn)算,可見矩陣對3d的作用很大。
參考:https://zhidao.baidu.com/question/1817876033322344268.html