渲染外部纹理到帧缓冲区时触发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); }
错误原因及修复方案
外部纹理参数设置类型不匹配
初始化外部纹理时,绑定的是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);外部纹理采样器未绑定
第一遍渲染使用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);帧缓冲区渲染未设置匹配视口
绑定帧缓冲区后,需设置与帧缓冲区纹理尺寸一致的视口,否则渲染区域不匹配可能触发错误:GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,frameBufferHandles[0]); // 设置视口为帧缓冲区纹理的宽高 GLES20.glViewport(0, 0, width, height);索引缓冲区类型兼容性问题
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
相关产品推荐
相关产品推荐

