OpenGL ES着色器似乎被删除了

时间:2014-04-14 17:57:15

标签: java android opengl-es shader wrapper

固定

现在一切正常!向下滚动以查看溶剂。

周长

我目前正在编写一个OpenGL引擎。 我为OpenGL着色器编写了一个包装器:

public abstract class Shader {
    protected String name;
    protected int type;
    protected String code;
    protected int shader;
    protected boolean loaded;

    public Shader(String name, String code) {
        this.name = name;
        this.code = code;
        this.loaded = false;
        this.type = -1;
    }

    public Shader(Shader s) {
        this.name = s.name;
        this.type = s.type;
        this.code = s.code;
        this.shader = -1;
        this.loaded = false;
    }

    public String getName() {
        return this.name;
    }

    public int getType() {
        return this.type;
    }

    public boolean isVertexShader() {
        return this.type == GLES20.GL_VERTEX_SHADER;
    }

    public boolean isFragmentShader() {
        return this.type == GLES20.GL_FRAGMENT_SHADER;
    }

    public void unload() {
        this.loaded = false;
    }

    public int load() {
        if(!this.loaded) {
            this.shader = GLES20.glCreateShader(this.type);

            GLES20.glShaderSource(this.shader, this.code);
            GLES20.glCompileShader(this.shader);
            DrainEmEngine.checkGlError("glCompileShader");

            this.loaded = true;
        }

        return this.shader;
    }
}

我有一个程序包装器:

public class Program {
    private String name;
    private VertexShader vertexShader;      // VertexShader and FragmentShader just 
    private FragmentShader fragmentShader;  // extend the Shader class and set it's type
    private int program;
    private boolean loaded;

    public Program(String name, VertexShader vertexShader, FragmentShader fragmentShader) {
        this.name = name;
        this.vertexShader = vertexShader;
        this.fragmentShader = fragmentShader;
        this.program = -1;
        this.loaded = false;
    }

    public Program(Program p) {
        this.name = p.name;
        this.vertexShader = new VertexShader(p.vertexShader);
        this.fragmentShader = new FragmentShader(p.fragmentShader);
        this.program = -1;
        this.loaded = false;
    }

    public String getName() {
        return this.name;
    }
    public VertexShader getVertexShader() {
        return this.vertexShader;
    }

    public FragmentShader getFragmentShader() {
        return this.fragmentShader;
    }

    public void unload() {
        this.vertexShader.unload();
        this.fragmentShader.unload();
        this.loaded = false;
    }

    public int load() {
        if(!this.loaded) {
            this.program = GLES20.glCreateProgram();
            DrainEmEngine.checkGlError("glCreateProgram");
            GLES20.glAttachShader(this.program, this.vertexShader.load());
            DrainEmEngine.checkGlError("glAttachShader"); // this gives Error 1281
            GLES20.glAttachShader(this.program, this.fragmentShader.load());
            DrainEmEngine.checkGlError("glAttachShader");
            GLES20.glLinkProgram(this.program);
            DrainEmEngine.checkGlError("glLinkProgram");
        }

        return this.program;
    }
}

我知道这远非完整,但我只是从这个项目开始......

我的引擎应该带有一些预定义的着色器,而3d对象可以从引擎获得着色器或程序。

// In constructor of the 3d object
this.program = DrainEmEngine.getInstance().getProgram("default");
// I also tried this, but same effect: 
this.program = new Program(DrainEmEngine.getInstance().getProgram("default"));

默认程序初始化如下:

    public void registerDefaultShaders() {
        this.registerShader(new VertexShader("default_ver", 
                "uniform mat4 uMVPMatrix;" +
                "attribute vec4 vPosition;" +
                "void main() {" +
                "  gl_Position = uMVPMatrix * vPosition;" +
                "}"));
        this.registerShader(new FragmentShader("default_frag", 
                "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor;" +
                "}"));
    }

    public void registerDefaultPrograms() {
        this.registerProgram(new Program("default", (VertexShader)this.getShader("default_ver"), (FragmentShader)this.getShader("default_frag")));
    }

这是我的渲染/绘制方法:

public void render(float[] mvpMatrix) {
        // Add program to OpenGL ES environment
        // this.program.unload(); // If this line is commented out it crashes!
        int program = this.program.load();
        GLES20.glUseProgram(program);
        DrainEmEngine.checkGlError("glUseProgram");

        // get handle to vertex shader's vPosition member
        int positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
        DrainEmEngine.checkGlError("glGetAttribLocation");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(positionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(positionHandle, Mesh.COORDS_PER_VERTEX,
                                     GLES20.GL_FLOAT, false,
                                     Mesh.vertexStride, this.mesh.getVertexBuffer());

        // get handle to fragment shader's vColor member
        int colorHandle = GLES20.glGetUniformLocation(program, "vColor");
        DrainEmEngine.checkGlError("glGetUniformLocation");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(colorHandle, 1, color, 0);
        DrainEmEngine.checkGlError("glUniform4fv");

        // get handle to shape's transformation matrix
        int MVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
        DrainEmEngine.checkGlError("glGetUniformLocation");

        updateModelMatrix();
        float[] tmp = mvpMatrix.clone();
        Matrix.multiplyMM(tmp, 0, mvpMatrix, 0, modelMatrix, 0);

        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(MVPMatrixHandle, 1, false, tmp, 0);
        DrainEmEngine.checkGlError("glUniformMatrix4fv");

        // Draw 
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, this.mesh.getDrawOrder().length,
                GLES20.GL_UNSIGNED_SHORT, this.mesh.getDrawListBuffer());

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }

