1.原理,类的继承关系,优缺点

Android使用opengl来绘制图形时主要依赖于GLSurfaceView,它的绘制流程如下

获取EGLDisplay对象
初始化与EGLDisplay 之间的连接。
获取EGLConfig对象
创建EGLContext 实例
创建EGLSurface实例
连接EGLContext和EGLSurface.
使用GL指令绘制图形
断开并释放与EGLSurface关联的EGLContext对象
删除EGLSurface对象
删除EGLContext对象

终止与EGLDisplay之间的连接。

下面看一下GLSurfaceView类的继承关系

java.lang.Object
   ↳ android.view.View
    ↳ android.view.SurfaceView
    ↳ android.opengl.GLSurfaceView

Android APP如果需要使用OpenGl制图,如果在java层实现,一般是GLSurfaceView来显示出OpenGl制图,GLSurfaceView的特点:

1.管理一个平面,这个平面是一个特殊的内存块,它可以和Android视图系统混合.2.管理一个EGL显示,它能够让OpenGL渲染到一个平面.3.接受一个用户提供的实际显示的Renderer对象.4.使用一个专用线程去渲染从而和UI线程解耦.5.支持连续的渲染和丰富的动画效果.


通过继承关系我们可以发现它是继承自View的,我们都知道View的绘制原理,包括测量,创建画布和绘制。那么GLSurfaceView的原理其实是一样的,只是它的坐标系多了一个z轴,来实现3D的效果。下面是

GLSurfaceViewSurfaceView,View各自的优缺点

View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。

SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。

GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,OpenGL专用。

2.GLSurfaceView的绘制流程

Android自定义控件,有了视图View、画布Canvas、画笔Paint,方能绘制炫彩多姿的各种控件。那么对于OpenGL的三维绘图来说,也同样需要具备这三种要素,分别是GLSurfaceView、GLSurfaceView.Renderer和GL10,其中GLSurfaceView继承自表面视图SurfaceView,对应于二维绘图的View;GLSurfaceView.Renderer是三维图形的渲染器,对应于二维绘图的Canvas;最后一个GL10自然相当于二维绘图的Paint了。有了GLSurfaceView、GLRender和GL10这三驾马车,Android才能实现OpenGL的三维图形渲染功能。

使用GLSurfaceView来绘制图形,主要是实现一个Renderer接口,来实现里面的方法,包括

1、onSurfaceCreated函数在GLSurfaceView创建时调用,相当于自定义控件的构造函数,一样可在此进行三维绘图的初始化操作;
2、onSurfaceChanged函数在GLSurfaceView创建、恢复与改变时调用,在这里不但要定义三维空间的大小,还要定义三维物体的方位,所以该函数相当于完成了自定义控件的onMeasure和onLayout两个函数的功能;
3、onDrawFrame顾名思义跟自定义控件的onDraw函数差不多,onDraw函数用于绘制二维图形的具体形状,而onDrawFrame函数用于绘制三维图形的具体形状;

