我对使用 OpenGL 有非常基本的了解,尤其是在 Android 上。我正在开发一个使用 OpenGL 的应用程序,以便在全屏图像之间快速切换(因为使用普通的 Android 框架太慢了)。
我发现为了加载纹理,我需要执行以下操作:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
textureBuffer = byteBuffer.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.position(0);
_gl = gl;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), _imageResourceId);
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
现在,由于图像是全屏的(而且它们非常大 - 1024x1024),这需要一些时间,特别是因为我需要同时加载其中的 5 个。
所以我需要问一些关于改进代码技巧的问题,特别是关于高效加载(但如果可能的话也使用更少的内存):
如果我将位图解码为没有透明像素,通过使用 RGB_565 格式,它会提高将图像加载到 OpenGL(或解码阶段)的速度吗?
输入位图的宽度和高度是否必须是 2 的幂,或者我可以让 OpenGL 只取它想要的部分,这将有这个规则吗?我的意思是,也许我可以让
texImage2D
命令只占用位图的一部分?也许我什至可以从一开始就只解码这一部分(所以如果我有一个 10000x10000 的巨大图像,我只能解码其中的 1024x1024 部分并将其提供给 OpenGL)?
是否可以仅加载和显示第一张图像,然后在后台加载其余图像?我听说您不能在不是处理它的线程中将位图发送到 OpenGL。
onDrawFrame
将它们加载到 的方法上是否有意义GlRenderer
,例如,第二次调用它?我记得我听说过一个关于将多个图像合并为一个图像的技巧(一个接一个,从左到右),这样它可能会在解码阶段有一些速度提升。不知道这种技术的名称是什么。Android上的OpenGL ES仍然可行吗?
是否可以避免创建 Bitmap 实例并让解码以更本地的方式完成(也许是 NDK)?根据我的测试,在模拟器上解码大小为 1024x1024 的 PNG 文件大约需要 400ms-700ms,而将其发送到 OpenGL 大约需要 50ms-70ms。
使用 Procrank,我发现 OpenGL 会占用大量内存。有没有可能让它使用更少的内存?也许使用更好的纹理类型?
由于Android可以在多种设备上运行,是否可以在清单中设置应用程序需要多少内存才能运行,这样内存太少的人就无法安装它?
@马吉德·麦克斯:
所以以这种方式解码并将其发送到OpenGL就足够了,还是在发送到openGL时我也应该设置一些特殊的东西?
没有这样的命令来获取位图的一部分吗?
我的意思是,我可以只解码要存储在位图中的文件的一部分,而不是全部位图吗?
所以我唯一能做的就是在开始时加载所有内容,然后全部使用?怎么会这样?游戏如何处理?我的意思是,它们确实显示了一个又一个阶段,即使看起来像 OpenGL 生成的进度条。这是非常有问题的。
我所描述的也可以在网络上找到。例如,网页不是加载多个微小的图像,而是包含一个图像并映射它的哪些部分应该显示在哪里。它的另一个名字是sprites。这里的例子。
我懂了。我想当我看到不再使用 glDeleteTextures 时,我也应该调用它,对吧?
我怎么做?我应该在代码中更改什么?
当我使用大量内存时会发生什么?当没有可用 RAM 时,是否存在诸如虚拟 RAM(可能使用内部存储)之类的东西?我在 Google IO 视频上听说过它,但似乎他们也不确定答案。链接在这里。他们只是说,当这种事情发生时,预计会有更多的崩溃。
@马吉德·麦克斯:
1+2+3。?
这可能有效。你的意思是我创建一个新线程来加载位图,然后将结果发送回 OpenGL 线程,它将纹理绑定到位图?也许我可以对 asyncTask 使用类似的方法,并使用 publishprogress。
这也是一件好事。你有什么我应该读的链接吗?或者也许是要更改我的代码的片段?
谢谢。
所以我认为使用 ETC1 是最简单的事情。但是它根本不支持透明度,所以根据这个链接,我需要为此使用另一个纹理,但我找不到这个示例。
我可以假设我可以使用的可用内存是什么?例如,我是否可以假设所有 Android 设备都可以为我提供 200MB 的视频内存,我可以将其用于 OpenGL?