我在启动时加载程序和着色器,之后它们应该可以正常工作。

问题

但是,如果我注释掉标记的行(应该是我猜),一切都会崩溃,并在调用Program.load()后在glAttachShader函数中得到OpenGL错误1281。 但是,由于Shader成功编译(如果调用DrainEmEngine.checkGlError("...")足以验证)至少一次,它应该正常工作......

如果我强制着色器和程序在每个帧中重新编译(通过取消注释该行),它的工作正常,但是从我所知道的OpenGL不是那种方式。

由于我对OpenGL很陌生并且我的所有研究都没有帮助我解决问题,我希望你们中的一个发现我的错误或者能够以OpenGL的方式启发我并告诉我在哪里我的逻辑失败了。

解决方案

我不知道问题是什么,但感谢ExMix的帖子,我解决了它。

以下是我的Shader和Program类现在的样子:
(实际的修复方法是加载和卸载方法)

public abstract class Shader {
    protected String name;
    protected int type;
    protected String code;
    protected int shader;
    protected boolean loaded;

    public Shader(String name, String code) {
        this.name = name;
        this.code = code;
        this.loaded = false;
        this.type = -1;
    }

    public Shader(Shader s) {
        this.name = s.name;
        this.type = s.type;
        this.code = s.code;
        this.shader = -1;
        this.loaded = false;
    }

    public String getName() {
        return this.name;
    }

    public int getType() {
        return this.type;
    }

    public boolean isVertexShader() {
        return this.type == GLES20.GL_VERTEX_SHADER;
    }

    public boolean isFragmentShader() {
        return this.type == GLES20.GL_FRAGMENT_SHADER;
    }

    public void unload() {
        if(this.loaded) {
            GLES20.glDeleteShader(this.shader);
            DrainEmEngine.checkGlError("glDeleteShader");
            this.loaded = false;
        }
    }

    public int load() {
        if(!this.loaded) {
            this.shader = GLES20.glCreateShader(this.type);

            GLES20.glShaderSource(this.shader, this.code);
            GLES20.glCompileShader(this.shader);
            DrainEmEngine.checkGlError("glCompileShader");

            this.loaded = true;
        }

        return this.shader;
    }
}

public class Program {
    private String name;
    private VertexShader vertexShader;
    private FragmentShader fragmentShader;
    private int program;
    private boolean loaded;

    public Program(String name, VertexShader vertexShader, FragmentShader fragmentShader) {
        this.name = name;
        this.vertexShader = vertexShader;
        this.fragmentShader = fragmentShader;
        this.program = -1;
        this.loaded = false;
    }

    public Program(Program p) {
        this.name = p.name;
        this.vertexShader = new VertexShader(p.vertexShader);
        this.fragmentShader = new FragmentShader(p.fragmentShader);
        this.program = -1;
        this.loaded = false;
    }

    public String getName() {
        return this.name;
    }
    public VertexShader getVertexShader() {
        return this.vertexShader;
    }

    public FragmentShader getFragmentShader() {
        return this.fragmentShader;
    }

    public void unload() {
        this.vertexShader.unload();
        this.fragmentShader.unload();
        this.loaded = false;
    }

    public int load() {
        if(!this.loaded) {
            this.program = GLES20.glCreateProgram();
            DrainEmEngine.checkGlError("glCreateProgram");
            int vert = this.vertexShader.load();
            GLES20.glAttachShader(this.program, vert);
            DrainEmEngine.checkGlError("glAttachShader");
            int frag = this.fragmentShader.load();
            GLES20.glAttachShader(this.program, frag);
            DrainEmEngine.checkGlError("glAttachShader");
            GLES20.glLinkProgram(this.program);
            DrainEmEngine.checkGlError("glLinkProgram");
            GLES20.glDetachShader(this.program, vert);
            DrainEmEngine.checkGlError("glDetachShader");
            GLES20.glDetachShader(this.program, frag);
            DrainEmEngine.checkGlError("glDetachShader");
            this.vertexShader.unload();
            this.fragmentShader.unload();
        }

        return this.program;
    }
}

1 个答案:

答案 0 :(得分:1)

首先,您的代码实际上不会卸载着色器和程序。 您漏掉了OpenGL资源。

卸载你必须做: 1)顶点和片段着色器的glDetachShader(programID,shaderID) 2)顶点和片段着色器的glDeleteShader(shaderID) 3)glDeleteProgram(programID)

之后,您的着色器将完全卸载。

但如果你取消注释"卸载"行代码将在每个帧上创建新的gpu着色器对象和gpu程序对象(不是你的包装器)。

此外,在程序链接后,您可以分离着色器并将其删除。这个过程就像C ++编译一样。来自* .obj文件的程序链接,但在progran链接后。* .jj文件不需要。

关于崩溃。你确定它在第一帧崩溃了吗?你可以插入一些静态计数器,它会在崩溃前计算帧吗?它可能会崩溃,因为OpenGL在某些时刻无法为着色器和程序分配名称。

您还需要检查checkGlError函数。

http://www.khronos.org/opengles/sdk/docs/man/

  

为了允许分布式实现,可能存在多个错误标志。如果任何单个错误标志记录了错误,则返回该标志的值,并在调用glGetError时将该标志重置为GL_NO_ERROR。如果多个标志记录了错误,glGetError将返回并清除任意错误标志值。因此,如果要重置所有错误标志,则应始终在循环中调用glGetError,直到它返回GL_NO_ERROR。