OpenGL应用的manifest需要指定两件事:
(1) 需要设备支持哪个OpenGL版本
(2) 应用支持哪种材质压缩格式
Listing 8-16给出了如何指定设备支持OpenGL 2.0和应用支持两种压缩格式:ETC1和PVRTC。
Listing 8-16 Manifest和OpenGL
...
当设备连接到市场的时候,安卓市场将使用这个信息去过滤应用。比如,仅支持OpenGL 1.1的设备不会看到需要OpenGL ES 2.0的应用。相似的,仅支持ETC1 texture压缩的设备不会看到仅支持PVRTC和ATC texture的应用。
如果你的应用不在manifest中指定任何压缩格式,安卓市场不会基于压缩格式做任何过滤(假设你的应用支持所有的压缩格式)。这将导致用户安装你的应用,仅仅发现应用不能工作,潜在的导致坏的评论。
3D场景的对象经常作为背景出现,不占用屏幕上很多空间。比如,在一个屏幕上仅占10*10像素的对象使用256*256的材质是资源的浪费(内存,带宽)。Mipmaps通过提供多个级别的材质细节解决了这个问题,Figure 8-6所示。
Figure 8-6 从256*256到1*1的Mipmaps
一个mipmap集合比单独的原始图片多使用33%的内存,它不仅可以提升性能,同样还有视觉质量。
就像你在Figure 8-6看到的,每个图片是原始图片的一个版本,但是在细节上级别不同。显然,在1*1版本的材质上很难看到花朵。
AMR Mali GPU Texture Compression Tool可以为你产生mipmaps。不是单独产生一个.pkm文件,工具将产生所有级别的细节直到1*1,每个都将创建一个.pkm文件。你的应用需要依次加载这些不同级别的细节,比如通过调用ETC1Util.loadTexture()或者GLES20.glTexImage2D()。
因为不是所有的设备都相同,他们可能不都需要同样级别的细节。比如,分辨率1920*1080(HD分辨率)的Google TV设备通常比Samsung Nexus S(800*480)需要更高的细节级别。结果,当256*256 材质可能被Google TV使用,但是Nexus S可能不会使用,而是使用128*128。
NOTE:不要简单的依赖设备的分辨率去决定你的应用做什么。比如,Sony Google TV电视(1920*1080分辨率)在400MHz使用PowerVR SGX535,没有在新的Samsung Galaxy Nexus(1280*720分辨率)上发现的384MHz的PowerVR SGX540强大。
材质如何被渲染同样依赖于使用glTexParameter()方法设置的材质参数(比如glTexParameteri())。比如,你可以通过调用glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,param)设置minifying功能,param是如下的值之一:
(1) GL_NEAREST
(2) GL_LINEAR
(3) GL_NEAREST_MIPMAP_NEAREST
(4) GL_NEAREST_MIPMAP_LINEAR
(5) GL_LINEAR_MIPMAP_NEAREST
(6) GL_LINEAR_MIPMAP_LINEAR
GL_NEAREST通常比GL_LINEAR要快,GL_LINEAR比其他四个要快,更好的视觉质量可以通过更慢的函数达到。应用选择什么样的参数可能依赖用户的设置(如果你提供了用户配置渲染方式的入口),然而很多用户没有耐心去尝试不同的设置,找到在他们的设置工作很好的组合。作为结论,你的应用需要尽力决定合适的OpenGL配置。
OpenGL许多事情可以配置,一些默认值倾向于视觉质量而不是性能,所以你要熟悉你的应用可以配置什么。比如,考虑到材质,http://www.khronos.org/opengles/sdk/docs/man/的文档给了你可以设置的所有不同参数。
因为你可能希望支持多个材质压缩格式(尽量为每种设备优化),mipmaps需要更多的空间,应用的大小可能会超出安卓市场定义的限制(现在是50M)
如果这发生了,你有三个选择:
(1) 减少应用的大小,比如只支持ETC1材质压缩
(2) 在应用安装完成后从远程服务器下载材质
(3) 产生多个APK,每个有自己的材质集
安卓市场允许你为应用发布不同的APK,每个针对不同的配置。比如,你可能有一个仅支持ETC1材质的APK,另外有个仅支持PVRTC材质的APK(基于Android设备针对PowerVR的优化APK)。这些APK共享同样的安卓市场列表,安卓市场会为每个设备选择合适的APK。用户不需要担心下载和安装的APK是否正确,因为选择是自动并且透明的。
NOTE:不是所有的Android应用市场支持这个功能,所以如果你在多个商店发布你的应用,尽可能使用支持所有设备的单个APK。
当然,材质可能不是你发布多个APK的单独原因。比如,你可能会为老一些的设备发布小一些的APK,为新的设备发布带有更多功能的更大的APK。尽管可以发布多个APK,它使得你的维护和发布过程更加复杂,推荐你如果可能的话尽量尝试单个APK。
OpenGL ES 2.0支持OpenGL ES Shading Language,替换OpenGL ES 1.x固定的转换函数和fragment管道。基于C,这门语言允许你通过自己的向量和fragment shaders控制OpenGL管道。
向任何的C工程,shaders可以从非常简单到及其复杂。没有你可以遵循的规则,需要尝试尽可能减少你的shader的复杂度,因为这会显著影响到性能。
显然,复杂的场景比渲染简单的场景需要更多的时间。因此提升帧率的一个简单的方式在维持可接受的视觉质量下简化要渲染的场景。比如,如果你已经看了材质,远处的对象细节更少,可以被更少的三角形组成。更简单的对象使用更少的内存和带宽。
尽管GPU的几何很好并且可以决定要渲染什么,你的应用需要尽力去消除视野之外的对象,这样不会为这些对象发送draw命令而是简单的忽略,因为他们是不可见的。
有很多的方法去cull对象,这些方法不在本书的讨论范围之内,差劲的culling可能会导致低于预期的帧率。比如,在camera后面的对象可以被简单的快速消除。
NOTE:你需要开启back face culling,所以背面的对象不会被渲染。
默认OpenGL renderer持续在场景上渲染,而不管什么已经改变。在一些情况下,帧之间的场景可能没有改变,你可能希望显式的告诉renderer仅仅在你需要的时候渲染场景。可以通过改变GLSurfaceView的渲染模式达到这个目标。
你可以通过调用setRenderMode()设置GLSurfaceView的渲染模式,使用如下的值:
(1) RENDERMODE_CONTINUOUSLY
(2) RENDERMODE_WHEN_DIRTY
当渲染模式设置为RENDERMODE_WHEN_DIRTY,当surface被创建的时候或者GLSurfaceView.requestRender()调用的时候renderer将会渲染场景。
现在GPU的一个伟大的品质是他们可以休眠或者至少在非交互的周期的快速慢下来。比如,GPU可以在两帧之间关闭(部分或者全部),如果有一阵无事可做,减少电能消耗因此增加电池使用时间。
一旦你的应用达到了可接受的帧率,你可能仍然希望为了减少电能消耗进一步优化。一帧被渲染的越快,GPU进入idle越快,电池的使用时间越长(用户可以使用你的应用的时间越长)。对某些应用,对OpenGL提升电池使用时间的一个伟大方式是仅当场景变化的时候去渲染帧(就像上面提到的RENDERMODE_WHEN_DIRTY渲染模式)。
最新的Android设备硬件功能很强大,支持2D和3D的大图。比起两年前一些优化已经不怎么重要,新的挑战在于设备支持越来越高的屏幕分辩率,对OpenGL ES 2.0的普遍支持,和用户更高的期望。本章仅仅涉及OpenGL的皮毛,仅介绍了几个可以简单实现但是可以得到很大效果的技术。幸运的是,学习OpenGL有很多的资源,不管是初级的还是高级的。记得参考GPU提供商的文档(ARM、Imagination Technologyies、NviDia和Qualcomm),因为他们以不同的方式为自己的GPU优化渲染。