我有大量JPG格式的纹理。 我需要在实际绘图开始之前在opengl内存中预加载它们。 我问了一个问题,我被告知这样做的方法是将JPEG解包和glTexImage2D(...)调用分开到另一个线程。 问题是我不太清楚如何做到这一点。
执行glTexImage2D所需的OpenGL(处理程序?)仅在GLSurfaceView.Renderer的onSurfaceCreated和OnDrawFrame方法中可用。
我无法解压缩所有纹理,然后在onSurfaceCreated(...)中将它们加载到opnegl中, 因为它们可能不适合有限的vm内存(20-40MB?)
这意味着我必须逐个解包并加载它们,但在这种情况下我无法获得一个opengl指针。
请有人,请给我以及为opengl游戏加载纹理的示例吗?
它必须是一些典型的程序,我无法在任何地方获得任何信息。
答案 0 :(得分:10)
如'OpenGLES preloading textures in other thread'中所述,有两个独立的步骤:位图创建和位图上传。在大多数情况下,只需在辅助线程上创建位图就可以了 - 这非常简单。
如果您在上传纹理时遇到帧丢失,请从后台线程调用texImage2D
。要做到这一点,你需要创建一个新的OpenGL上下文,它与你的渲染线程共享它的纹理,因为每个线程都需要它自己的OpenGL上下文。
EGLContext textureContext = egl.eglCreateContext(display, eglConfig, renderContext, null);
获取eglCreateContext
的参数有点棘手。您需要在setEGLContextFactory
上使用SurfaceView
来挂钩EGLContext创建:
@Override
public EGLContext createContext(final EGL10 egl, final EGLDisplay display, final EGLConfig eglConfig) {
EGLContext renderContext = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, null);
// create your texture context here
return renderContext;
}
然后你准备开始一个纹理加载线程:
public void run() {
int pbufferAttribs[] = { EGL10.EGL_WIDTH, 1, EGL10.EGL_HEIGHT, 1, EGL14.EGL_TEXTURE_TARGET,
EGL14.EGL_NO_TEXTURE, EGL14.EGL_TEXTURE_FORMAT, EGL14.EGL_NO_TEXTURE,
EGL10.EGL_NONE };
EGLSurface localSurface = egl.eglCreatePbufferSurface(display, eglConfig, pbufferAttribs);
egl.eglMakeCurrent(display, localSurface, localSurface, textureContext);
int textureId = loadTexture(R.drawable.waterfalls);
// here you can pass the textureId to your
// render thread to be used with glBindTexture
}
我已在https://github.com/perpetual-mobile/SharedGLContextsTest创建了上述代码段的工作演示。
此解决方案基于互联网上的许多来源。最有影响力的是这三个:
答案 1 :(得分:2)
您只需拥有上传例程的主线程,即可访问OpenGL并调用glTexImage2D。另一个线程将图像从文件加载(并解码)到内存。当辅助线程加载下一个图像时,主线程将先前加载的图像上载到纹理中。因此,您只需要两个图像的内存,即当前从文件加载的图像和当前上载到GL中的图像(之前加载的图像)。当然你需要一些同步,以防止加载器线程覆盖内存,主线程当前发送给GL并防止主线程发送未完成的数据。
答案 2 :(得分:2)
“必须有一种方法可以在初始化函数之外调用GL函数。” - 是的只需将指针复制到gl并在任何地方使用它。
“确保只在主线程中使用OpenGL。”很重要。你不能在你的游戏引擎(可能在另一个线程中)中调用一个与gl-thread不同步的纹理加载函数。设置一个标志来指示你的gl线程加载一个新纹理(例如,你可以在OnDrawFrame(GL gl)中放置一个函数来检查是否必须加载一个新的纹理。
答案 3 :(得分:0)
为此找到了一个解决方案,这实际上非常简单:在加载位图(在单独的线程中)后,将其存储在实例变量中,并在draw
方法中检查它是否已初始化,如果是的,加载纹理。像这样:
if (bitmap != null && textureId == -1) {
initTexture(gl, bitmap);
}
答案 4 :(得分:0)
要添加到Rodja's answer,如果您需要OpenGL ES 2.0上下文,请使用以下内容创建上下文:
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] contextAttributes =
{ EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext renderContext = egl.eglCreateContext(
display, config, EGL10.EGL_NO_CONTEXT, contextAttributes);
您仍然需要调用setEGLContextClientVersion(2)
,因为默认配置选择器也会使用它。