OpenGL ES 3.0中无纹理四边形无法渲染的问题排查
问题分析与解决方案
看起来你踩了OpenGL ES 3.0的一个常见坑——你把所有着色器相关的代码都注释掉了,但ES 3.0完全依赖可编程着色器管线,旧版GL10的固定功能管线在ES 3.0中已经被移除了。没有激活有效的着色器程序,GPU根本不知道如何处理你提交的顶点数据,自然只会显示清屏的红色背景。下面是具体的修复步骤:
1. 实现正确的Shader类
首先你需要一个能正确加载、编译和链接GLSL ES 3.0着色器的工具类,这里给你一个标准实现:
public class Shader { private int programId; public Shader(AssetManager assetManager, String vertexPath, String fragmentPath) throws IOException { // 从assets加载着色器源码 String vertexSource = loadShaderSource(assetManager, vertexPath); String fragmentSource = loadShaderSource(assetManager, fragmentPath); // 编译顶点着色器 int vertexShader = compileShader(GLES30.GL_VERTEX_SHADER, vertexSource); // 编译片段着色器 int fragmentShader = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource); // 创建并链接程序 programId = GLES30.glCreateProgram(); GLES30.glAttachShader(programId, vertexShader); GLES30.glAttachShader(programId, fragmentShader); GLES30.glLinkProgram(programId); // 检查链接状态 int[] linkStatus = new int[1]; GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == GLES30.GL_FALSE) { String error = GLES30.glGetProgramInfoLog(programId); GLES30.glDeleteProgram(programId); GLES30.glDeleteShader(vertexShader); GLES30.glDeleteShader(fragmentShader); throw new RuntimeException("Shader link failed: " + error); } // 链接完成后删除临时着色器对象 GLES30.glDeleteShader(vertexShader); GLES30.glDeleteShader(fragmentShader); } private String loadShaderSource(AssetManager assetManager, String path) throws IOException { StringBuilder source = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open(path))); String line; while ((line = reader.readLine()) != null) { source.append(line).append("\n"); } reader.close(); return source.toString(); } private int compileShader(int type, String source) { int shader = GLES30.glCreateShader(type); GLES30.glShaderSource(shader, source); GLES30.glCompileShader(shader); // 检查编译状态 int[] compileStatus = new int[1]; GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == GLES30.GL_FALSE) { String error = GLES30.glGetShaderInfoLog(shader); GLES30.glDeleteShader(shader); throw new RuntimeException("Shader compile failed: " + error); } return shader; } public void activate() { GLES30.glUseProgram(programId); } public void deactivate() { GLES30.glUseProgram(0); } public void free() { GLES30.glDeleteProgram(programId); } }
2. 恢复Sketcher类的着色器逻辑
把你注释掉的着色器初始化和激活代码恢复,确保渲染前激活着色器程序:
@Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { p = new Player(); try { pShader = new Shader(am, "playerVertexShader.txt", "playerFragmentShader.txt"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("Failed to load shaders"); } GLES30.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); } @Override public void onDrawFrame(GL10 unused) { GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT); pShader.activate(); // 必须先激活着色器程序 GLES30.glBindVertexArray(p.getVaoID()); GLES30.glDrawElements(GLES30.GL_TRIANGLES, p.getVertexCount(), GLES30.GL_UNSIGNED_INT, 0); GLES30.glBindVertexArray(0); pShader.deactivate(); }
3. 优化Player类的VAO初始化
在Player类初始化VAO时,把顶点属性数组的启用状态保存到VAO中,避免每次渲染重复调用:
public Player() { // ... 原有VAO、Buffer创建代码 ... GLES30.glBindVertexArray(vaoID); GLES20.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vBufferID); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vBuffer.capacity(), vBuffer, GLES20.GL_STATIC_DRAW); GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 0, 0); GLES20.glEnableVertexAttribArray(0); // 在这里启用,VAO会记录这个状态 GLES20.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, iBufferID); GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, iBuffer.capacity(), iBuffer, GLES20.GL_STATIC_DRAW); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); GLES30.glBindVertexArray(0); }
4. 额外注意事项
- 确保着色器文件
playerVertexShader.txt和playerFragmentShader.txt放在项目的assets目录下,路径完全匹配。 - 尽量统一使用
GLES30的API(比如把GLES10.glClearColor换成GLES30.glClearColor),避免混合不同版本的GL API。 - 可以添加错误检查辅助排查问题,比如在关键GL调用后执行:
int error = GLES30.glGetError(); if (error != GLES30.GL_NO_ERROR) { Log.e("OpenGL Error", "Code: " + error); }
按照以上步骤修改后,你的黑色四边形应该就能正常显示在红色背景上了。
内容的提问来源于stack exchange,提问作者J. Lengel
相关产品推荐
相关产品推荐

