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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:17:08