openGL ES绘制的基本流程
1.创建一个类,继承自GLSurfaceView
2.创建渲染器Render,实现三个方法
3.创建绘制对象,初始化顶点数据
4.开始绘制
准备工作
首先创建一个类,继承自GLSurfaceView,重写两个参数构造方法.跟自定义View一样,一个参数构造是为在代码中new的形式创建对象,两个参数构造是为在XML中创建对象.
在构造方法中建立init方法,准备初始化工作.
- 创建view对象
public class EGLView extends GLSurfaceView {
public EGLView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
EGLRenderer renderer = new EGLRenderer();//创建渲染器
setRenderer(renderer);//设置渲染器
setRenderMode(RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
}
//创建渲染器Rander对象
private class EGLRenderer implements Renderer {
private SanJiaoXing sanJiaoXing;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0, 1, 1, 1);//设置背景色
//也在这里对绘制对象进行初始化
//此处以三角形为例,绘制其他,替换掉即可
sanJiaoXing = new SanJiaoXing();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);//设置视口为整个屏幕,前两参数是屏幕原点,后两个参数为屏幕宽高
gl.glMatrixMode(GL10.GL_PROJECTION);//设置投影矩阵
gl.glLoadIdentity();//使矩阵生效
float r = (float) width / height;//获得宽高比
gl.glFrustumf(-r, r, -1, 1, 1, 20);//设置视角大小
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT | GL10.GL_COLOR_BUFFER_BIT);//清理深度缓存和颜色缓存
gl.glMatrixMode(GL10.GL_MODELVIEW);//设置模型矩阵
gl.glLoadIdentity();//使矩阵生效
//为防止渲染图像渲染在近平面之前,我们对图像进行Z轴平移
gl.glTranslatef(0, 0, -1.5f);//z轴平移1.5
//在这里进行绘制
//此处以三角形为例,绘制其他,替换掉即可
sanJiaoXing.drawSelf(gl);
}
}
}
视口大小中,我们获得的宽高比:
为了计算方便,我们将高度H设置为单位长度1,则宽度应为r = W/H.
设中心点为0,那么,左右上下分别为-r,r,1,-1.
投影分类:正交投影/透视投影
正交投影:没有近大远小效果
透视投影:有近大远小效果,视野更宽广
Render类
我们继承Render,实现了三个方法,分别为
onSurfaceCreated
onSurfaceChanged
onDrawFrame
onSurfaceCreated只在第一次初始化的时候调用,初始化结束后会调用onSurfaceChanged.每次渲染时会调用onDrawFrame.
渲染分为主动渲染(RENDERMODE_CONTINOUSLY)和等待渲染(RENDERMODE_WHEN_DIRTY).
主动渲染就是一直不停的在渲染,会持续调用onDrawFrame进行绘制.
等待渲染只有在调用requestRender()方法时才会唤醒onDrawFrame进行绘制.
我们在init()方法中对Render进行初始化.
- 在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>
- 准备的工具类(青坏坏版),将数组放入缓冲区
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) {
//创建三角形构造索引数据缓冲
ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
return indexBuffer;
}
public IntBuffer getIntBuffer(int[] ver) {
//创建顶点坐标数据缓存,由于不同平台字节顺序不同,数据单元不是字节的
// 一定要经过ByteBuffer转换,关键是通过ByteOrder设置nativeOrder()
//一个整数四个字节,根据最新分配的内存块来创建一个有向的字节缓冲
ByteBuffer vbb = ByteBuffer.allocateDirect(ver.length * 4);
vbb.order(ByteOrder.nativeOrder());//设置这个字节缓冲的字节顺序为本地平台的字节顺序
IntBuffer intBuffer = vbb.asIntBuffer();//转换为int型缓冲
intBuffer.put(ver);//向缓冲区中放入顶点坐标数据
intBuffer.position(0);//设置缓冲区的起始位置
return intBuffer;
}
}
- 创建绘制对象
public class SanJiaoXing extends OpenGLUtils {
public SanJiaoXing() {
init();
}
private void init() {
}
public void drawSelf(GL10 gl) {
}
}
开始绘制
绘制点
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() {
//顶点数据
int UNIT_SIZE = 10000;//比例
int var[] = new int[]{//初始化6个点,每三个一组(x,y,z坐标)
-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);//创建数据缓冲
//颜色数据
int one = 65536;//支持65536色彩通道
//顶点个数=颜色个数
//颜色数据(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);
//创建索引数组
byte index[] = new byte[]{
0, 1, 2, 3, 4, 5
};
indexBuffer = getByteBuffer(index);//注意此处为byte类型
}
//绘制自身
public void drawSelf(GL10 gl) {
//启用顶点数组坐标
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//启用顶点颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//设置画笔,给画笔设置顶点数据
gl.glVertexPointer(3,//verBuffer中多少个数据是一个顶点
GL10.GL_FIXED,//数据类型-这个代表整形
0,//顶点间隔,默认0
verBuffer );//顶点数据
//给画笔指定顶点颜色数据
gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同顶点数据
gl.glPointSize(20);//设置点大小,太小的话看不见
gl.glDrawElements(GL10.GL_POINTS,//绘制类型
6, //绘制顶点个数,即索引数组中点个数,可小于数组长度
GL10.GL_UNSIGNED_BYTE, indexBuffer);
}
}
点绘制效果如下:
绘制线段
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() {
//顶点数据
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);
//颜色数据
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);
//创建索引数组
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);//设置线宽,线太窄时看不见
gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
}
}
正常绘制:两个点一组进行点的绘制,如果只有一个点就会舍弃这个点
正常绘制效果如下:
gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
条带线:按照顶点顺序连接顶点
gl.glDrawElements(GL10.GL_LINE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
条带线绘制效果如下:
循环线:按照顶点顺序连接顶点,最后一个点连接第一点
gl.glDrawElements(GL10.GL_LINE_LOOP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
循环线效果如下:
绘制三角形
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() {
//顶点数据
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);
//颜色数据
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);
//创建索引数组,即绘制点位的顺序,此处为顺序绘制,亦可乱序绘制
//如1,0,3,2,5,4
//顺序不同,绘制效果亦不同
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,//verBuffer中多少个数据是一个顶点
GL10.GL_FIXED,//数据类型-这个代表整形
0,//顶点间隔,默认0
verBuffer );//顶点数据
//给画笔指定顶点颜色数据
gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同顶点数据
//索引法绘制
gl.glDrawElements(GL10.GL_TRIANGLES,//绘制模型(点1,线段3,三角形3)
6,//绘制顶点个数,即索引数组中点个数,可小于数组长度
GL10.GL_UNSIGNED_BYTE,indexBuffer);
}
}
三角形绘制效果:
正常效果,三个点一组,如果不够三个点就会舍弃多余的点.
如下面两图,上面是当绘制5个点时的效果,下面为绘制6个点时效果.
gl.glDrawElements(GL10.GL_TRIANGLES, 5, GL10.GL_UNSIGNED_BYTE, indexBuffer);
5个点效果如下:
gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
6个点效果如下:
三角形带:顶点按照顺序依次 组成三角形绘制,最后实际形成的是一个三角型带!
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
三角形带效果如下:
三角形扇面:将第一个点作为中心点,其他点作为边缘点,绘制一系列的组成扇形的三角形
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
三角形扇面效果如下:
以上均为索引法绘制,下面展示下数组法绘制:
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() {
//顶点数据
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);
//颜色数据
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);
//使用数组法,索引值就不需要了
//创建索引数组
//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);
//数组法绘制
gl.glDrawArrays(
GL10.GL_TRIANGLE_FAN,//绘制类型
0, //起始值,即顶点数据三个数据为一组,从哪个组开始绘制
6);//绘制个数
}
}
效果和之前的扇面三角形是一样的,不再重复展示.上面7种绘制,均可使用数组法.有兴趣可自己尝试.