ref:php
Jayway Team Blog中OpenGL ES简明开发教程
https://blog.jayway.com/tag/opengl-es/html
OpenGL ES 开发教程
http://www.guidebee.info/wordpress/%E6%95%99%E7%A8%8B/android-opengl-es-%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8Bjava
http://blog.csdn.net/jason0539/article/details/9164885android
https://developer.android.com/guide/topics/graphics/opengl.htmlgit
http://www.guidebee.info/wordpress/archives/category/opengl-esapi
http://blog.csdn.net/mapdigit/article/details/7526556
http://blog.csdn.net/column/details/apidemoopengl.html数组
本文只关注于如何一步步实如今Android平台下运用OpenGl。 ide
GLSurfaceView是Android应用程序中实现OpenGl画图的重要组成部分。GLSurfaceView中封装了一个Surface。而android平台下关于图像的现实,差很少都是由Surface来实现的。wordpress
有了GLSurfaceView以后,就至关于咱们有了画图的纸。如今咱们所须要作的就是如何在这张纸上画图。因此咱们须要一支笔。
Renderer是GLSurfaceView的内部静态接口,它就至关于在此GLSurfaceView上做画的笔。咱们经过实现这个接口来“做画”。最后经过GLSurfaceView的setRenderer(GLSurfaceView.Renderer renderer)方法,就能够将纸笔关联起来了。函数
实现Renderer须要实现它的三个接口:onSurfaceCreated(GL10 gl, EGLConfig config)、 onSurfaceChanged(GL10 gl, int width, int height)、onDrawFrame(GL10 gl)。下面就这三个接口的具体意义作个简单的介绍。
此方法看名字就知道它是在Surface建立的时候被调用的。所以咱们能够在这个函数的实现中作一些初始化的工做。例如取出文房四宝、铺好画布、调好颜料之类的。它的函数原
型以下:
public abstract void onSurfaceCreated (GL10 gl, EGLConfig config)
第二个参数在文档中没有关于它的任何public方法和域。所以咱们能够不用管它。
第一个参数很是重要。若是说Renderer是画笔的话,那么这个gl参数,就能够说是咱们的手了。如何操做这支画笔,都是它说了算!因此咱们绝大部分时候都是经过这个叫作gl的手来指挥Renderer画图的。
当GLSurfaceView大小改变时,对应的Surface大小也会改变。值得注意的是,在Surface刚建立的时候,它的size实际上是0,也就是说在画第一次图以前它也会被调用一次的。(并且对于不少时候,Surface的大小是不会改变的,那么此函数就只在建立之初被调用一次而已)
原型以下:
public abstract void onSurfaceChanged (GL10 gl, int width, int height)
一样的,画图的手是必需的。
另外值得注意的是,它告诉了咱们这张纸有多高多宽。这点很重要。由于在onSurfaceCreated的时候咱们是不知道纸的宽高的,因此有一些和长宽相关的初始化工做还得在此函数中来作。
好了,咱们的初始化工做作得差很少了,那么如今就是该真刀真枪画画的时候了!此函数就是真正给你画画用的。每调用一次就画一幅图。可能的疑问是这个函数何时会被调
用呢?最开始的时候确定是会被调用的。之后会有两种模式供你选择:
第一种模式(RENDERMODE_CONTINUOUSLY):
接二连三的刷,画完一幅图立刻又画下一幅。这种模式很明显是用来画动画的;
第二种模式(RENDERMODE_WHEN_DIRTY):
只有在须要重画的时候才画下一幅。这种模式就比较节约CPU和GPU一些,适合用来画不常常须要刷新的状况。多说一句,系统如何知道须要重画了呢?固然是你要告诉它……
调用GLSurfaceView的requestRender ()方法,告诉它,你脏了。
这两种模式在什么地方设置呢? GLSurfaceView的setRenderMode(int renderMode)方法。能够供你设置你须要的刷新模式。
仍是来看看这个函数的原型吧: public abstract void onDrawFrame (GL10 gl) 很简单,只有手。
咱们从画一个三角形开始提及:
通过前面的介绍,咱们应该知道如今须要作的事,就是写好Renderer的三个接口方法。
咱们须要从新写一个类实现它,而后重写这三个方法。
class MyRender implements GLSurfaceView.Renderer
OK,笔已经拿好了。“铺好纸”是很是关键的一步。虽然咱们说GLSurfaceView就是咱们做图的纸,可是“铺”好这张纸,却也很是的重要。
下面咱们重点讲下,这纸该怎么铺。OpenGL这张纸可不是通常的纸啊。最起码,它是三维的。然而实际的显示屏幕倒是一个平面。因此这纸还很差“铺”。
首先,不必定整张纸都用来做画吧,Surface不必定所有都用上(固然,通常状况下咱们是所有用上的)。那么咱们须要计划好,哪部分区域用来做画。
gl.glViewport(0, 0, width, height);
根据这里的参数width和height,咱们能够知道这个宽高须要在onSurfaceChanged里得知。
而后,这一步很关键。如何在平面上画三维坐标的点或图形呢?OpenGL有一个坐标系,以下图:
咱们须要将这个坐标系和咱们的GLSurfaceView里的Surface作一个映射关系。
glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-400, 400, -240, 240, 0.3f, 100);
glMatrixMode(GL10.GL_PROJECTION); 是说咱们如今改变的是坐标系与Surface的映射关系(投影矩阵)。
下一句 gl.glLoadIdentity(); 是将之前的改变都清掉(以前对投影矩阵的任何改变)。
glFrustumf (float left, float right, float bottom, float top, float zNear, float zFar) 这个函数很是Powerful。它实现了Surface和坐标系之间的映射关系。它是以透视投影的方式来进行映射的。
透视投影的意思见下图:
映射说明:
一、 前面一个矩形表示的是咱们平面做图的范围。即Surface的做图范围。它有四条边,
咱们将它们暂时命名edgeLeft、edgeRight、edgeTop、edgeBottom。
二、 glFrustumf 参数中的left、right、bottom、top指的是做图范围的四条edge在OpenGL x = -400的位置。同理top、right、bottom的值表示的是edgeRight、edgeTop、edgeBottom这几条边在坐标系中的位置。
三、上面第二点定出了做图范围的x和y的范围。那么对于3D的OpenGL这张纸来讲,咱们还须要定出z的范围。首先,要想象一下,相机或者眼睛在坐标系的哪一个位置?
默认的眼睛的位置在OpenGL坐标的原点处(0,0,0)。视线方向是平行于Z轴往里看。
near表示的是眼睛到做图平面的距离(绝对值哦!),far表示眼睛到最远可见处的平面范围。因而,默认状况下z的做图范围就是在-near到-far的位置。
四、好了,咱们已经肯定好x、y、z方向的做图范围了。回过头来,就能够发现,这张“立体”的纸,是一个方椎体切去头部的平截头体。咱们所画的物体坐标落在这个区域范围内的部分将能够被咱们看到(即在屏幕里画出来)。OK,至此,咱们把纸终于铺好了。
glMatrixMode函数的选项(参数)有后面三种:GL_PROJECTION,GL_MODELVIEW和GL_TEXTURE;
顺便说下,OpenGL里面的操做,不少是基于对矩阵的操做的,好比位移,旋转,缩放,因此,
这里其实说的规范一点就是glMatrixMode是用来指定哪个矩阵是当前矩阵,而它的参数表明要操做的目标:
Android 的 OpenGL 做图,不一样于通常的做图,这点咱们不得不感慨。它将数据和画彻底分开来。例如,咱们要画一个三角形。很显然,三角形有三个点。咱们在画图以前首先要构图,好比每一个点在哪一个地方。咱们将这些数据放在一个一个数组缓冲区中,放好这些数据以后,再统一一块儿画出来。
下面,主要讲下,如何将顶点数据和颜色数据放入符合 Android OpenGL 的数组缓冲区中。
首先咱们要明白的是,OpenGL 是一个很是底层的画图接口,它所使用的缓冲区存储结构是和咱们的 Java 程序中不相同的。Java 是大端字节序(BigEdian),而 OpenGL 所须要的数据是小端字节序(LittleEdian)。因此,咱们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时须要做一些工做。
byte 数据缓冲区
无论咱们的数据是整型的仍是浮点型的,为了完成 BigEdian 到 LittleEdian 的转换,咱们都首先须要一个 ByteBuffer。咱们经过它来获得一个能够改变字节序的缓冲区。
ByteBuffer mBuffer = ByteBuffer.allocateDirect(pointCount*dimension*4);
mBuffer.order(ByteOrder.nativeOrder());
注意,咱们应该用“allocateDirect”来分配内存空间,由于这样才能够 order 排序。最后咱们就能够经过:
resultBuffer = mBuffer.asFloatBuffer();
resultBuffer = mBuffer.asIntBuffer();
将字节缓冲区转化为整型或者浮点型缓冲区了。
有了前面全部的准备工做以后,有一个好消息能够告诉你,咱们终于能够画图了!并且,有了前面这么多工做,真正画图的工做其实比较简单。
3.3.一、清理好你的纸
前面咱们说过了,在画图以前,必定要把纸给清理干净喽:
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
另外,以前咱们在映射坐标系的时候,用了glMatrixMode(GL10.GL_PROJECTION);来指定改变的是投影矩阵。那么如今要画图了,因此咱们须要指定改变的是“视图矩阵”:
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
3.3.二、启用数组
咱们的前面说过,画图的数据都放在数组缓冲区里,最后再一块儿传过来做画。那么咱们首先要告诉 OpenGL,咱们须要用到哪些数组。例如咱们须要顶点数组和颜色数组:
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
3.3.三、指定数组数据
咱们前面已经构造好了咱们的数据缓冲区,floatBuffer(或 IntBuffer)。如今咱们只须要将这个数据缓冲区和对应的功能绑定起来就行了:
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, VertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
这两句话分别绑定了顶点数据数组和颜色数据数组。其中第一个参数表示的是每一个点有几个坐标。例如顶点,有 x、y、z值,因此是 3;而颜色是 r、g、b、a 值,因此是 4。
3.3.四、画图!
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
第一个参数指明了画图的类型——三角形(android 彷佛只支持画三角形、点、线,不支持画多边形)。后面两个参数指明,从哪一个顶点开始画,画多少个顶点。
OK!至此,咱们的第一个三角形就画出来了,来看看效果吧。