Search code examples
javaopenglglslshaderlwjgl

OpenGL drawing triangle doesn't show + glClearColor not setting screen color


I have a project im working on where it creates a window, creates the vertex and shaders, shader program, MemoryBuffer, etc... I have gotten through a ton of errors so far, and there are no errors when I run the code. The glClearColor() call doesn't set the screen color, and no triangle is drawn. Tutorial I'm following: https://github.com/SilverTiger/lwjgl3-tutorial/wiki/Rendering

Main:

public class DungeonRunners {
private double lastTick;

private float timeCount;

private int fps;
private int fpsCount;
private int ups;
private int upsCount;

public static void main(String[] args) {
    System.out.println("LWJGL Version: " + Version.getVersion());
    int width = 1240;
    int height = 720;
    boolean legacyGL = false;
    for (int i = 0; i < args.length; i++) {
        String arg = args[i];
        switch (arg) {
            case "width:":
                try {
                    width = Integer.parseInt(args[i+1]);
                    i++;
                } catch (NumberFormatException ignored) { }
                break;
            case "height:":
                try {
                    height = Integer.parseInt(args[i+1]);
                } catch (NumberFormatException ignored) { }
                break;
            case "-useLegacyGL":
                legacyGL = true;
                break;
        }
    }
    DungeonRunners dungeonRunners = new DungeonRunners();
    dungeonRunners.start(width, height, legacyGL);
}

private void start(int width, int height, Boolean legacyGL) {
    GLFW.glfwInit();
    if(!legacyGL) {
        GLFW.glfwDefaultWindowHints();
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3);
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 2);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE);
    } else {
        GLFW.glfwDefaultWindowHints();
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 2);
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 1);

    }
    GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
    long window = GLFW.glfwCreateWindow(width, height, "Dungeon Runners", MemoryUtil.NULL, MemoryUtil.NULL);
    GLFW.glfwMakeContextCurrent(window);
    GL.createCapabilities(true);
    RenderEngine engine = new RenderEngine(legacyGL);
    GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
    GLFW.glfwShowWindow(window);
    try ( MemoryStack stack = MemoryStack.stackPush() ) {
        IntBuffer pWidth = stack.mallocInt(1);
        IntBuffer pHeight = stack.mallocInt(1);
        GLFW.glfwGetWindowSize(window, pWidth, pHeight);
        GLFWVidMode vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
        assert vidmode != null;
        GLFW.glfwSetWindowPos(
                window,
                (vidmode.width() - pWidth.get(0)) / 2,
                (vidmode.height() - pHeight.get(0)) / 2
        );
    }
    while(!GLFW.glfwWindowShouldClose(window)) {
        update();
        engine.frame();
        GLFW.glfwPollEvents();
    }
    engine.clean();
    GL.destroy();
    GLFW.glfwDestroyWindow(window);
    GLFW.glfwTerminate();
    try {
        Objects.requireNonNull(GLFW.glfwSetErrorCallback(null)).free();
    } catch(NullPointerException e) {
        System.exit(-1);
    }
}

private void update() {
    if (timeCount > 1f) {
        fps = fpsCount;
        fpsCount = 0;

        ups = upsCount;
        upsCount = 0;

        timeCount -= 1f;
    }
}

private float getDelta() {
    double time = getTime();
    float delta = (float) (time - lastTick);
    lastTick = time;
    timeCount += delta;
    return delta;
}

private double getTime() {
    return System.nanoTime() / 1000000000.0;
}

private void updateFPS() {
    fpsCount++;
}

private void updateUPS() {
    upsCount++;
}
}

RenderEngine:

