- 基本框架
编写一个类来继承GLSurfaceView.Renderer,并实现其中的三个方法onSurfaceCreated、onSurfaceChanged、onDrawFrame。
/**
* 基本框架
* Created by mazaiting on 2017/8/9.
*/
public class GLRenderer implements GLSurfaceView.Renderer {
/**
* 在窗口被创建时被调用,需要做一些必要的初始化工作:
*/
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 启动阴影平滑
gl.glShadeModel(GL10.GL_SMOOTH);
// 黑色背景,设置清楚屏幕时所用的颜色取值:RGBA.0f-1.0f
gl.glClearColor(0, 0, 0, 1.0f);
// 设置深度缓存--决定哪个物体先画
gl.glClearDepthf(1.0f);
// 启动深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
// 所作深度测试的类型
gl.glDepthFunc(GL10.GL_LEQUAL);
// 告诉系统对透视进行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
}
/**
* 当窗口大小发生改变时被调用,不管窗口的大小是否已经改变,在程序开始时至少运行一次。
*/
@Override public void onSurfaceChanged(GL10 gl, int width, int height) {
float ratio = (float) width / height;
// 设置OpenGL场景的大小
gl.glViewport(0, 0, width, height);
// 设置投影矩阵--增加透视
gl.glMatrixMode(GL10.GL_PROJECTION);
// 重置投影矩阵--恢复原始状态
gl.glLoadIdentity();
// 设置视图的大小
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
// 选择模型观察矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 重置模型观察矩阵
gl.glLoadIdentity();
}
/**
* 在窗口内进行绘图操作。 在绘图之前,需要将屏幕清楚成前面指定的颜色,清楚深度缓存并且重置场景
*/
@Override public void onDrawFrame(GL10 gl) {
// 清楚屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 具体绘图开始...
}
/**
* OpenGL 是一个非常底层的画图接口,它所使用的缓冲区存储结构是和我们的 java 程序中不相同的。
* Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。
* 所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。建立buff的方法如下
**/
public Buffer bufferUtil(int []arr){
// 先初始化buffer,数组的长度*4,因为一个int占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
// 数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
// 将ByteBuffer转换为IntBuffer
IntBuffer mBuffer = qbb.asIntBuffer();
// 将数组设置进去
mBuffer.put(arr);
//mBuffer.position(0);
// 重置
mBuffer.flip();
return mBuffer;
}
}
其中都是一些具体的配置,在主Activity中,我们首先创建出一个GLRenderer对象 ,并创建GLSurfaceView对象,将GLRenderer对象设置在GLSurfaceView对象中。、
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 基础框架
GLSurfaceView.Renderer renderer = new GLRenderer();
GLSurfaceView glView = new GLSurfaceView(this);
glView.setRenderer(renderer);
// 设置布局
setContentView(glView);
}
}
至此,我们将OpenGL开发时所要做的配置已完成,接下来我们在写新的Renderer时,只需要继承在GLRenderer,重写onDrawFrame(GL10 gl)方法即可。
- 绘制多边形
实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();
的此行代码更改为GLSurfaceView.Renderer renderer = new PolygonRenderer();
/**
* 绘制多边形
* Created by mazaiting on 2017/8/9.
*/
public class PolygonRenderer extends GLRenderer{
int one = 0x00010000;
//三角形三个顶点
private int[] triggerBuffer = new int[] {
0, one, 0,//上顶点
- one, -one, 0, //左下点
one, -one, 0,}; //右下点
//正方形的4个顶点
private int[] quaterBuffer = new int[]{
one,one,0,
-one,one,0,
one,-one,0,
-one,-one,0};
@Override public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
// 此函数,就是将画笔沿X轴左移1.5f个单位,Y轴保持不变,Z轴向屏幕里面移动6.0f个单位。
//gl.glTranslatef(-1.5f, 0.0f, -6.0f);
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(-1.5f, 0.0f, -6.0f);// z 轴值小于-1.0f
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 设置三角形
gl.glVertexPointer(3, GL10.GL_FIXED, 0, bufferUtil(triggerBuffer));
// 绘制三角形
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
// 重置当前模型观察矩阵
gl.glLoadIdentity();
// 右移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(1.5f, 0.0f, -6.0f);// z 轴值小于-1.0f
// 设置四边形
gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(quaterBuffer));
// 绘制四边形
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
//取消顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
}
}
效果图:
- 绘制颜色
实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();
的此行代码更改为GLSurfaceView.Renderer renderer = new ColorRenderer();
/**
* 绘制颜色
* Created by mazaiting on 2017/8/9.
*/
public class ColorRenderer extends GLRenderer {
int one = 0x10000;
//三角形三个顶点 (r,g,b,a)
private int[] triggerBuffer = new int[] {
0, one, 0,//上顶点
- one, -one, 0, //左下点
one, -one, 0,}; //右下点
//正方形的4个顶点
private int[] quaterBuffer = new int[]{
one,one,0,
-one,one,0,
one,-one,0,
-one,-one,0};
//三角形的顶点颜色值(r,g,b,a)
private int[] colorBuffer = new int[]{
one,0,0,one,
0,one,0,one,
0,0,one,one,
};
@Override public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(-1.5f,0.0f,-6.0f);
//设置定点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//设置颜色数组 -- 开启颜色渲染功能.
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
// 设置三角形顶点的颜色
gl.glColorPointer(4,GL10.GL_FIXED,0,bufferUtil(colorBuffer));
// 设置三角形顶点
gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(triggerBuffer));
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
//关闭颜色数组 -- 关闭颜色渲染功能.
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
gl.glLoadIdentity();
// 右移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(1.5f,0.0f,-6.0f);
// 设置当前色为蓝色
gl.glColor4f(0.0f,0.5f,1.0f,1.0f);
//设置和绘制正方形
gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(quaterBuffer));
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
//取消顶点数组
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
}
效果图:
- 绘制旋转
实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();
的此行代码更改为GLSurfaceView.Renderer renderer = new RotateRenderer();
/**
* 绘制旋转图形
* Created by mazaiting on 2017/8/9.
*/
public class RotateRenderer extends GLRenderer {
int one = 0x00010000;
//三角形三个顶点
private int[] triggerBuffer = new int[] {
0, one, 0,//上顶点
- one, -one, 0, //左下点
one, -one, 0,}; //右下点
@Override public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
gl.glTranslatef(-1.5f,0.0f,-6.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glRotatef(180f,0,0,0);//旋转180
gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(triggerBuffer));
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glLoadIdentity();
}
}
效果图:
- 绘制三棱锥
实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();
的此行代码更改为GLSurfaceView.Renderer renderer = new PyramidRenderer();
/**
* 四棱锥
* Created by mazaiting on 2017/8/9.
*/
public class PyramidRenderer extends GLRenderer {
int one = 0x10000;
// 四棱锥顶点数组:
private int[] triggerBuffer = new int[] {
0, one, 0,
-one, -one, one,
one, -one, one,
0,one, 0,
one,-one, one,
one, -one, -one,
0, one, 0,
one, -one, -one,
-one, -one, -one,
0, one, 0,
-one, -one, -one,
-one, -one, one
};
/**
* Every vertex has got its own color, described by 4 values
* R(ed)
* G(green)
* B(blue)
* A(lpha)
*/
int colors[] = {
0, 0, 0, one, one, 0, 0, one, one, one, 0, one, 0, one, 0, one, 0, 0, one, one, one, 0, one,
one, one, one, one, one, 0, one, one, one,
};
/**
* The last thing is that we need to describe some Triangles.
* A triangle got 3 vertices.
* The confusing thing is, that it is important in which order
* the vertices of each triangle are described.
* So describing a triangle through the vertices: "0, 4, 5"
* will not result in the same triangle as: "0, 5, 4"
* You probably ask: Why the hell isn't that the same ???
* The reason for that is the call of: "gl.glFrontFace(gl.GL_CW);"
* which means, that we have to describe the "visible" side of the
* triangles by naming its vertices in a ClockWise order!
* From the other side, the triangle will be 100% lookthru!
* You can create a kind of magic mirror with that
**/
byte indices[] = {
0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3,
0, 1, 3, 1, 2
};
@Override public void onSurfaceChanged(GL10 gl, int width, int height) {
super.onSurfaceChanged(gl, width, height);
// 设置透视范围
GLU.gluPerspective(gl, 45.0f, ((float) width) / height, 0.1f, 10f);
}
float xRot = 0.0f;
float yRot = 0.0f;
@Override public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
gl.glMatrixMode(GL10.GL_MODELVIEW);// 切换至模型观察矩阵
gl.glLoadIdentity();// 重置当前的模型观察矩阵
GLU.gluLookAt(gl, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1f, 0f);//设置视点和模型中心位置
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, bufferUtil(triggerBuffer));
gl.glRotatef(xRot,1f,0f,0f);// 绕着(0,0,0)与(1,0,0)即x轴旋转
gl.glRotatef(yRot,0f,1f,0f);
gl.glColor4f(1.0f,0.0f,0.0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
gl.glColor4f(0.0f,1.0f,0.0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 3, 3);
gl.glColor4f(0.0f,0.0f,1.0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 6, 3);
gl.glColor4f(1.0f,0.0f,1.0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 9, 3);
xRot += 1.0f;
yRot += 0.5f;
}
}
效果图:
- 绘制正方体
实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();
的此行代码更改为GLSurfaceView.Renderer renderer = new CubeRenderer();
/**
* 绘制正方体
* Created by mazaiting on 2017/8/10.
*/
public class CubeRenderer extends GLRenderer{
float box[] = new float[] {
// FRONT
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
// BACK
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// LEFT
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
// RIGHT
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
// TOP
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// BOTTOM
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
};
FloatBuffer cubeBuff;
float xRot = 0.0f;
float yRot = 0.0f;
public CubeRenderer(){
cubeBuff = makeFloatBuffer(box);//转换为float数组
}
@Override public void onSurfaceChanged(GL10 gl, int width, int height) {
super.onSurfaceChanged(gl, width, height);
// 设置透视范围
GLU.gluPerspective(gl,45.0f,((float)width)/height,0.1f,10f);
}
@Override public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
gl.glMatrixMode(GL10.GL_MODELVIEW);// 切换至模型观察矩阵
gl.glLoadIdentity();// 重置当前的模型观察矩阵
GLU.gluLookAt(gl,0f,0f,3f,0f,0f,0f,0f,1f,0f);//设置视点和模型中心位置
gl.glVertexPointer(3,GL10.GL_FLOAT, 0,cubeBuff);//设置顶点数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glRotatef(xRot,1f,0f,0f);// 绕着(0,0,0)与(1,0,0)即x轴旋转
gl.glRotatef(yRot,0f,1f,0f);
gl.glColor4f(1.0f,0f,0f,1.0f);//设置颜色,红色
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);//绘制正方形FRONT面
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,4,4);
gl.glColor4f(0f,1.0f,0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,8,4);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,12,4);
gl.glColor4f(0f,0f,1.0f,1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,16,4);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,20,4);
xRot += 1.0f;
yRot += 0.5f;
}
/**
* 将float数组转换为存储在字节缓冲数组
* @param arr
* @return
*/
public FloatBuffer makeFloatBuffer(float[] arr){
// 分配缓冲空间,一个float占4个字节
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(arr.length*4);
// 设置字节顺序,其中ByteOrder.nativeOrder()是获取本机字节顺序
byteBuffer.order(ByteOrder.nativeOrder());
// 转换为float型
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
// 添加数据
floatBuffer.put(arr);
// 设置数组的起始位置
floatBuffer.position(0);
return floatBuffer;
}
}
效果图: