Search code examples
javaopenglshaderlwjgl

I can't change the color in my fragment shader


Im learning OpenGL and Im trying to draw a red triangle but my triangle stay black.

This is the code of my fragment shader:

#version 330 core

out vec4 color;

void main()
{
    color = vec4(1,0,0,1);
}

And my shaders compile without issue, I use this class for using shaders:

public class ShaderProgram
{
    private final int programId;

    private int vertexShaderId;

    private int fragmentShaderId;

    public ShaderProgram() throws Exception
    {
        programId = glCreateProgram();
        if (programId == 0) {
            throw new Exception("Could not create Shader");
        }
    }

    public void createVertexShader(String shaderCode) throws Exception {
        vertexShaderId = createShader(shaderCode, GL_VERTEX_SHADER);
    }

    public void createFragmentShader(String shaderCode) throws Exception {
        fragmentShaderId = createShader(shaderCode, GL_FRAGMENT_SHADER);
    }

    protected int createShader(String shaderCode, int shaderType) throws Exception {
        int shaderId = glCreateShader(shaderType);
        if (shaderId == 0) {
            throw new Exception("Error creating shader. Code: " + shaderId);
        }

        glShaderSource(shaderId, shaderCode);
        glCompileShader(shaderId);

        if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
            throw new Exception("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
        }

        glAttachShader(programId, shaderId);

        return shaderId;
    }

    public void link() throws Exception {
        glLinkProgram(programId);
        if (glGetProgrami(programId, GL_LINK_STATUS) == 0) {
            throw new Exception("Error linking Shader code: " + glGetShaderInfoLog(programId, 1024));
        }

        glValidateProgram(programId);
        if (glGetProgrami(programId, GL_VALIDATE_STATUS) == 0) {
            System.err.println("Warning validating Shader code: " + glGetShaderInfoLog(programId, 1024));
        }

    }

    public void bind() {
        glUseProgram(programId);
    }

    public void unbind() {
        glUseProgram(0);
    }

    public void cleanup() {
        unbind();
        if (programId != 0) {
            if (vertexShaderId != 0) {
                glDetachShader(programId, vertexShaderId);
            }
            if (fragmentShaderId != 0) {
                glDetachShader(programId, fragmentShaderId);
            }
            glDeleteProgram(programId);
        }
    }
}

Then this is my Game class:

public class Game {

    int vertexArrayID;
    int vertexBufferID;

    ShaderProgram fragmentShader;
    ShaderProgram vertexShader;

    float[] vertexData =
        {
            -1.0f, -1.0f, 0.0f,
            1.0f, -1.0f, 0.0f,
            0.0f,  1.0f, 0.0f,
        };

    FloatBuffer vertexBuffer;

    public Game() throws Exception {
        vertexArrayID = glGenVertexArrays();
        glBindVertexArray(vertexArrayID);

        vertexBufferID = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);

        vertexBuffer = BufferUtils.createFloatBuffer(vertexData.length);
        vertexBuffer.put(vertexData);
        vertexBuffer.flip();

        glBufferData(GL_ARRAY_BUFFER, vertexBuffer,GL_STATIC_DRAW);

        fragmentShader = new ShaderProgram();
        vertexShader = new ShaderProgram();

        fragmentShader.createFragmentShader(loadShader("fragmentShader.glsl"));
        vertexShader.createVertexShader(loadShader("vertexShader.glsl"));

        fragmentShader.link();
        vertexShader.link();
    }

    public void update()
    {
        fragmentShader.bind();
        vertexShader.bind();

        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER,vertexBufferID);
        glVertexAttribPointer(0,3,GL_FLOAT,false,0,0);

        glDrawArrays(GL_TRIANGLES,0,3);
        glDisableVertexAttribArray(0);

        fragmentShader.unbind();
        vertexShader.unbind();
    }

    private String loadShader(String input) {
        String r = "";

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(Game.class.getResourceAsStream("/shaders/" + input)));
            String buffer = "";
            while ((buffer = reader.readLine()) != null) {
                r += buffer + "\n";
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return r;
    }
}

Why my triangle stay black after that ?


Solution

  • It looks like you're misunderstanding the use of Programs and Shaders in OpenGL.

    Shader objects correspond to "Shader Stages" in OpenGL, i.e. the Vertex Shader or the Fragment Shader or one of the other programmable stages in the OpenGL graphics pipeline.

    Program objects correspond to the assembled chain of Shader Stages, sometimes referred to colloquially as the complete "Shader".

    In your program, however, what you appear to be doing is creating two programs, attaching a vertex shader to one and attaching a fragment shader to the other, and using both of them. To my knowledge, this won't cause OpenGL to throw an error (it'll just use a basic pass-through shader for whichever stage is missing) but that also means that only one of those stages will be active at any time, negating the other. In your case, the code in your update function:

    fragmentShader.bind();
    vertexShader.bind();
    

    Means that only the Vertex Shader is being used, which is why making changes to your Fragment Shader is having no effect.

    To fix this, create only one ShaderProgram object, and attach both the Vertex and Fragment shader source code to that object with respective calls to create[X]Shader. Link it once, and then in the update function, only make a single call to use. The program should work as expected once you've done this.