public class RenderEngine {

private int shaderProgram;
private int vao = GL30.glGenVertexArrays();
private int fragmentShader;
private int vertexShader;
private List<Integer> vbos = new ArrayList<>();
private String vertexShaderCode =
        "#version 150 core\n"+
                "\n"+
                "in vec3 position;\n"+
                "in vec3 color;\n"+
                "\n"+
                "out vec3 vertexColor;\n"+
                "\n"+
                "uniform mat4 model;\n"+
                "uniform mat4 view;\n"+
                "uniform mat4 projection;\n"+
                "\n"+
                "void main() {\n"+
                "    vertexColor = color;\n"+
                "    mat4 mvp = projection * view * model;\n"+
                "    gl_Position = mvp * vec4(position, 1.0);\n"+
                "}";
private String fragmentShaderCode =
        "#version 150 core\n"+
        "\n"+
        "in vec3 vertexColor;\n"+
        "\n"+
        "out vec4 fragColor;\n"+
        "\n"+
        "void main() {\n"+
        "    fragColor = vec4(vertexColor, 1.0);\n"+
        "}";
private String legacyVertexShaderCode =
        "#version 120\n"+
                "\n"+
                "attribute vec3 position;\n"+
                "attribute vec3 color;\n"+
                "\n"+
                "varying vec3 vertexColor;\n"+
                "\n"+
                "uniform mat4 model;\n"+
                "uniform mat4 view;\n"+
                "uniform mat4 projection;\n"+
                "\n"+
                "void main() {\n"+
                "    vertexColor = color;\n"+
                "    mat4 mvp = projection * view * model;\n"+
                "    gl_Position = mvp * vec4(position, 1.0);\n"+
                "}";
private String legacyFragmentShaderCode =
        "#version 120\n"+
        "\n"+
        "varying vec3 vertexColor;\n"+
        "\n"+
        "void main() {\n"+
        "    gl_FragColor = vec4(vertexColor, 1.0);\n"+
        "}";

public RenderEngine(Boolean legacyGL) {
    GL30.glBindVertexArray(vao);
    setVertexAttribs();
    setupShaders(legacyGL);
    GL20.glUseProgram(shaderProgram);
    setUniformVars();
    bindImage();
}

private void bindImage() {
    MemoryStack stack = MemoryStack.stackPush();
    FloatBuffer vertices = stack.mallocFloat(3*6);
    vertices.put(-0.6f).put(-0.4f).put(0f).put(1f).put(0f).put(0f);
    vertices.put(0.6f).put(-0.4f).put(0f).put(0f).put(1f).put(0f);
    vertices.put(0f).put(0.6f).put(0f).put(0f).put(0f).put(1f);
    vertices.flip();
    int vbo = GL15.glGenBuffers();
    vbos.add(vbo);
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
    MemoryStack.stackPop();
}

public void frame() {
    GL11.glClearColor(1, 0, 0, 0);
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
    bindImage();
    GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
}

public void clean() {
    GL30.glDeleteVertexArrays(vao);
    for(int vbo : vbos)
        GL15.glDeleteBuffers(vbo);
    GL20.glDeleteShader(vertexShader);
    GL20.glDeleteShader(fragmentShader);
    GL20.glDeleteProgram(shaderProgram);
}

private void setUniformVars() {
    int uniModel = GL20.glGetUniformLocation(shaderProgram, "model");
    UtilMatrix4f model = new UtilMatrix4f();
    GL20.glUniformMatrix4fv(uniModel, false, model.getBuffer());

    int uniView = GL20.glGetUniformLocation(shaderProgram, "view");
    UtilMatrix4f view = new UtilMatrix4f();
    GL20.glUniformMatrix4fv(uniView, false, view.getBuffer());

    int uniProjection = GL20.glGetUniformLocation(shaderProgram, "projection");
    float ratio = 640f/480f;
    UtilMatrix4f projection = UtilMatrix4f.orthographic(-ratio, ratio, -1f, 1f, -1f, 1f);
    GL20.glUniformMatrix4fv(uniProjection, false, projection.getBuffer());
}

private void setVertexAttribs() {
    int floatSize = 4;
    int posAttrib = GL20.glGetAttribLocation(shaderProgram, "position");
    GL20.glEnableVertexAttribArray(posAttrib);
    GL20.glVertexAttribPointer(posAttrib, 3, GL11.GL_FLOAT, false, 6*floatSize, 0);
    int colAttrib = GL20.glGetAttribLocation(shaderProgram, "color");
    GL20.glEnableVertexAttribArray(colAttrib);
    GL20.glVertexAttribPointer(colAttrib, 3, GL11.GL_FLOAT, false, 6*floatSize, 3*floatSize);
}

private void setupShaders(boolean legacyGL) {
    vertexShader = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
    if (legacyGL)
        GL20.glShaderSource(vertexShader, legacyVertexShaderCode);
    else
        GL20.glShaderSource(vertexShader, vertexShaderCode);
    GL20.glCompileShader(vertexShader);
    int status = GL20.glGetShaderi(vertexShader, GL20.GL_COMPILE_STATUS);
    if (status != GL11.GL_TRUE) {
        throw new RuntimeException(GL20.glGetShaderInfoLog(vertexShader));
    }
    fragmentShader = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
    if (legacyGL)
        GL20.glShaderSource(fragmentShader, legacyFragmentShaderCode);
    else
        GL20.glShaderSource(fragmentShader, fragmentShaderCode);
    GL20.glCompileShader(fragmentShader);
    status = GL20.glGetShaderi(fragmentShader, GL20.GL_COMPILE_STATUS);
    if (status != GL11.GL_TRUE) {
        throw new RuntimeException(GL20.glGetShaderInfoLog(fragmentShader));
    }
    shaderProgram = GL20.glCreateProgram();
    GL20.glAttachShader(shaderProgram, vertexShader);
    GL20.glAttachShader(shaderProgram, fragmentShader);
    GL30.glBindFragDataLocation(shaderProgram, 0, "fragColor");
    GL20.glLinkProgram(shaderProgram);
    status = GL20.glGetProgrami(shaderProgram, GL20.GL_LINK_STATUS);
    if (status != GL11.GL_TRUE) {
        throw new RuntimeException(GL20.glGetProgramInfoLog(shaderProgram));
    }
}
}

Solution

  • glfwSwapBuffers is missing, to make the rendering "visible":

    while(!GLFW.glfwWindowShouldClose(window)) {
        update();
        engine.frame();
        GLFW.glfwSwapBuffers(window);
        GLFW.glfwPollEvents();
    }
    

    The location of the vertex attributes has to be get, after the shader program is linked and the vertex array object is bound:

    setupShaders(legacyGL);
    bindImage();
    GL30.glBindVertexArray(vao);
    setVertexAttribs();
    GL20.glUseProgram(shaderProgram);
    setUniformVars();
    

    Note, glGetAttribLocation ask for the index of an active program resource and the active program resources are determined when liking the shader program by glLinkProgram.
    Further the named vertex buffer has to be bound by glBindBuffer before glVertexAttribPointer is called, if the last parameter should be treated as a byte offset into the buffer object's data store (and of course especially in core mode).