Android OpenGL es 2.0 无法重新加载对象上的纹理。

Android OpenGL es 2.0 Can not reload textures on object.

我已经成功地加载纹理并自由旋转球体,这要归功于这里提出的几个教程、问题和答案,但我偶然发现需要在运行时重新加载纹理(获取位图图像,处理它然后应用它作为对象的新纹理)。我没有为我的特定问题找到任何有效的解决方案(我已经阅读了所有相关的问题和答案)。 我对 OpenGL 很陌生。这是我的第二个项目,我的第一个 3D 项目,也是我在这里提出的第一个问题。所以这里是:

基本上纹理加载是在以下函数中完成的:

    public void loadGLTexture(final Context context, final int texture) {
    GLES20.glGenTextures(1, mTextures, 0);
    if (mTextures[0] != 0)
    {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), texture, options);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,   GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }

    if (mTextures[0] == 0)
    {
        throw new RuntimeException("Texture load fail");
    }
}

在此函数中完成抽奖时:

     public void draw(float[] mvpMatrix) {
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.mTextures[0]);
    GLES20.glFrontFace(GLES20.GL_CW);


    for (int i = 0; i < this.mTotalNumStrips; i++) {
        //vertex
        this.mVertexBuffer.get(i).position(0);
        GLES20.glVertexAttribPointer(mPositionHandle, NUM_FLOATS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, this.mVertexBuffer.get(i));
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //texture
        this.mTextureBuffer.get(i).position(0);
        GLES20.glVertexAttribPointer(mTextureCoordinateHandle, NUM_FLOATS_PER_TEXTURE,
                GLES20.GL_FLOAT, false,
                textureStride, this.mTextureBuffer.get(i));
        GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
        //draw strip
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, this.mVertices.get(i).length / NUM_FLOATS_PER_VERTEX);
    }

    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    // Disable the client state before leaving.
    GLES20.glDisableVertexAttribArray(mPositionHandle);
    GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
}

在上面的函数中,我使用了 Strips 技术来渲染一个球体。对于每个条带,我必须加载纹理和顶点数据并最终绘制它。

我还有一个应该删除纹理的函数,除了:

        public void clearGLTextures(){
    GLES20.glDeleteTextures(1, mTextures, 0);
}

我想在这里实现的是重新加载纹理,所以计划是:

INITIALISE(有效):loadGLTexture(initialtexture)-> 循环 draw() 函数

重新加载(不起作用):clearGLTextures() -> loadGLTexture(newTexture) -> 循环 draw() 函数

因此,不仅我无法重新加载纹理,而且对 clearGLTextures() 的调用似乎也不起作用,因为初始纹理仍保留在屏幕上。

欢迎任何想法, 谢谢!

这是在 Android 上进行 OpenGL 编程时常见问题的一个示例。不幸的是,症状变化很大,所以问题并不是真的重复。

当您使用 GLSurfaceView 时,它会创建一个单独的渲染线程。 GLSurfaceView.Renderer 实现(onSurfaceCreatedonSurfaceChangedonDrawFrame)中的所有方法都在此渲染线程中调用。

难题的第二部分是您需要当前的 OpenGL 上下文才能进行 OpenGL 调用。并且 OpenGL 上下文是当前的 每个线程 GLSurfaceView 负责创建上下文,并使其在渲染线程中处于当前状态。

因此,您无法从其他线程进行任何 OpenGL 调用(除非您做更多工作并增加复杂性)。最常见的错误是尝试从 UI 线程进行 OpenGL 调用,通常是在处理触摸事件等用户输入时。这些电话将不起作用。

如果您想执行 OpenGL 调用以响应用户输入,有许多选项。例如,您可以在 Renderer 中设置描述必要状态更改的成员,并在 onDraw() 方法中进行检查。另一个方便的选择是在视图上使用 queueEvent() 方法,它允许您传入 Runnable 稍后将在渲染线程中执行。

我刚 运行 遇到了同样的问题。

Reto Koradi 解释得很好,但我想分享我的解决方案: 我在 GLSurfaceView 中使用 queueEventRunnable

public void addTexture(final int textureResId) {
    queueEvent(new Runnable() {
        @Override
        public void run() {
            mRenderer.loadTexture(textureResId);
            // or different GL thread tasks like clearing the texture
        }
    });
}