在Android中使用OpenGLES主要有四种方法:
- 使用
GLSurfaceView
配合GLSurfaceView.Renderer
的纯Java代码实现。 - 使用
GLSurfaceView
配合GLSurfaceView.Renderer
的半Java半C/C++代码实现。 - 使用
SurfaceView
的的半Java半C/C++代码实现。 - 使用
ANativeActivity
的纯C/C++代码实现。
下面主要阐述这四种方法各自的特点。
使用GLSurfaceView
配合GLSurfaceView.Renderer
的纯Java代码实现
- 对于使用Java/Kotlin的Android开发者来说,可能不愿意写C/C++代码,或者使用C/C++需要学习成本;
- Android之所以使用Java作为上层应用开发语音,也是考虑到Android不止使用一种CPU架构的指令集,如果每个应用都使用C/C++来编写,那么每个应用必然存在armeabi-v7a、arm64-v8a、x86、x86-64、mips…对应的可执行文件,所以使用Java开发可以屏蔽CPU架构的指令集的差异,减小安装包的大小;
- 使用
GLSurfaceView
可以不用写EGL相关部分的代码,因为GLSurfaceView
已经帮你处理好了EGL设置(具体在EglHelper
中实现),除非默认的设置不满足你的要求才需要自行设置; GLSurfaceView
里面有一个GLThread
,GLThread
保证所有GLES10
、GLES20
、GLES30
…的方法都只能在这个线程中调用,也就是需要把相关方法写在onSurfaceCreated
、onSurfaceChanged
和onDrawFrame
三个回调方法里,这样可以保证OpenGLES方法调用都在同一个线程中执行。
使用GLSurfaceView
配合GLSurfaceView.Renderer
的半Java半C/C++代码实现
这种方法除了具备方法一的3、4两个优点外,还具备以下优点:
- 对于喜欢使用C/C++的Android开发者来说,可以使用很多开源库来支持OpenGLES的开发,如glm、stb和assimp库,这些库都没有对应的官方版Java库;
- 如果想通过native的gl方法调用来增加执行速度可能不是很明显,因为上一种方法其实也是通过jni调用相应的native方法,而真正提升速度的可能是3D模型加载以及一些耗时计算上;
- 当加载大图片或者大3D模型时使用Java代码实现很容易造成OOM,如果使用native方法来加载,可以减小OOM的可能性。
使用SurfaceView
的的半Java半C/C++代码实现
这种方法同样具备方法二的1、2、3这三个优点,但是需要自行处理EGL的配置,以及线程安全的问题。
使用ANativeActivity
的纯C/C++代码实现
对于纯C/C++的开发人员来讲是最想使用的方法,特别是跨平台开发者的最爱,他们只需要根据不同平台抽离EGL配置以及事件获取部分的代码,其它渲染部分的代码都是共用的。那么只要使用了C/C++代码开发的特点有:
- 在跨平台开发时,部分C/C++代码在所有平台上都可以运行,不需要使用不同的语言再实现一次,代码复用性高;
- 优点与缺点并存,对于按钮的实现不能使用Android的
Button
这样的Java控件,所以需要自己实现按钮(如Cocos2d-x)。
示例项目
- 对于纯Java的Android开发者,首推《OpenGL ES 2 for Android : A Quick-Start Guide》的示例代码;
- 对于纯C/C++开发者,可以参考《OpenGL ES 3.0编程指南》的示例代码或者Google官方示例代码native-activity);
- 对于半C/C++半Java开发,可以参考Google官方示例代码gles3jni。
后记
现在Vulkan都开始替代OpenGL了,为什么还要花心思去学习OpenGL呢?
- OpenGL的学习资料更多;
- 学习OpenGL后,更容易上手Vulkan,因为Vulkan沿用了OpenGL的一些知识点(如MVP,GLSL等);
- 如果要在Android设备上使用Vulkan,目前大部分手机并不支持,而且必须使用C/C++开发,并未提供Java API。