Android OpenGL入门

作者: 孤独的根号十二 | 来源:发表于2019-02-14 13:12 被阅读48次

    GLSurfaceView

    GLSurfaceView是Android应用程序中实现OpenGl画图的重要组成部分。GLSurfaceView中封装了一个Surface,而android平台下关于图像的现实,差不多都是由Surface来实现的

    Renderer

    有了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)。下面就这三个接口的具体意义做个简单的介绍。

    onSurfaceCreated

    此方法看名字就知道它是在Surface创建的时候被调用的。因此我们可以在这个函数的实现中做一些初始化的工作。例如取出文房四宝、铺好画布、调好颜料之类的。它的函数原型如下:
    public abstract void onSurfaceCreated (GL10 gl, EGLConfig config)
    第二个参数在文档中没有关于它的任何public方法和域。因此我们可以不用管它。
    第一个参数非常重要。如果说Renderer是画笔的话,那么这个gl参数,就可以说是我们的手了。如何操作这支画笔,都是它说了算!所以我们绝大部分时候都是通过这个叫做gl的手来指挥Renderer画图的。

    onSurfaceChanged

    当GLSurfaceView大小改变时,对应的Surface大小也会改变。值得注意的是,在Surface刚创建的时候,它的size其实是0,也就是说在画第一次图之前它也会被调用一次的。(而且对于很多时候,Surface的大小是不会改变的,那么此函数就只在创建之初被调用一次而已)

    原型如下:
    public abstract void onSurfaceChanged (GL10 gl, int width, int height)
    同样的,画图的手是必需的。
    另外值得注意的是,它告诉了我们这张纸有多高多宽。这点很重要。因为在onSurfaceCreated的时候我们是不知道纸的宽高的,所以有一些和长宽相关的初始化工作还得在此函数中来做。

    onDrawFrame

    好了,我们的初始化工作做得差不多了,那么现在就是该真刀真枪画画的时候了!此函数就是真正给你画画用的。每调用一次就画一幅图。可能的疑问是这个函数什么时候会被调
    用呢?最开始的时候肯定是会被调用的。以后会有两种模式供你选择:

    RENDERMODE_CONTINUOUSLY
    RENDERMODE_WHEN_DIRTY
    第一种模式(RENDERMODE_CONTINUOUSLY):
    连续不断的刷,画完一幅图马上又画下一幅。这种模式很明显是用来画动画的;

    第二种模式(RENDERMODE_WHEN_DIRTY):
    只有在需要重画的时候才画下一幅。这种模式就比较节约CPU和GPU一些,适合用来画不经常需要刷新的情况。多说一句,系统如何知道需要重画了呢?当然是你要告诉它……
    调用GLSurfaceView的requestRender ()方法,告诉它,你脏了。

    这两种模式在什么地方设置呢? GLSurfaceView的setRenderMode(int renderMode)方法。可以供你设置你需要的刷新模式。

    还是来看看这个函数的原型吧: public abstract void onDrawFrame (GL10 gl) 很简单,只有手。

    使用流程

    创建GLSurfaceView

    public class MainActivity extends Activity {
    GLSurfaceView glSurfaceView;
        boolean supportsEs2;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            glSurfaceView=new GLSurfaceView(this);
            setContentView(glSurfaceView);
                 glSurfaceView.setRenderer(new MyRenderer());
    }
    }
    

    创建Renderer

    class MyRenderer implements GLSurfaceView.Renderer {
    //在onSurfaceCreated()中调用glClearColor设置清空屏幕用的颜色
            @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config){
                gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
            }
    //设置视口的大小
            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height){
                gl.glViewport(1,1,width,height);
            }
    //在onDrawFrame()中调用glClear(GL_COLOR_BUFFER_BIT)清空屏幕,会调用glClearColor中定义的颜色来填充整个屏幕
            @Override
            public void onDrawFrame(GL10 gl) {
                gl.glClear(GL_COLOR_BUFFER_BIT);
            }
    

    OpenGL的坐标

    opengl坐标.jpg

    如何改变Surface和OpenGL的坐标的映射关系

    gl.glMatrixMode(GL10.GL_PROJECTION);//改变的是坐标系与Surface的映射关系(投影矩阵)。
    gl.glLoadIdentity();//将以前的改变都清掉(之前对投影矩阵的任何改变)
    gl.glFrustumf(-400, 400, -240, 240, 0.3f, 100);//实现了Surface和坐标系之间的映射关系
    

    glMatrixMode

    glMatrixMode函数的选项(参数)有后面三种:GL_PROJECTION,GL_MODELVIEW和GL_TEXTURE;

    1.GL_PROJECTION,是投影的意思,就是要对投影相关进行操作,也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如glFrustum()或gluPerspective();

    2.GL_MODELVIEW,是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像gluLookAt()这样的函数;

    3.GL_TEXTURE,就是对纹理相关进行操作;

    OpenGL里面的操作,很多是基于对矩阵的操作的,比如位移,旋转,缩放,glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标:
    GL_PROJECTION是对投影矩阵操作;
    GL_MODELVIEW是对模型视景矩阵操作;
    GL_TEXTURE是对纹理矩阵进行随后的操作;

    glFrustumf

    OpenGL的坐标可以通过glFrustumf函数实现Surface和坐标系之间的映射关系。它是以透视投影的方式来进行映射的,该函数原型是:
    glFrustumf (float left, float right, float bottom, float top, float zNear, float zFar)

    透视投影.png
    映射说明:
    1、 前面一个矩形表示的是我们平面作图的范围。即Surface的作图范围。它有四条边
    2.glFrustumf 参数中的left、right、bottom、top指的是作图范围的四条edge在OpenGL x = -400的位置。同理top、right、bottom的值表示的是edgeRight、edgeTop、edgeBottom这几条边在坐标系中的位置
    3.上面第二点定出了作图范围的x和y的范围。那么对于3D的OpenGL这张纸来说,我们还需要定出z的范围。首先,要想象一下,相机或者眼睛在坐标系的哪个位置?
    默认的眼睛的位置在OpenGL坐标的原点处(0,0,0)。视线方向是平行于Z轴往里看。
    near表示的是眼睛到作图平面的距离(绝对值哦!),far表示眼睛到最远可见处的平面范围。于是,默认情况下z的作图范围就是在-near到-far的位置。

    画图

    Android 的 OpenGL 作图,不同于一般的作图。它将数据和画完全分开来。例如,我们要画一个三角形。很显然,三角形有三个点。我们在画图之前首先要构图,比如每个点在哪个地方。我们将这些数据放在一个一个数组缓冲区中,放好这些数据之后,再统一一起画出来。如何将顶点数据和颜色数据放入符合 Android OpenGL 的数组缓冲区中,OpenGL 是一个非常底层的画图接口,它所使用的缓冲区存储结构是和我们的 java 程序中不相同的。Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。
    不管我们的数据是整型的还是浮点型的,为了完成 BigEdian 到 LittleEdian 的转换,我们都首先需要一个 ByteBuffer。我们通过它来得到一个可以改变字节序的缓冲区。

    ByteBuffer mBuffer = ByteBuffer.allocateDirect(pointCountdimension4);
    mBuffer.order(ByteOrder.nativeOrder());

    注意,我们应该用“allocateDirect”来分配内存空间,因为这样才可以 order 排序。最后我们就可以通过:
    resultBuffer = mBuffer.asFloatBuffer();
    resultBuffer = mBuffer.asIntBuffer();
    将字节缓冲区转化为整型或者浮点型缓冲区了。
    启用数组:
    要告诉 OpenGL,我们需要用到哪些数组。例如我们需要顶点数组和颜色数组:
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

    指定数组数据:
    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。

    画图:
    gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

    第一个参数指明了画图的类型——三角形(android 似乎只支持画三角形、点、线,不支持画多边形)。后面两个参数指明,从哪个顶点开始画,画多少个顶点。

    其他:

    1.默认情况下,GLSurfaceView会在单独一条线程中调用渲染器的方法
    2.GLSurfaceView会显示设备的刷新频率不断地渲染,也可以配置按请求渲染,只需要设置GLSurfaceVIew.RENDERMODE_WHEN_DIRTY作为参数调用GLSurfaceView.setRenderMode()
    按请求渲染:

    glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    

    3.GLSurfaceView实例可以调用queueEvent()传递一个Runnable给后台渲染线程,渲染线程可以调用Activity的runOnUITHread()来传递事件给主线程

    glSurfaceView.queueEvent(new Runnable() {
                @Override
                public void run() {
                    Log.d("sun",Thread.currentThread().getName()+"");
                }
            });
    
    
    class MyRenderer implements GLSurfaceView.Renderer {
    
            @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config){
                gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
            }
            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height){
                gl.glViewport(1,1,width,height);
            }
            @Override
            public void onDrawFrame(GL10 gl) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("sun",Thread.currentThread().getName()+"");
                    }
                });
                gl.glClear(GL_COLOR_BUFFER_BIT);
            }
        }
    
    

    4.GLSurfaceView的生命周期要协同好Activity的生命周期,避免造成内存泄漏

        @Override
        protected void onPause() {
            super.onPause();
           glSurfaceView.onPause();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            glSurfaceView.onResume();
        }
    

    5.Android平台上目前有penGL ES1.0和penGL ES2.0,如果我们使用penGL ES2.0,需要判断设备是否支持2.0版本

    ActivityManager activityManager =
                    (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            ConfigurationInfo configurationInfo = activityManager
                    .getDeviceConfigurationInfo();
            if (configurationInfo.reqGlEsVersion >= 0x20000
                    || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
                    && (Build.FINGERPRINT.startsWith("generic")
                    || Build.FINGERPRINT.startsWith("unknown")
                    || Build.MODEL.contains("google_sdk")
                    || Build.MODEL.contains("Emulator")
                    || Build.MODEL.contains("Android SDK built for x86")))){
                //设定使用的版本
                glSurfaceView.setEGLContextClientVersion(2);
            }else{
                glSurfaceView.setEGLContextClientVersion(1);
            }
    

    相关文章

      网友评论

        本文标题:Android OpenGL入门

        本文链接:https://www.haomeiwen.com/subject/qrerjqtx.html