3.使用过程的主体render

   
@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {    //初始化    gl.glClearColor(0f, 0f, 0f, 0f);    // 启动阴影平滑    gl.glShadeModel(GL10.GL_SMOOTH);}
@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {
   
// 设置输出屏幕大小    
gl.glViewport(0, 0, width, height);
// 设置投影矩阵,对应gluPerspective(调整相机)、glFrustumf(调整透视投影)、glOrthof(调整正投影)    
gl.glMatrixMode(GL10.GL_PROJECTION);
// 设置透视图视窗大小
    GLU.gluPerspective(gl, 40, (float) width / height, 0.1f, 20.0f);
   
//选择模型观察矩阵
    gl.glMatrixMode(GL10.GL_MODELVIEW);
   
// 重置投影矩阵,即去掉所有的平移、缩放、旋转操作

    gl.glLoadIdentity();

    }

   
@Overridepublic void onDrawFrame(GL10 gl) {
//具体的绘制的过程
   
// 清除屏幕和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 重置当前的模型观察矩阵gl.glLoadIdentity();// 设置画笔颜色gl.glColor4f(0f, 1f, 0f, 1f);//gl.glColor4x(0,0,1,1);// 设置镜头的方位GLU.gluLookAt(gl, 5.0f, 4.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
前面的三个参数是相机放置的位置在世界坐标系中的坐标,中间三个参数表示被观察的物体在世界坐标系中的位置,后面3个表示相机的

    顶部的朝向,例如第一个如果为1表示相机的顶部向右偏离,类似于人的眼睛观察物体,头顶的朝向

    }

4.OpenGl采用的坐标系采用右手坐标系


定义图形时定义相应的xyz轴上的坐标值(x,y,z)

5.绘制3D图形的流程,涉及到的方法和参数的说明

在坐标系中绘制图形需要知道相关的额坐标点,三维的话应该是xyz对应的值,比如(0,1,0)就代表一个点

不过这个浮点数组并不能直接传给OpenGL处理,因为OpenGL的底层是用C语言实现的,C语言与其它语言(如Java)默认的数据存储方式在字节顺序上可能不同(如大端小端问题),所以其它语言的数据结构必须转换成C语言能够识别的形式,说白了就是翻译。这里面C语言能听懂的数据结构名叫FloatBuffer,于是问题的实质就变成了如何将浮点数组folat[]转换为浮点缓存FloatBuffer,具体的转换过程已经有了现成的模板,开发者只管套进去即可,详细的转换函数代码如下所示:

public FloatBuffer getFloatBuffer(float[] array) {    //初始化字节缓冲区的大小=数组长度*数组元素大小。float类型的元素大小为Float.SIZE,    //int类型的元素大小为Integer.SIZE,double类型的元素大小为Double.SIZE。    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length * Float.SIZE);    //以本机字节顺序来修改字节缓冲区的字节顺序    //OpenGL在底层的实现是C语言,与Java默认的数据存储字节顺序可能不同,即大端小端问题。    //因此,为了保险起见,在将数据传递给OpenGL之前,需要指明使用本机的存储顺序    byteBuffer.order(ByteOrder.nativeOrder());    //根据设置好的参数构造浮点缓冲区    FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();    //把数组数据写入缓冲区    floatBuffer.put(array);    //设置浮点缓冲区的初始位置    floatBuffer.position(0);    return floatBuffer;}

6.demo样例分析,3D图案,动画效果

public class OpenTrangle extends GLSurfaceView implements GLSurfaceView.Renderer {    private FloatBuffer mFloatBuffer, mNextFloatBuffer;    private float mAngle=0.5f;    public OpenTrangle(Context context) {        this(context, null);    }    public OpenTrangle(Context context, AttributeSet set) {        this(context, set, 0);    }    public OpenTrangle(Context context, AttributeSet set, int def) {        super(context, set);        setRenderer(this);    }    //opengl created    @Override    public void onSurfaceCreated(GL10 gl, EGLConfig config) {        //the background of the canvas        gl.glClearColor(1f, 1f, 1f, 0f);        //set the mode of shade        gl.glShadeModel(GL10.GL_SMOOTH);    }    //size change    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        gl.glViewport(0, 0, width, height);        gl.glMatrixMode(GL10.GL_PROJECTION);        //adjust the camera        GLU.gluPerspective(gl, 40, (float) width / height, 0.1f, 20.0f);        gl.glMatrixMode(GL10.GL_MODELVIEW);        gl.glLoadIdentity();    }    //draw the view    @Override    public void onDrawFrame(GL10 gl) {        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);        gl.glColor4f(0, 1, 0, 30);        drawShape(gl, -2f);        if (mAngle > 30) {            mAngle = 0.1f;        } else {            mAngle += 0.1f;    }        }   private void drawShape(GL10 gl, float transLenght) {        for (int i = 0; i < 8; i++) {            float[] mNextPoints = {                    0f, 2f + transLenght * i, 1f,                    2f, 1f + transLenght * i, 1f,                    2f, 0f + transLenght * i, 1f,                    0f, 1f + transLenght * i, 1f,                    -2f, 0f + transLenght * i, 1f,                    -2f, 1f + transLenght * i, 1f            };            gl.glLoadIdentity();            gl.glRotatef(mAngle, -1, 0, 0);            GLU.gluLookAt(gl, 0f, -15f+i, 9f-i, 0.0f, 0f, .0f, 0f, 1f, 0f);
            mNextFloatBuffer = getFloatBuffer(mNextPoints);
   
            // 启用顶点开关            
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            //size: 每个顶点有几个数值描述。必须是2,3 ,4 之一。            //type: 数组中每个顶点的坐标类型。取值:GL_BYTE, GL_SHORT, GL_FIXED, GL_FLOAT。            //stride:数组中每个顶点间的间隔,步长(字节位移)。取值若为0,表示数组是连续的            //pointer:即存储顶点的Buffer   
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mNextFloatBuffer);
            // 用画线的方式将点连接并画出来            //GL_POINTS ————绘制独立的点            //GL_LINE_STRIP————绘制连续的线段,不封闭            //GL_LINE_LOOP————绘制连续的线段,封闭            //GL_LINES————顶点两两连接,为多条线段构成            //GL_TRIANGLES————每隔三个顶点构成一个三角形            //GL_TRIANGLE_STRIP————每相邻三个顶点组成一个三角形            //GL_TRIANGLE_FAN————以一个点为三角形公共顶点,组成一系列相邻的三角形            
            gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, 6);
            //关闭定点开关            
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);       
         }
 }    
