Android 创建一个 OpenGL 程序
这是在 Android 设备上构建 OpenGL 程序系列文章的第一篇,本文的主要目的是说明如何构建一个简单的 OpenGL 程序。使用 OpenGL ES 2.0 进行开发。
本系列文章的示例代码地址:https://github.com/cmder/Demos/tree/main/OpenGLProject
这个程序非常简单:初始化 OpenGL 并不停地清空屏幕。
为什么要不停地清空屏幕呢?因为在每一帧中,我们都需要重新绘制场景。如果不清空屏幕,上一帧的内容会残留在屏幕上,导致视觉上的混乱。通过不断地清空屏幕,我们可以确保每一帧都是干净的,避免了这种问题。
使用 GLSurfaceView 初始化 OpenGL。GLSurfaceView 会处理 OpenGL 初始化过程中比较基本的操作,如配置显示设备(display) 以及在后台线程中渲染,渲染是在显示设备中一个称为 “surface” 的特定区域完成的,有时也成为视口(viewport)。
GLSurfaceView 提供了很多方法和 Android Activity 的生命周期方法关联,例如在 Activity 的 onPause() 方法中暂停渲染,在 onResume() 方法中恢复渲染等。
GLSurfaceView 实际上为自己创建了一个窗口(Window),并在视图层次(View Hierarchy)上穿了个“洞”,让底层的 OpenGL Surface 显示出来。对于大多数应用程序来说,这种方式足够简单且有效。但是 GLSurfaceView 与常规视图(view)不同,它没有动画或者变形特效,因为 GLSurfaceView 是窗口(Window)的一部分。
从 Android 4.0 Ice Cream Sandwich 开始,Android 提供了一个纹理视图(TextureView),它可以渲染 OpenGL 内容而不用创建单独的窗口或打洞了,这就意味着,TextureView 可以与其他视图(如 ImageView、Button 等)一起使用,支持动画和变形效果。但是,TextureView 没有内置的 OpenGL 初始化操作,想要使用 TextureView,一种方法是执行自定义的 OpenGL 初始化,并在 TextureView 的 Surface 中进行渲染,另一种方法是把 GLSurfaceView 的源代码拿出来,把它适配到 TextureView 中。
创建 GLSurfaceView 实例: 1
glSurfaceView = GLSurfaceView(this)
检查系统是否支持 OpenGL ES 2.0: 1
2
3
4
5
6
7
8
9
10val activityManager: ActivityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
val configurationInfo: ConfigurationInfo = activityManager.deviceConfigurationInfo
val supportsEs2: Boolean =
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")))
为 OpenGL ES 2.0 配置渲染表面: 1
2
3
4
5
6
7
8
9
10
11if (supportsEs2) {
glSurfaceView.setEGLContextClientVersion(2)
glSurfaceView.setRenderer(OpenGLProjectRenderer())
rendererSet = true
} else {
Toast.makeText(this, "This device does not support OpenGL ES 2.0.",
Toast.LENGTH_LONG).show();
return;
}
setContentView(glSurfaceView)
处理 Android Activity 生命周期的事件: 1
2
3
4
5
6
7
8
9
10
11
12
13override fun onPause() {
super.onPause()
if (rendererSet) {
glSurfaceView.onPause()
}
}
override fun onResume() {
super.onResume()
if (rendererSet) {
glSurfaceView.onResume()
}
}
创建 Render 类: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class OpenGLProjectRenderer : GLSurfaceView.Renderer {
/**
* 程序第一次被运行的时候,Surface 被创建, GLSurfaceView 会调用这个方法。
* 当设备被唤醒或者用户从其他 Activity 切换回来时,也会调用这个方法。
*/
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 设置清空屏幕用的颜色
glClearColor(1.0f, 0.0f, 0.0f, 0.0f)
}
/**
* Surface 创建后,每次 Surface 尺寸发生变化时,GLSurfaceView 会调用这个方法。
* 横屏、竖屏来回切换的时候,Surface 的宽高会发生变化。
*/
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 设置视口大小,告诉 OpenGL 可以用来渲染的 surface 的大小
glViewport(0, 0, width, height);
}
/**
* 当绘制一帧的时候,GLSurfaceView 会调用这个方法。在这个方法中,我们一定要绘制一些东西,
* 即使没有任何可绘制的内容,也要调用 glClear() 方法清空颜色缓冲区。因为在这个方法返会后,
* 渲染缓冲区会被交换并显示在屏幕上,如果什么都不绘制,屏幕上会残留上一帧的内容。
*/
override fun onDrawFrame(gl: GL10?) {
// 擦除屏幕的所有颜色,并用 glClearColor 定义的颜色填充
glClear(GL_COLOR_BUFFER_BIT);
}
}
GLSurfaceView
会在一个单独的线程中调用渲染器的方法。默认情况下,GLSurfaceView
会以显示设备的刷新率不断进行渲染。它也可以配置为按请求渲染,只需要用调用GLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY)
即可。
GLSurfaceView 在后台线程中执行渲染,就必须要小心,只能在这个线程中进行 OpenGL 的绘制操作,不能在主线程中进行。两个线程间的通信可以用如下方法:在主线程中的 GLSurfaceView.queueEvent() 方法中提交一个 Runnable 任务,在后台渲染线程中执行。渲染线程可以调用 Activity.runOnUiThread() 方法来传递事件(event)给主线程。
总结 :本文介绍了如何在 Android 中创建一个 OpenGL 程序和清空屏幕的操作。