OpenGL ES 3.0 Hello 三角形是黑色的

OpenGL ES 3.0 Hello Triangle is Black

这是我第一次使用 Android 和 OpenGL ES,但我确实有相当多的直接 OpenGL 经验。我只是想画一个红色三角形。没有制服,只有一个顶点属性。

顶点着色器:

#version 300 es

in vec3 in_loc;

void main() {
    gl_Position.xyz = in_loc;
}

片段着色器:

#version 300 es

precision mediump float;

out vec4 outColor;

void main() {
    outColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}

结果:

我正在检查并在着色器创建、编译、附件和链接期间没有发现任何错误。我试过将 precision mediump float; 更改为 precision highp float;precision lowp float;

通常我希望这个纯黑色输出表示片段着色器损坏,但我没有收到任何错误,据我所知,这是非常简单的代码,与网上找到的各种示例代码相同。

着色器创建代码:

public class Shader {

    public enum Type { VERTEX, FRAGMENT }

    static private int createShader(String src, Type type) {
        int shaderHandle = 0;
        switch (type) {
            case VERTEX:
                shaderHandle = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
                break;
            case FRAGMENT:
                shaderHandle = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
                break;
        }
        if (shaderHandle == 0) {
            Log.e("Shader", "Failed to create shader");
            return 0;
        }

        // Compile shader
        GLES30.glShaderSource(shaderHandle, src);
        GLES30.glCompileShader(shaderHandle);

        // Check for compile errors
        int status[] = { 0 };
        GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, status, 0);
        if (status[0] == 0) {
            Log.e("Shader", "Failed to compile shader");
            Log.e("Shader", GLES30.glGetShaderInfoLog(shaderHandle));
            GLES30.glDeleteShader(shaderHandle);
            return 0;
        }

        // Check for OpenGL errors
        if (Util.isGLError()) {
            GLES30.glDeleteShader(shaderHandle);
            return 0;
        }

        return shaderHandle;
    }

    static private int createProgram(int vertShaderHandle, int fragShaderHandle) {
        int programHandle = GLES30.glCreateProgram();
        if (programHandle == 0) {
            Log.e("Shader", "Failed to create program");
            return 0;
        }

        // Attach and link shaders to form program
        GLES30.glAttachShader(programHandle, vertShaderHandle);
        GLES30.glAttachShader(programHandle, fragShaderHandle);
        GLES30.glLinkProgram(programHandle);

        // Check for linking errors
        int status[] = { 0 };
        GLES30.glGetProgramiv(programHandle, GLES30.GL_LINK_STATUS, status, 0);
        if (status[0] == 0) {
            Log.e("Shader", "Failed to link program");
            Log.e("Shader", GLES30.glGetProgramInfoLog(programHandle));
            GLES30.glDeleteProgram(programHandle);
            return 0;
        }

        // Check for OpenGL errors
        if (Util.isGLError()) {
            GLES30.glDeleteProgram(programHandle);
            return 0;
        }

        return programHandle;
    }

    private String name;
    private int programHandle;

    public Shader(String name) {
        this.name = name;
        this.programHandle = 0;
    }

    public boolean initFromSrc(String vertSrc, String fragSrc) {
        // Create shaders
        int vertShaderHandle = createShader(vertSrc, Type.VERTEX);
        if (vertShaderHandle == 0) {
            Log.e("Shader", "Failed to create vertex shader");
            return false;
        }
        int fragShaderHandle = createShader(fragSrc, Type.FRAGMENT);
        if (fragShaderHandle == 0) {
            Log.e("Shader", "Failed to create fragment shader");
            GLES30.glDeleteShader(vertShaderHandle);
            return false;
        }

        // Create shader program
        programHandle = createProgram(vertShaderHandle, fragShaderHandle);
        if (programHandle == 0) {
            Log.e("Shader", "Failed to create program");
            GLES30.glDeleteShader(vertShaderHandle);
            GLES30.glDeleteShader(fragShaderHandle);
            return false;
        }

        // Shaders can now be deleted
        GLES30.glDeleteShader(vertShaderHandle);
        GLES30.glDeleteShader(fragShaderHandle);

        return true;
    }

    public boolean initFromFile(String vertFile, String fragFile) {
        String vertSrc = Util.readTextFile(vertFile);
        if (vertSrc == null) {
            Log.e("Shader", "Failed to read shader file: " + vertFile);
            return false;
        }

        String fragSrc = Util.readTextFile(fragFile);
        if (fragSrc == null) {
            Log.e("Shader", "Failed to read shader file: " + fragFile);
            return false;
        }

        return initFromSrc(vertSrc, fragSrc);
    }

    public int getProgramHandle() { return programHandle; }

}

其他一切:

@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    GLES30.glClearColor(0.0f, 0.5f, 1.0f, 1.0f);

    shader = new Shader("basic");
    if (!shader.initFromFile("shaders/basic.vert", "shaders/basic.frag")) {
        Log.e("MainRenderer", "Failed to initialize basic shader");
    }

    float[] locs = {
            0.0f, 0.0f, 0.0f,
            1.0f, 0.0f, 0.0f,
            1.0f, 1.0f, 0.0f
    };
    ByteBuffer locsBB = ByteBuffer.allocateDirect(locs.length * 4);
    locsBB.order(ByteOrder.nativeOrder());
    FloatBuffer locsFB = locsBB.asFloatBuffer();
    locsFB.put(locs);
    int[] vboArr = { 0 };
    GLES30.glGenBuffers(1, vboArr, 0);
    int vboHandle = vboArr[0];
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandle);
    GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, locs.length * 4, locsBB, GLES30.GL_STATIC_DRAW);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

    int[] indices = {
            0, 1, 2
    };
    ByteBuffer indicesBB = ByteBuffer.allocateDirect(indices.length * 4);
    indicesBB.order(ByteOrder.nativeOrder());
    IntBuffer indicesIB = indicesBB.asIntBuffer();
    indicesIB.put(indices);
    int[] iboArr = { 0 };
    GLES30.glGenBuffers(1, iboArr, 0);
    int iboHandle = iboArr[0];
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
    GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER, indices.length * 4, indicesBB, GLES30.GL_STATIC_DRAW);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);

    int[] vaoArr = { 0 };
    GLES30.glGenVertexArrays(1, vaoArr, 0);
    vaoHandle = vaoArr[0];
    if (vaoHandle == 0) {
        Log.e("MainRenderer", "Failed to create vao");
    }

    GLES30.glBindVertexArray(vaoHandle);
    int locationAI = GLES30.glGetAttribLocation(shader.getProgramHandle(), "in_loc");
    GLES30.glEnableVertexAttribArray(locationAI);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboHandle);
    GLES30.glVertexAttribPointer(locationAI, 3, GLES30.GL_FLOAT, false, 0, 0);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
    GLES30.glBindVertexArray(0);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
}

@Override
public void onDrawFrame(GL10 unused) {
    // Redraw background color
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

    // Draw triangle
    GLES30.glBindVertexArray(vaoHandle);
    GLES30.glDrawElements(GLES30.GL_TRIANGLES, 3, GLES30.GL_UNSIGNED_INT, 0);
    GLES30.glBindVertexArray(0);
}

正如 Ville 指出的那样,问题是我没有在 draw 方法中使用该程序。

修订:

// Draw triangle

// Actually use the program
GLES30.glUseProgram(shader.getProgramHandle());

GLES30.glBindVertexArray(vaoHandle);
GLES30.glDrawElements(GLES30.GL_TRIANGLES, 3, GLES30.GL_UNSIGNED_INT, 0);
GLES30.glBindVertexArray(0);

GLES30.glUseProgram(0);