public FloatBuffer getFloatBuffer(float[] array) {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length * Float.SIZE);
        byteBuffer.order(ByteOrder.nativeOrder());        FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();        floatBuffer.put(array);        floatBuffer.position(0);       return floatBuffer;    }}

7.总结相关的使用

public class OpenTrangle extends GLSurfaceView implements GLSurfaceView.Renderer {    private FloatBuffer mNextFloatBuffer;    private float mAngle = 0.5f;    public OpenTrangle(Context context) {        this(context, null);    }    public OpenTrangle(Context context, AttributeSet set) {        this(context, set, 0);    }    public OpenTrangle(Context context, AttributeSet set, int def) {        super(context, set);        setRenderer(this);    }    @Override    public void onSurfaceCreated(GL10 gl, EGLConfig config) {        gl.glClearColor(1f, 1f, 1f, 0f);        gl.glShadeModel(GL10.GL_SMOOTH);    }    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        gl.glViewport(0, 0, width, height);        gl.glMatrixMode(GL10.GL_PROJECTION);        GLU.gluPerspective(gl, 40, (float) width / height, 0.1f, 20.0f);        gl.glMatrixMode(GL10.GL_MODELVIEW);        gl.glLoadIdentity();    }    @Override    public void onDrawFrame(GL10 gl) {        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);        gl.glColor4f(0, 1, 0, 30);        drawShape(gl, -2f);        if (mAngle > 30) {            mAngle = 0.1f;        } else {            mAngle += 0.1f;        }    }    private void drawShape(GL10 gl, float transLenght) {        for (int i = 0; i < 8; i++) {            float[] mNextPoints = {                    0f, 2f + transLenght * i, 1f,                    2f, 1f + transLenght * i, 1f,                    2f, 0f + transLenght * i, 1f,                    0f, 1f + transLenght * i, 1f,                    -2f, 0f + transLenght * i, 1f,                    -2f, 1f + transLenght * i, 1f            };            gl.glLoadIdentity();            gl.glRotatef(mAngle, 1, 0, 0);            GLU.gluLookAt(gl, 0f, -15f + i, 9f - i, 0.0f, 0f, .0f, 0f, 1f, 0f);            mNextFloatBuffer = getFloatBuffer(mNextPoints);            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mNextFloatBuffer);            gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, 6);            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);        }    }    public FloatBuffer getFloatBuffer(float[] array) {        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length * Float.SIZE);        byteBuffer.order(ByteOrder.nativeOrder());        FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();        floatBuffer.put(array);        floatBuffer.position(0);        return floatBuffer;    }}
最后在使用的时候作为自定义的view去使用就可以了



更多相关文章

  1. Android图表绘制
  2. View绘制体系(三)——AttributeSet与TypedArray详解
  3. android画图------字节数组转化为图片
  4. [转]关于读取手机号码
  5. Android中GUI系统的Event路由机制
  6. 熟悉Android---canvas第一笔
  7. [置顶] Android加载动态库失败分析
  8. Android(安卓)ListView列表视图的使用方法
  9. - Android深入浅出Binder机制

随机推荐

  1. linux 破解root密码时遇到的问题
  2. SpringBoot热部署加持
  3. WebRTC 的现状和未来:专访 W3C WebRTC Cha
  4. Spring Boot Admin 2.0开箱体验
  5. 从一份配置清单详解Nginx服务器配置
  6. 没想到,这么简单的线程池用法,深藏这么多坑
  7. 用TS+GraphQL查询SpaceX火箭发射数据[每
  8. 6 个珍藏已久 IDEA 小技巧,这一波全部分享
  9. 华熙集团:十年如一日发展传统文化
  10. Java并发编程--ReentrantReadWriteLock