openGL ES繪制的基本流程
1.創(chuàng)建一個(gè)類,繼承自GLSurfaceView
2.創(chuàng)建渲染器Render,實(shí)現(xiàn)三個(gè)方法
3.創(chuàng)建繪制對(duì)象,初始化頂點(diǎn)數(shù)據(jù)
4.開始繪制
準(zhǔn)備工作
首先創(chuàng)建一個(gè)類,繼承自GLSurfaceView,重寫兩個(gè)參數(shù)構(gòu)造方法.跟自定義View一樣,一個(gè)參數(shù)構(gòu)造是為在代碼中new的形式創(chuàng)建對(duì)象,兩個(gè)參數(shù)構(gòu)造是為在XML中創(chuàng)建對(duì)象.
在構(gòu)造方法中建立init方法,準(zhǔn)備初始化工作.
- 創(chuàng)建view對(duì)象
public class EGLView extends GLSurfaceView {
public EGLView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
EGLRenderer renderer = new EGLRenderer();//創(chuàng)建渲染器
setRenderer(renderer);//設(shè)置渲染器
setRenderMode(RENDERMODE_CONTINUOUSLY);//設(shè)置渲染模式為主動(dòng)渲染
}
//創(chuàng)建渲染器Rander對(duì)象
private class EGLRenderer implements Renderer {
private SanJiaoXing sanJiaoXing;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0, 1, 1, 1);//設(shè)置背景色
//也在這里對(duì)繪制對(duì)象進(jìn)行初始化
//此處以三角形為例,繪制其他,替換掉即可
sanJiaoXing = new SanJiaoXing();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);//設(shè)置視口為整個(gè)屏幕,前兩參數(shù)是屏幕原點(diǎn),后兩個(gè)參數(shù)為屏幕寬高
gl.glMatrixMode(GL10.GL_PROJECTION);//設(shè)置投影矩陣
gl.glLoadIdentity();//使矩陣生效
float r = (float) width / height;//獲得寬高比
gl.glFrustumf(-r, r, -1, 1, 1, 20);//設(shè)置視角大小
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT | GL10.GL_COLOR_BUFFER_BIT);//清理深度緩存和顏色緩存
gl.glMatrixMode(GL10.GL_MODELVIEW);//設(shè)置模型矩陣
gl.glLoadIdentity();//使矩陣生效
//為防止渲染圖像渲染在近平面之前,我們對(duì)圖像進(jìn)行Z軸平移
gl.glTranslatef(0, 0, -1.5f);//z軸平移1.5
//在這里進(jìn)行繪制
//此處以三角形為例,繪制其他,替換掉即可
sanJiaoXing.drawSelf(gl);
}
}
}
視口大小中,我們獲得的寬高比:
為了計(jì)算方便,我們將高度H設(shè)置為單位長(zhǎng)度1,則寬度應(yīng)為r = W/H.
設(shè)中心點(diǎn)為0,那么,左右上下分別為-r,r,1,-1.
投影分類:正交投影/透視投影
正交投影:沒有近大遠(yuǎn)小效果
透視投影:有近大遠(yuǎn)小效果,視野更寬廣
Render類
我們繼承Render,實(shí)現(xiàn)了三個(gè)方法,分別為
onSurfaceCreated
onSurfaceChanged
onDrawFrame
onSurfaceCreated只在第一次初始化的時(shí)候調(diào)用,初始化結(jié)束后會(huì)調(diào)用onSurfaceChanged.每次渲染時(shí)會(huì)調(diào)用onDrawFrame.
渲染分為主動(dòng)渲染(RENDERMODE_CONTINOUSLY)和等待渲染(RENDERMODE_WHEN_DIRTY).
主動(dòng)渲染就是一直不停的在渲染,會(huì)持續(xù)調(diào)用onDrawFrame進(jìn)行繪制.
等待渲染只有在調(diào)用requestRender()方法時(shí)才會(huì)喚醒onDrawFrame進(jìn)行繪制.
我們?cè)趇nit()方法中對(duì)Render進(jìn)行初始化.
- 在Activity的xml里引用剛才建立的view
<?xml version="1.0" encoding="utf-8"?>
<com.otitan.opengltest.view.EGLView
android:id="@+id/igl_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.otitan.opengltest.MainActivity">
</com.otitan.opengltest.view.EGLView>
- 準(zhǔn)備的工具類(青壞壞版),將數(shù)組放入緩沖區(qū)
public abstract class OpenGLUtils {
public FloatBuffer getFloatbuffer(float[] ver) {
ByteBuffer vbb = ByteBuffer.allocateDirect(ver.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer buffer = vbb.asFloatBuffer();
buffer.put(ver);
buffer.position(0);
return buffer;
}
public ByteBuffer getByteBuffer(byte[] indices) {
//創(chuàng)建三角形構(gòu)造索引數(shù)據(jù)緩沖
ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
return indexBuffer;
}
public IntBuffer getIntBuffer(int[] ver) {
//創(chuàng)建頂點(diǎn)坐標(biāo)數(shù)據(jù)緩存乡摹,由于不同平臺(tái)字節(jié)順序不同直晨,數(shù)據(jù)單元不是字節(jié)的
// 一定要經(jīng)過(guò)ByteBuffer轉(zhuǎn)換,關(guān)鍵是通過(guò)ByteOrder設(shè)置nativeOrder()
//一個(gè)整數(shù)四個(gè)字節(jié)逻卖,根據(jù)最新分配的內(nèi)存塊來(lái)創(chuàng)建一個(gè)有向的字節(jié)緩沖
ByteBuffer vbb = ByteBuffer.allocateDirect(ver.length * 4);
vbb.order(ByteOrder.nativeOrder());//設(shè)置這個(gè)字節(jié)緩沖的字節(jié)順序?yàn)楸镜仄脚_(tái)的字節(jié)順序
IntBuffer intBuffer = vbb.asIntBuffer();//轉(zhuǎn)換為int型緩沖
intBuffer.put(ver);//向緩沖區(qū)中放入頂點(diǎn)坐標(biāo)數(shù)據(jù)
intBuffer.position(0);//設(shè)置緩沖區(qū)的起始位置
return intBuffer;
}
}
- 創(chuàng)建繪制對(duì)象
public class SanJiaoXing extends OpenGLUtils {
public SanJiaoXing() {
init();
}
private void init() {
}
public void drawSelf(GL10 gl) {
}
}
開始繪制
繪制點(diǎn)
import com.otitan.opengltest.utils.OpenGLUtils;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Points extends OpenGLUtils {
private IntBuffer verBuffer;
private IntBuffer colorBuffer;
private ByteBuffer indexBuffer;
public Points () {
init();
}
private void init() {
//頂點(diǎn)數(shù)據(jù)
int UNIT_SIZE = 10000;//比例
int var[] = new int[]{//初始化6個(gè)點(diǎn),每三個(gè)一組(x,y,z坐標(biāo))
-3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
0, -2 * UNIT_SIZE, 0,
-1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
};
verBuffer = getIntBuffer(var);//創(chuàng)建數(shù)據(jù)緩沖
//顏色數(shù)據(jù)
int one = 65536;//支持65536色彩通道
//頂點(diǎn)個(gè)數(shù)=顏色個(gè)數(shù)
//顏色數(shù)據(jù)(RGB A)
int color[] = new int[]{
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0
};
colorBuffer = getIntBuffer(color);
//創(chuàng)建索引數(shù)組
byte index[] = new byte[]{
0, 1, 2, 3, 4, 5
};
indexBuffer = getByteBuffer(index);//注意此處為byte類型
}
//繪制自身
public void drawSelf(GL10 gl) {
//啟用頂點(diǎn)數(shù)組坐標(biāo)
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//啟用頂點(diǎn)顏色數(shù)組
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//設(shè)置畫筆,給畫筆設(shè)置頂點(diǎn)數(shù)據(jù)
gl.glVertexPointer(3,//verBuffer中多少個(gè)數(shù)據(jù)是一個(gè)頂點(diǎn)
GL10.GL_FIXED,//數(shù)據(jù)類型-這個(gè)代表整形
0,//頂點(diǎn)間隔,默認(rèn)0
verBuffer );//頂點(diǎn)數(shù)據(jù)
//給畫筆指定頂點(diǎn)顏色數(shù)據(jù)
gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同頂點(diǎn)數(shù)據(jù)
gl.glPointSize(20);//設(shè)置點(diǎn)大小,太小的話看不見
gl.glDrawElements(GL10.GL_POINTS,//繪制類型
6, //繪制頂點(diǎn)個(gè)數(shù),即索引數(shù)組中點(diǎn)個(gè)數(shù),可小于數(shù)組長(zhǎng)度
GL10.GL_UNSIGNED_BYTE, indexBuffer);
}
}
點(diǎn)繪制效果如下:
繪制線段
import com.otitan.opengltest.utils.OpenGLUtils;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Line extends OpenGLUtils {
private IntBuffer verBuffer;
private IntBuffer colorBuffer;
private ByteBuffer indexBuffer;
public Line() {
init();
}
private void init() {
//頂點(diǎn)數(shù)據(jù)
int UNIT_SIZE = 10000;
int var[] = new int[]{
-3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
0, -2 * UNIT_SIZE, 0,
-1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
};
verBuffer = getIntBuffer(var);
//顏色數(shù)據(jù)
int one = 65536;
int color[] = new int[]{
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0
};
colorBuffer = getIntBuffer(color);
//創(chuàng)建索引數(shù)組
byte index[] = new byte[]{
0, 1, 2, 3, 4, 5
};
indexBuffer = getByteBuffer(index);
}
public void drawSelf(GL10 gl) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, verBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
gl.glLineWidth(30);//設(shè)置線寬,線太窄時(shí)看不見
gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
}
}
正常繪制:兩個(gè)點(diǎn)一組進(jìn)行點(diǎn)的繪制禾进,如果只有一個(gè)點(diǎn)就會(huì)舍棄這個(gè)點(diǎn)
正常繪制效果如下:
gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
條帶線:按照頂點(diǎn)順序連接頂點(diǎn)
gl.glDrawElements(GL10.GL_LINE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
條帶線繪制效果如下:
循環(huán)線:按照頂點(diǎn)順序連接頂點(diǎn)朝抖,最后一個(gè)點(diǎn)連接第一點(diǎn)
gl.glDrawElements(GL10.GL_LINE_LOOP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
循環(huán)線效果如下:
繪制三角形
import com.otitan.opengltest.utils.OpenGLUtils;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.opengles.GL10;
public class SanJiaoXing extends OpenGLUtils {
private IntBuffer verBuffer;
private IntBuffer colorBuffer;
private ByteBuffer indexBuffer;
public SanJiaoXing() {
init();
}
private void init() {
//頂點(diǎn)數(shù)據(jù)
int UNIT_SIZE = 10000;
int var[] = new int[]{
-3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
0, -2 * UNIT_SIZE, 0,
-1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
};
verBuffer = getIntBuffer(var);
//顏色數(shù)據(jù)
int one = 65536;
int color[] = new int[]{
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0
};
colorBuffer = getIntBuffer(color);
//創(chuàng)建索引數(shù)組,即繪制點(diǎn)位的順序,此處為順序繪制,亦可亂序繪制
//如1,0,3,2,5,4
//順序不同,繪制效果亦不同
byte index[] = new byte[]{
0, 1, 2, 3, 4, 5
};
indexBuffer = getByteBuffer(index);
}
public void drawSelf(GL10 gl) {
//啟用頂點(diǎn)數(shù)組坐標(biāo)
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//啟用頂點(diǎn)顏色數(shù)組
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//設(shè)置畫筆,給畫筆設(shè)置頂點(diǎn)數(shù)據(jù)
gl.glVertexPointer(3,//verBuffer中多少個(gè)數(shù)據(jù)是一個(gè)頂點(diǎn)
GL10.GL_FIXED,//數(shù)據(jù)類型-這個(gè)代表整形
0,//頂點(diǎn)間隔,默認(rèn)0
verBuffer );//頂點(diǎn)數(shù)據(jù)
//給畫筆指定頂點(diǎn)顏色數(shù)據(jù)
gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同頂點(diǎn)數(shù)據(jù)
//索引法繪制
gl.glDrawElements(GL10.GL_TRIANGLES,//繪制模型(點(diǎn)1,線段3,三角形3)
6,//繪制頂點(diǎn)個(gè)數(shù),即索引數(shù)組中點(diǎn)個(gè)數(shù),可小于數(shù)組長(zhǎng)度
GL10.GL_UNSIGNED_BYTE,indexBuffer);
}
}
三角形繪制效果:
正常效果,三個(gè)點(diǎn)一組罗岖,如果不夠三個(gè)點(diǎn)就會(huì)舍棄多余的點(diǎn).
如下面兩圖,上面是當(dāng)繪制5個(gè)點(diǎn)時(shí)的效果,下面為繪制6個(gè)點(diǎn)時(shí)效果.
gl.glDrawElements(GL10.GL_TRIANGLES, 5, GL10.GL_UNSIGNED_BYTE, indexBuffer);
5個(gè)點(diǎn)效果如下:
gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
6個(gè)點(diǎn)效果如下:
三角形帶:頂點(diǎn)按照順序依次 組成三角形繪制确买,最后實(shí)際形成的是一個(gè)三角型帶恬叹!
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
三角形帶效果如下:
三角形扇面:將第一個(gè)點(diǎn)作為中心點(diǎn)候生,其他點(diǎn)作為邊緣點(diǎn),繪制一系列的組成扇形的三角形
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
三角形扇面效果如下:
以上均為索引法繪制,下面展示下數(shù)組法繪制:
package com.otitan.opengltest.draw;
import com.otitan.opengltest.utils.OpenGLUtils;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.opengles.GL10;
public class SanJiaoXing extends OpenGLUtils {
private IntBuffer verBuffer;
private IntBuffer colorBuffer;
//private ByteBuffer indexBuffer;
public SanJiaoXing() {
init();
}
private void init() {
//頂點(diǎn)數(shù)據(jù)
int UNIT_SIZE = 10000;
int var[] = new int[]{
-3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
0, -2 * UNIT_SIZE, 0,
-1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
};
verBuffer = getIntBuffer(var);
//顏色數(shù)據(jù)
int one = 65536;
int color[] = new int[]{
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0,
one, 0, 0, 0
};
colorBuffer = getIntBuffer(color);
//使用數(shù)組法,索引值就不需要了
//創(chuàng)建索引數(shù)組
//byte index[] = new byte[]{
// 0, 1, 2, 3, 4, 5
//};
//indexBuffer = getByteBuffer(index);
}
public void drawSelf(GL10 gl) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, verBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
//此為索引法繪制
//gl.glDrawElements(GL10.GL_TRIANGLE_FAN, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
//數(shù)組法繪制
gl.glDrawArrays(
GL10.GL_TRIANGLE_FAN,//繪制類型
0, //起始值,即頂點(diǎn)數(shù)據(jù)三個(gè)數(shù)據(jù)為一組,從哪個(gè)組開始繪制
6);//繪制個(gè)數(shù)
}
}
效果和之前的扇面三角形是一樣的,不再重復(fù)展示.上面7種繪制,均可使用數(shù)組法.有興趣可自己嘗試.