OpenGL渲染位图无显示问题求助(附MyGLSurfaceView代码)
OpenGL 位图无法显示问题排查与修复建议
我看了你贴的MyGLSurfaceView代码,目前这里的配置没什么明显问题——你正确初始化了OpenGL ES 2.0上下文,设置了渲染器并开启了按需渲染模式。不过因为你没贴完整的MyGLRenderer代码,大概率问题出在渲染器的位图加载、纹理绑定或者绘制逻辑上。下面给你梳理几个关键排查方向,还有一个可参考的示例代码:
关键排查点
位图加载与纹理创建是否正确
- 确保你在
onSurfaceCreated(或合适的时机)用BitmapFactory.decodeResource正确加载了位图,检查资源ID是否正确、资源是否存在于res/drawable目录下。 - 创建纹理时,必须调用
glGenTextures生成纹理ID,然后用glBindTexture绑定,再通过glTexParameteri设置纹理过滤和环绕参数,最后用GLUtils.texImage2D把位图数据传入纹理。任何一步出错都会导致纹理无效。
- 确保你在
着色器程序是否正常工作
- 顶点着色器需要传递顶点位置和纹理坐标到片段着色器,片段着色器要使用
texture2D函数正确采样纹理。 - 一定要检查着色器编译和程序链接状态:编译后用
glGetShaderiv获取GL_COMPILE_STATUS,链接后用glGetProgramiv获取GL_LINK_STATUS,避免语法错误导致着色器失效。
- 顶点着色器需要传递顶点位置和纹理坐标到片段着色器,片段着色器要使用
绘制流程是否完整
- 在
onDrawFrame中要先清除颜色缓冲区(glClear(GL_COLOR_BUFFER_BIT)),然后绑定纹理、启用顶点属性、设置顶点/纹理坐标,最后调用glDrawArrays或glDrawElements绘制矩形(覆盖屏幕的两个三角形)。 - 别忘了激活纹理单元(
glActiveTexture(GL_TEXTURE0))并把纹理ID传递给片段着色器的sampler2Duniform变量。
- 在
参考示例Renderer代码
public class MyGLRenderer implements GLSurfaceView.Renderer { private Context mContext; private int mTextureId; private FloatBuffer mVertexBuffer; private FloatBuffer mTexCoordBuffer; private int mProgram; // 屏幕坐标系顶点(覆盖全屏的矩形) private static final float[] VERTEX_DATA = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; // 对应纹理的坐标 private static final float[] TEX_COORD_DATA = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }; private static final String VERTEX_SHADER = "attribute vec4 aPosition;\n" + "attribute vec2 aTexCoord;\n" + "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTexCoord = aTexCoord;\n" + "}"; private static final String FRAGMENT_SHADER = "precision mediump float;\n" + "varying vec2 vTexCoord;\n" + "uniform sampler2D uTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTexCoord);\n" + "}"; public MyGLRenderer(Context context) { mContext = context; // 初始化顶点和纹理坐标缓冲区(必须用native字节序) mVertexBuffer = ByteBuffer.allocateDirect(VERTEX_DATA.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(VERTEX_DATA); mVertexBuffer.position(0); mTexCoordBuffer = ByteBuffer.allocateDirect(TEX_COORD_DATA.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(TEX_COORD_DATA); mTexCoordBuffer.position(0); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // 设置清屏颜色为黑色 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 编译并链接着色器程序 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); // 加载位图并创建纹理 Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.your_bitmap); mTextureId = createTexture(bitmap); bitmap.recycle(); // 释放位图内存 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // 设置视口大小为屏幕尺寸 GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { // 清除颜色缓冲区 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); // 使用当前着色器程序 GLES20.glUseProgram(mProgram); // 获取着色器中的属性和Uniform位置 int aPositionLoc = GLES20.glGetAttribLocation(mProgram, "aPosition"); int aTexCoordLoc = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); int uTextureLoc = GLES20.glGetUniformLocation(mProgram, "uTexture"); // 启用顶点属性数组 GLES20.glEnableVertexAttribArray(aPositionLoc); GLES20.glEnableVertexAttribArray(aTexCoordLoc); // 设置顶点坐标数据 GLES20.glVertexAttribPointer(aPositionLoc, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer); // 设置纹理坐标数据 GLES20.glVertexAttribPointer(aTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, mTexCoordBuffer); // 激活纹理单元并绑定纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); // 告诉着色器使用纹理单元0 GLES20.glUniform1i(uTextureLoc, 0); // 绘制矩形(用TRIANGLE_STRIP模式,4个顶点) GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); // 禁用顶点属性数组 GLES20.glDisableVertexAttribArray(aPositionLoc); GLES20.glDisableVertexAttribArray(aTexCoordLoc); } // 编译着色器的工具方法 private int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); // 检查编译错误 int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { Log.e("GLRenderer", "Shader compile failed: " + GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); return 0; } return shader; } // 创建纹理的工具方法 private int createTexture(Bitmap bitmap) { int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); int textureId = textureIds[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 设置纹理过滤参数,避免拉伸时出现锯齿 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); // 将位图数据上传到纹理 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); return textureId; } }
你可以把自己的Renderer代码和这个示例对比,重点检查纹理创建、着色器逻辑和绘制流程这几个部分。如果还是不行,可以把完整的Renderer代码贴出来,我再帮你定位具体问题。
内容的提问来源于stack exchange,提问作者rosu alin




