本文是两部分中的第二部分,讨论了将 OpenGL 游戏移植到 Google Android 平台存在的障碍。 在开始游戏植入项目(包括 OpenGL 扩展的差别、浮点支持、纹理压缩格式和 GLU 库)之前,您应该认识到这些障碍。 此外,还介绍了借助 OpenGL ES 如何为英特尔凌动处理器设置 Android 的开发系统,以及如何获得 Android 虚拟设备模拟工具的最佳性能。
本文的第一部分介绍了如何通过软件开发套件(SDK)或 Android 原生开发套件(NDK)在 Android 平台上使用 OpenGL ES,以及如何确定选择何种方法。 本文还介绍了各种 SDK 和 NDK 中的 OpenGL ES 示例应用,以及 Java* 原生接口,这支持您结合使用 Java 和 C/C++ 组件。 此外,还讨论了如何确定应使用 OpenGL ES 版本 1.1 还是 2.0。
OpenGL ES 1.1 应用编程接口(API)是 OpenGL 1.x API 的子集,数十年来一直用于台式机 Linux* 和 Windows* 系统。 同样,OpenGL ES 2.0 API 是台式系统 OpenGL 2.0 API 的子集。 因此,所有的游戏移植项目都应先评估游戏代码使用的 OpenGL 特性集,以确定代码的使用年限并找到需要重新写入的部分,因为使用的 OpenGL 版本与 Android 上要使用的 OpenGL ES 版本不同。
OpenGL 2.0 与 OpenGL ES 2.0 之间的主要区别分为三大类: 几何规范、几何变换以及 OpenGL 着色语言(GLSL)中的变化。 OpenGL 2.0 可提供四种方式来描述几何: 即时模式、显示列表、顶点阵列和顶点缓冲区对象。 OpenGL ES 2.0 不支持即时模式(glBegin
和 glEnd
块 )和显示列表,因此,您必须使用顶点阵列或顶点缓冲区对象来描述几何。
OpenGL 2.0 具备加载模式视图、投影和纹理矩阵的函数,以及联合这些标准的便捷函数,如 glTranslate
、glRotate
和 glScale
。 OpenGL ES 2.0 不具备上述函数,也不具备投影相关函数,因为固定函数管道被着色器编程模块替换。 对于 OpenGL ES 2.0,应在顶点着色器中执行标准和投影计算。
此外,面向 OpenGL ES 2.0 (GLSL ES)的着色器语言也是台式机 OpenGL 2.0 (GLSL) 着色器语言的子集。 主要区别是,GLSL 具备内置变量来访问顶点着色器固定函数管道中的声明(如 gl_ModelViewMatrix
)。 因为 OpenGL ES 2.0 不支持固定函数管道,所以 GLSL ES 中不支持这些变量。
OpenGL ES 2.0 中不具备下列 OpenGL 2.0 特性:
注: 关于 OpenGL 2.0 和 OpenGL ES 2.0 之间区别的更多信息,请参阅 http://software.intel.com/en-us/articles/targeting-3d-applications-for-mobile-devices-powered-by-opengl-and-opengl-es。
一般而言,使用 OpenGL 从其他嵌入式系统向 Android 移植 OpenGL ES 游戏比从台式机系统移植更简单,因为大部分的嵌入式系统都与 Android 使用相同的 OpenGL ES 1.1 或 2.0 规范。 当从其他嵌入式系统移植 OpenGL ES 代码时,一般而言,最大的区别在于支持的 OpenGL ES 扩展 — 尤其与压缩纹理格式相关时。 另一个值得注意的地方是嵌入式系统间浮点支持的区别。
名为 Common 和 Common-Lite 的两个版本提供了 OpenGL ES 1.0 和 1.1 的早期实施。 Common 版本适用于有浮点硬件的处理器;Common-Lite 版本适用于没有浮点硬件的处理器,因此必须使用固定点计算。 因为 Android 需要包含浮点支持的处理器,如英特尔凌动处理器,所以无需使用固定点,因此 Google 为 Android 提供的系统映像仅包括面向 OpenGL ES 1.1 的 Common(浮点) 驱动程序。 面向 OpenGL ES 2.0 的 Khronos 标准仅可用于浮点。 如果您正在从其他使用 Common-Lite 版 OpenGL ES 的嵌入式系统移植 OpenGL ES 1.0 或 1.1 游戏时,这非常重要,因为您必须将代码中的所有固定点计算转换为浮点(因为 Android 仅支持 Common 版 OpenGL ES)。 一般而言,嵌入式 Linux 系统上的 Common 和 Common-Lite 驱动程序分别以类似 libGLESv1_CM.so
和 libGLESv1_CL.so
的方式命名。
如果您正在从台式机应用移植 OpenGL 代码,则可能已经使用了浮点代码。 但是,请注意代码中任何双精度浮点的使用。 Android 支持双精度浮点,但是 OpenGL ES 仅支持单精度浮点。 因此,必须先将所有双精度值转换为单精度,然后再传递到 OpenGL ES 1.1 或 2.0。
最大的障碍之一是,跨平台移植 OpenGL 代码来自于应用代码对 OpenGL 扩展和嵌入式系统图形库(EGL)的使用。 您应在移植流程的早期评估传统代码中使用了哪些扩展,并将其与目标 Android 设备上实际使用的扩展进行比较。 扩展是 GPU 开发人员暴露其 GPU 在标准 OpenGL 规则之外的特定特性的方式。 传统代码中使用的不适用于目标平台的所有扩展都将要求使用新代码,以消除对扩展的需求或使用不同的扩展。
支持的扩展在不同版本的 OpenGL 中差别很大,而且由于 GPU 架构的区别,即使在不同的 Android 平台上,支持的扩展也不相同。 英特尔凌动处理器采用 PowerVR* GPU,这是当今移动设备上使用最普遍的 GPU 架构。 Android 应用不应假定 OpenGL ES 的任何扩展适用于任何设备。 功能良好的应用可在运行时通过查询 OpenGL ES 获得可用扩展列表。 您可以使用下列调用从 OpenGL ES 和 EGL 驱动程序中获得可用扩展列表:
glGetString(GL_EXTENSIONS);
eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
调用扩展所需的所有函数和枚举名称均在 glext.h
、 gl2ext.h
和 eglext.h
头文件中定义,因此,您的应用在运行时确认某个扩展可用后,您便可直接调用它。
Google Play 网站提供了一个有用的应用 — OpenGL Extensions Viewer (GLview),可为运行它的任何 Android 设备执行该查询和显示返回信息。 您可以使用类似 GLview 的工具亲自对多种 Android 设备进行测试,以确认可用扩展。 此外,Khronos 可为所有已知 OpenGL ES 扩展维护 OpenGL ES API 注册表 — 重要参考。
OpenGL ES 最重要的扩展可提供对压缩纹理的支持。 这是大部分 3D 游戏用来降低内存要求和提升性能的一项重要技术,但是配合 OpenGL ES 使用的各种格式仅可通过扩展来定义,因此在每个平台上各有不同。 遗憾的是,所有 Android 设备上仅支持爱立信纹理压缩(ETC1)一种格式(除第一代设备以外,第一代设备不支持 OpenGL ES 2.0)。 ETC1 仅支持每像素 8 位的精度,且不支持 alpha。 因为大部分的游戏都使用带有 alpha 的压缩纹理,一般而言,这对移植是一个严重的障碍。 不支持 alpha 的几种 Android 平台上可使用几种专用格式,但是使用这些格式,您的游戏将限制在采用该特定 GPU 架构的 Android 设备上。 表 1 对它们为 Android 提供的各种 GPU 架构和纹理压缩格式进行了总结。
表 1. 包含 Alpha 支持的专用压缩纹理格式
GPU 架构 | 纹理格式 |
---|---|
PowerVR | PVRTC |
ATI/AMD | ATITC/3DC |
NVIDIA Tegra* 2/3 | DXT/S3TC |
英特尔凌动处理器上的 PowerVR GPU 可支持 PVRTC 格式和 ETC1。 PVRTC 支持 alpha 和每像素 2 位或 4 位精度,这可大幅降低 ETC1 格式的纹理尺寸。 PVRTC 是继 ETC1 之后支持最广泛的格式,它也可以在各代 Apple iPhone*、iPod touch* 和 iPad* 设备中使用,因此它可在英特尔凌动处理器上提供基本 OpenGL ES 标准以外的 Android 兼容性,这在移植游戏时对 Android 和 Apple iOS* 平台都很重要。 但是仍然无法处理使用不同 GPU 架构的 Android 设备。 如果您的游戏使用了任何专用纹理格式,它必须在运行时查询 OpenGL ES 扩展以确认哪些格式实际可用,并仅使用这些格式压缩的纹理。 这让设计决策变得困难。 您可以选择:
在确定您的游戏需要哪些专用格式后,确保在清单中将其声明,详见下文。 关于更多信息,请参阅纹理压缩支持。
一般情况下,台式机版本的 OpenGL 可提供一个名为 OpenGL Utility Library 的便捷函数库。 GLU 包括严格意义上不属于 OpenGL 或不是使用 OpenGL 必需的函数,但是它们在为 OpenGL 编写 3D 应用时非常有帮助。 标准的 GLU 库包括构造模型视图和投影矩阵、执行一般的矩阵计算、计算二次曲面、细分多边形、生成位图和报告错误消息的函数。 台式机 OpenGL 上的大多数游戏使用一些 GLU 函数,这可能会带来麻烦,因为 Android 仅可提供最低的 GLU 实施(仅可从 SDK 中获得)。 该类如表 2 所示。
表 2.提供 GLU 功能的 Android 类总结
类 | 功能 |
---|---|
android.opengl.GLU | 为 OpenGL ES 创建投影矩阵 |
android.graphics.Matrix | 创建模型视图和通用矩阵 |
OpenGL ES 1.1 和 2.0 可提供生成位图的支持,因此不具备计算二次曲面的功能和细分多边形的支持。 如果您需要这些功能,还存在其他的开源 GLU 执行,它们更完整且更适合配合 Android NDK 使用,如 GLU ES。
需要 OpenGL ES 的应用应在其清单文件中声明该事实。 这将会阻止您的应用在不支持所需 OpenGL ES 版本的设备上安装。 下列示例展示了 AndroidManifest.xml 文件中所需清单参数的正确语法。
OpenGL ES 1.1:
<uses-feature android:glEsVersion="0x00010001" android:required="true" />
<uses-sdk android:minSdkVersion="4"/>
OpenGL ES 2.0:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-sdk android:minSdkVersion="8"/>
Android 4.0 (API 级别 14) 的主要变化是,在所有应用上默认启用 OpenGL ES 硬件加速,可为 14 或更高级别的 minSdkVersion
参数发表声明。 API 级别更低的应用可以通过将 android:hardwareAccelerated=“true”
添加至 <application>
标签来启用加速。 如果您的应用使用了需要 OpenGL ES 硬件加速的 Android 4.0 中的新特性,如 TextureView
类,您必须在清单中启用它,否则这些特性不会运行。
如果您的应用使用了 NativeActivity
类,其清单必须声明它,并指定包括本地 Activity 的共享对象库名称。 此外,minSdkVersion
参数必须为 9 或更高版本。 请参考 NDK 中的 native-activity
示例应用。
如果您的应用使用了压缩纹理,请确保按照如下方式声明它们。 这不会阻止您的应用在没有这些特性的设备上安装,因此,您仍应在运行时查询设备以获得所需专用格式的扩展。 外部设备(如 Google Play)可使用这些清单参数过滤不能在无法支持所需纹理格式的设备上运行的应用。
压缩纹理格式所需的清单参数:
<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
<supports-gl-texture android:name="GL_IMG_texture_compression_pvrtc" />
当您设置 Android 开发系统时,请确保选择包含英特尔凌动处理器内置支持的 Android 版本。 这可帮助您找到英特尔凌动设备,并使用英特尔凌动系统映像 Android 虚拟设备(AVD)。 表 3 总结了目前所有的主要 Android 版本以及哪些版本包括面向英特尔凌动处理器内置的系统映像。
表 3. 包含 OpenGL ES 和英特尔® 凌动? 处理器支持的 Android 版本总结
版本 | 名称 | API | 支持 | 英特尔凌动支持 |
---|---|---|---|---|
Android 1.5 | Cupcake | 3 | OpenGL ES 1.0 | 无 |
Android 1.6 | Donut | 4 | OpenGL ES 1.0, 1.1 | 无 |
Android 2.0 | Éclair | 5 | OpenGL ES 1.0, 1.1 | 无 |
Android 2.1 | Éclair | 7 | OpenGL ES 1.0, 1.1 | 无 |
Android 2.2 | Froyo | 8 | OpenGL ES 1.0, 1.1, 2.0 | 无 |
Android 2.3.3 | Gingerbread | 10 | OpenGL ES 1.0, 1.1, 2.0 | 支持 |
Android 3.0 | Honeycomb | 11 | OpenGL ES 1.0, 1.1, 2.0 | 无 |
Android 3.1 | Honeycomb | 12 | OpenGL ES 1.0, 1.1, 2.0 | 无 |
Android 3.2 | Honeycomb | 13 | OpenGL ES 1.0, 1.1, 2.0 | 无 |
Android 4.0 | Ice Cream Sandwich | 14 | OpenGL ES 1.0, 1.1, 2.0 | 无 |
Android 4.0.3 | Ice Cream Sandwich | 15 | OpenGL ES 1.0, 1.1, 2.0 | 支持 |
Android 4.1 | Jelly Bean | 16 | OpenGL ES 1.0, 1.1, 2.0 | 支持 |
Android 版本 2.3.3 (Gingerbread)、4.0.3 (Ice Cream Sandwich) 和 4.1 (Jelly Bean)包括对英特尔凌动处理器的全面支持,包括面向 OpenGL ES 1.0、 1.1 和 2.0 的驱动程序。 仅从上述 Android SDK 管理器版本中选择一个版本并确保其依照原样列出英特尔 x86 凌动系统映像。 此外,还需确保下载和安装英特尔® 硬件加速执行管理器(HAXM) — 位于 Extras 下的 SDK 管理器中。 如果您尚未安装 Android SDK 管理器,可以从http://developer.android.com/sdk/index.html 进行下载。
SDK 管理器可下载 HAXM 安装程序并将其存入 Extras 文件夹,但是如要完成该安装,您必须手动运行 HAXM 安装程序。 此外,您还须在开发器的 ROM BIOS 设置菜单中启用虚拟技术特性。 HAXM 安装程序保存在 …\android-sdk\extras\intel\Hardware_Accelerated_Execution_Manager
上。 HAXM 正确安装后,当启动 x86 AVD 时将会显示下列消息: “HAX is working and emulator runs in fast virt mode”。
每个嵌入式开发人员都知道虚拟设备管理器对新应用开发的加速效果。 但是在 ARM 系统映像上使用 AVD 非常慢,因为 AVD 必须模拟 Windows 或 Linux 开发系统上的每个 ARM 指令。 仅在普通的 ARM AVD 上启动 Android 就需要 5 分钟或更长的时间。 借助 HAXM,英特尔解决了该问题,它可使用全新英特尔台式机处理器中内置的虚拟技术特性直接运行 Android x86 系统映像。 当安装该工具并配合英特尔凌动 x86 系统映像 AVD 使用时,您的应用开发将会获得显著加速,甚至都不需要启用英特尔凌动硬件。
Google 于 2012 年 4 月在 AVD 模拟中添加了对 OpenGL ES 2.0 的支持(从 SDK 工具版本 17 开始)。它通过将 OpenGL ES 2.0 调用转化至主机操作系统上可用的 OpenGL 2.0 API 来操作,这使得 3D 图形的运行速度更快。 但是,您必须在创建 AVD 时专门启用该特性;否则,对 OpenGL ES 2.0 的调用将失败。 当然,您的 Windows 或 Linux 主机系统必须针对 OpenGL 2.0(或更高版本)安装驱动程序,通常这还需要独立显卡。 OpenGL ES 2.0 模拟可在 ARM 和 x86 两种系统映像上运行,但是为了获得最佳性能,请将其用在启用了 HAXM 的 x86 系统映像上。 性能方面的差别非常显著。
如要启用 OpenGL ES 2.0 模拟,请在 AVD 管理器中选择一个 AVD 并点击 Edit。 然后,在 Hardware Properties 窗口中点击 New;滚动浏览属性列表,查找 GPU emulation,然后点击 OK。 最后,将该属性的 no 更改为 yes 并点击 Edit AVD、OK 来保存该变更。 然后将会出现一条消息,显示 hw.gpu.enabled=yes
。
对于 SDK 工具版本 17 以前的版本, AVD 模拟仅支持 OpenGL ES 1.0 和 1.1。 严格上讲,版本 1.1 并不要求启用 GPU 模拟,因为它不需要主机系统提供帮助就可以进行模拟,如果启用它将会变得更慢。 但是,没有主机的帮助 SDK 工具无法模拟版本 2.0,因此如果其不可用,当 AVD 尝试初始化 OpenGL ES 2.0 时会直接关闭您的应用。
使用 HelloEffects
示例应用来初次测试 OpenGL ES 2.0 模拟是否起作用。 GLES20TriangleRenderer
示例在 AVD 上运行时实际转换为版本 1.1, 因此该测试不成功。 Android SDK 和 NDK 中提供的所有 OpenGL ES 2.0 样本均与原始示例有关,且不应被用于 OpenGL 2.0 模拟测试。
在英特尔凌动处理器上以 Android 为目标的 OpenGL ES 应用开发人员可使用的另一重要工具套件是英特尔® 图形性能分析器(英特尔® GPA)。 该工具套件可提供几十个关键系统标准(包括 CPU、GPU 和 OpenGL ES)的实时视图。 英特尔 GPA 可在 Windows 或 Ubuntu Linux 开发系统上运行,并通过 Android 调试接口与 Android 目标设备上运行的驱动程序组件通信。 通过运行几种实验,您可以快速看到图形管线中的问题并找到优化代码的最佳机遇。
如欲了解更多信息以及下载面向 Android 开发的最新版英特尔 GPA (2012 R4),请访问 http://software.intel.com/en-us/vcsource/tools/intel-gpa?cid=sem121p7972。
Android 对 OpenGL ES 和 C/C++ 的支持降低了移植游戏和其他在 Android 平台设备中大量使用 3D 图形的应用的障碍。 尽管如此,还是存在一些障碍,您须在开始游戏移植项目之前先了解它们。 最大的障碍是 OpenGL 扩展中的差别和 Android 上支持的纹理压缩格式。 好的一面是,近年来 Google 和英特尔在改进面向 OpenGL ES 2.0 开发的工具上取得了巨大的进步,如今有大量的游戏、游戏引擎和传统游戏软件可在 OpenGL 标准下使用,这对于已经开始开发它的软件开发人员而言是一个巨大的机遇。
Clay D. Montgomery 是在嵌入式系统上开发面向 OpenGL 的驱动程序和应用的主要开发人员。 他曾在 STB 系统、VLSI 技术、飞利浦半导体、诺基亚、德州仪器、AMX 以及作为独立顾问从事跨平台图形加速器硬件、图形驱动程序、APIs 和 OpenGL 应用的设计。 他曾参与 Freescale i.MX 和 TI OMAP* 平台以及 Vivante、AMD 和 PowerVR 首个 OpenGL ES、OpenVG* 和 SVG 驱动程序和应用的开发。 他开发了在嵌入式 Linux 上开发 OpenGL ES 并开设了研讨班进行教授,且是 Khronos Group 多家公司的代表。