Android OpenGL 开发1 - 初见

Android OpenGL教程第一篇。

版本的选择

在进入到真正的开发之前,我们先来理一理目前android平台上的OpenGL ES的版本。

目前android支持的opengl es有三个版本:1.1, 2.0和3.0. 其中2.0和3.0的开发方式基本一致,不过3.0中的api是2.0的超集。而1.0却和二者很不一致。所以当我们在开发opengl es应用的时候需要先考虑用什么版本的api。选择的时候通常需要考虑以下几个因素:

  • 性能:一般来说2.0/3.0性能会更好一些。
  • 硬件兼容:1.0是在android 1.0中引入的。2.0是android 2.2(API level 8),而3.0是android 4.3(API level 18),3.1是android 5.1(API level 21)
  • Texture support:3.0中对纹理的支持最好。
  • Graphic control:2.0/3.0相对于1.0的最大不同,是开发者可以更好的控制graphic的pipeline。从而创建效率更高的应用。

所以基于以上,除非你特别想兼容更多设备的话,可能你会考虑用1.0(实际上这个规则),除此之外,2.0/3.0将会是你的选择。毕竟这样才是一个更为‘现代化’的gl应用方式。

快速应用

在android中,最便捷的opengl es的开发方式是使用GLSurfaceView以及GLSurfaceView.render. 你所需要做的是通过GLSurfaceView派生出一个自己的View用于显示,然后实现其Render用于控制如何显示。
GLSurfaceView的使用比较简单。

另外,当你需要在应用的layout的某一部分使用opengl的时候,也可以考虑使用TextureView。

如果你急于想看到一个可以运行的例子,那么现在奉上:

这个例子并不做什么有意义的东西,只是在屏幕上画了一块‘红布’。

首先是layout文件,我们只显示一个GLSurfaceView:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <android.opengl.GLSurfaceView
        android:id="@+id/glsurface"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

接下来是Activity代码:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if(hasGLES20()) {

        mGLView = (GLSurfaceView)findViewById(R.id.glsurface);
        mGLView.setEGLContextClientVersion(2);
        mGLView.setPreserveEGLContextOnPause(true);
        mGLView.setRenderer(new GLES20Renderer());
    }

}

private boolean hasGLES20() {
    ActivityManager am = (ActivityManager)
            getSystemService(Context.ACTIVITY_SERVICE);
    ConfigurationInfo info = am.getDeviceConfigurationInfo();
    return info.reqGlEsVersion >= 0x20000;
}

@Override
protected void onResume() {
    super.onResume();

    if(mGLView != null) {
        mGLView.onResume();
    }
}

@Override
protected void onPause() {
    super.onPause();

    if (mGLView != null) {
        mGLView.onPause();
    }
}

这里面需要注意几点:

  • 先判断当前设备是否支持opengl 2.0
  • Activity onResume/onPause的时候需要调用GLSurfaceView相应的onResume/onPause

我们看到在onCreate里面给GLSurfaceView set了自定义的render,现在就来实现:

public class GLES20Renderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }
}

关于GLSurfaceView的renderer,下面是应该注意的知识点以及这里用到的API介绍:

  • Renderer是被GLSurfaceView调用的。
  • Renderer的方法是运行在一个单独线程中的,而非main thread。
  • 默认情况下,GLSurfaceView会以display的refresh rate不停的进行render,但是我们也可以将其配置成触发刷新,用到的api是 GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY)
  • onDrawFrame()中的glClear()目的是clear buffer to preset values,也就是在onSurfaceCreated中设置的值。

总结

怎么样,和opengl的首次接触还很轻松吧?但是和人生一样,若只如初见那就太不真实了:J 到现在为止我们就只写一个了一个最最简单的android openGL程序,而且该程序还有两个明显的缺陷:

  1. 这只是一个简单的2D应用,并没有利用opengl强大的3D功能。
  2. 这还是一个‘传统’的opengl应用,并没有利用‘现代’的opengl概念以及pipeline,比如shader等。

但是别着急,我们在下一篇教程中解决这些问题。