You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

渲染外部纹理到帧缓冲区时触发GL_INVALID_OPERATION错误排查

Android OpenGL双渲染流程中GL_INVALID_OPERATION错误排查

我尝试将Android MediaCodec输出的外部纹理内容,先渲染到带有纹理附件的帧缓冲区(第一遍渲染),再使用该纹理渲染到显示端(第二遍渲染),目的是在两次渲染的着色器中分别执行不同操作。但第一遍渲染调用glDrawElements时触发GL_INVALID_OPERATION错误,导致帧缓冲区关联的纹理未被填充。如果第一遍渲染不使用vTextureCoord而是直接渲染纯色,第二遍就能正常读取帧缓冲区关联的纹理内容,因此猜测问题与vTextureCoord相关。


OpenGL环境初始化

GLES20.glGenFramebuffers(1,frameBufferHandles,0);

GLES20.glGenBuffers(2, bufferHandles, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, Utils.VERTICES.length * FLOAT_SIZE_BYTES, Utils.getVertexBuffer(), GLES20.GL_DYNAMIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, Utils.INDICES.length * FLOAT_SIZE_BYTES, Utils.getIndicesBuffer(), GLES20.GL_DYNAMIC_DRAW);

GLES20.glGenTextures(2, textureHandles, 0);

GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureHandles[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]);

GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[1]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,0,GLES20.GL_RGBA,width,height,0,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE,null); //width.height为视口尺寸,已正确初始化  
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D,textureHandles[1],0);

int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if(status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
    Log.d(TAG,"Frame Buffer Incomplete : " + status);
} else
    Log.d(TAG,"Frame Buffer Complete");

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);

顶点着色器设置

public static final String DEFAULT_VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n"
        + "uniform mat4 uSTMatrix;\n"
        + "attribute vec4 aPosition;\n"
        + "attribute vec4 aTextureCoord;\n"
        + "varying vec2 vTextureCoord;\n"
        + "void main() {\n"
        + "  gl_Position = uMVPMatrix * aPosition;\n"
        + "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n"
        + "}\n";

片段着色器设置

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform sampler2D sTextureSampler;
uniform float r;
uniform int pass;


void main() {
        if(pass == 0) {
            gl_FragColor = texture2D(sTexture, vTextureCoord);
            //gl_FragColor = vec4(1.0,1.0,0.0,1.0);
         }
        else if(pass == 1) {
            gl_FragColor = texture2D(sTextureSampler, vTextureCoord);
         }
}

顶点属性和索引缓冲区定义

public static float[] VERTICES = {
        -1.0f, -1.0f, 0.0f, 0f, 0f,
        -1.0f,  1.0f, 0.0f, 0f, 1f,
         1.0f,  1.0f, 0.0f, 1f, 1f,
         1.0f, -1.0f, 0.0f, 1f, 0f
};

public static int[] INDICES = {
        2, 1, 0, 0, 3, 2
};

draw()函数实现

protected void draw() {
    Matrix.setIdentityM(mvpMatrix, 0);

    if(mProgram != -1)
        mProgram = createProgram();

    int vertexHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    int uvsHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
    int texMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
    int mvpMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    int textureHandle = GLES20.glGetUniformLocation(mProgram, "sTextureSampler");

    int pass = GLES20.glGetUniformLocation(mProgram, "pass");


    GLES20.glUseProgram(mProgram);
    GLES20.glUniformMatrix4fv(texMatrixHandle, 1, false, texMatrix, 0);
    GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandles[0]);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufferHandles[1]);
    GLES20.glEnableVertexAttribArray(vertexHandle);
    GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 4 * 5, 0);
    GLES20.glEnableVertexAttribArray(uvsHandle);
    GLES20.glVertexAttribPointer(uvsHandle, 2, GLES20.GL_FLOAT, false, 4 * 5, 3 * 4);

    //Pass 0 - 渲染到帧缓冲区,触发GL_INVALID_OPERAION错误
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]);
    GLES20.glUniform1i(pass, 0);
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0);
    
    //Pass 1 - 渲染到显示端
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
    GLES20.glUniform1i(textureHandle, 2);
    GLES20.glUniform1i(pass, 1);
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT, 0);

}

错误原因及修复方案

  1. 外部纹理参数设置类型不匹配
    初始化外部纹理时,绑定的是GL_TEXTURE_EXTERNAL_OES类型,但设置纹理参数时误用了GLES20.GL_TEXTURE_2D,导致纹理状态异常。修复代码:

    GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureHandles[0]);
    // 将纹理参数的目标改为GL_TEXTURE_EXTERNAL_OES
    GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    
  2. 外部纹理采样器未绑定
    第一遍渲染使用samplerExternalOES类型的sTexture,但draw函数中未给该采样器绑定对应的纹理单元。需在第一遍渲染前添加绑定逻辑:

    // 获取外部纹理采样器句柄
    int sTextureHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
    // 绑定外部纹理到纹理单元0
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureHandles[0]);
    GLES20.glUniform1i(sTextureHandle, 0);
    
  3. 帧缓冲区渲染未设置匹配视口
    绑定帧缓冲区后,需设置与帧缓冲区纹理尺寸一致的视口,否则渲染区域不匹配可能触发错误:

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]);
    // 设置视口为帧缓冲区纹理的宽高
    GLES20.glViewport(0, 0, width, height);
    
  4. 索引缓冲区类型兼容性问题
    OpenGL ES 2.0规范仅要求支持GL_UNSIGNED_BYTE和GL_UNSIGNED_SHORT作为索引类型,部分设备不兼容GL_UNSIGNED_INT。可将索引数组改为short[],并修改对应调用参数:

    // 修改索引数组类型
    public static short[] INDICES = {2, 1, 0, 0, 3, 2};
    // 初始化元素缓冲区时修改字节长度(short占2字节)
    GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, Utils.INDICES.length * 2, Utils.getIndicesBuffer(), GLES20.GL_DYNAMIC_DRAW);
    // glDrawElements使用GL_UNSIGNED_SHORT类型
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, 0);
    

内容的提问来源于stack exchange,提问作者bsguru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 22:00:55