OpenGL for Android


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

 
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    //初始化
    gl.glClearColor(0f, 0f, 0f, 0f);
    // 启动阴影平滑
    gl.glShadeModel(GL10.GL_SMOOTH);
}
@Override
public 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();

    }

 
@Override
public 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去使用就可以了



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值