Search code examples
openglglsllwjgl

Not able to configure vertex attributes correctly in Opengl 2.x


Problem: I can't make the triangle to appear. I'm not able to set up the vertex attributes correctly only by using OpenGL 2.0

Result of a run of the code: Red window 600*800 without a triangle.

Objective: I try to:

  • associate the position to attribute array 0, float x 2 (for a start)
  • associate the color to attribute array 1, int x 3
  • indexed vertices.

Java code:

package prot20;

import static org.lwjgl.glfw.GLFW.GLFW_FALSE;
import static org.lwjgl.glfw.GLFW.GLFW_RESIZABLE;
import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints;
import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor;
import static org.lwjgl.glfw.GLFW.glfwInit;
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
import static org.lwjgl.glfw.GLFW.glfwPollEvents;
import static org.lwjgl.glfw.GLFW.glfwShowWindow;
import static org.lwjgl.glfw.GLFW.glfwSwapBuffers;
import static org.lwjgl.glfw.GLFW.glfwSwapInterval;
import static org.lwjgl.glfw.GLFW.glfwWindowHint;
import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glDrawElements;
import static org.lwjgl.opengl.GL11.glEnableClientState;
import static org.lwjgl.opengl.GL11.glGetString;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.system.MemoryUtil.NULL;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;

import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.opengl.GLCapabilities;

public class prot20_2 {
    private long window;
    private int width = 800;
    private int height = 600;

    public static void main(String[] args) {
        new prot20_2();
    }

    public prot20_2() {

        if (!glfwInit()) {
            throw new IllegalStateException("Unable to initialize GLFW");
        }

        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        long monitor = glfwGetPrimaryMonitor();

        window = glfwCreateWindow(width, height, "Window20", NULL, NULL);
        if (window == NULL) {
            throw new AssertionError("Failed to create window");
        }

        glfwMakeContextCurrent(window);
        glfwSwapInterval(0);
        glfwShowWindow(window);

        GLCapabilities c = GL.createCapabilities();

        System.out.println(c.OpenGL20);
        System.out.println(glGetString(GL11.GL_VERSION));

        int program = util.createProgram(prot20_2.getSimpleVertexShaderCode(), prot20_2.getSimpleFragmentShaderCode());


        float[] vertices = { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f };
        int[] color = { 0, 128, 128, 128, 0, 128, 128, 128, 0 };
        int[] indices = { 0, 1, 2 };

        FloatBuffer vb = this.toBuffer(vertices);
        IntBuffer ib = this.toBuffer(indices);
        IntBuffer cb = this.toBuffer(color);

        int vbo = glGenBuffers();
        int ibo = glGenBuffers();
        int cbo = glGenBuffers();

        // Position
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, vb, GL_STATIC_DRAW);
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
        glBindAttribLocation(program, 0, "position");
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // Color
        glBindBuffer(GL_ARRAY_BUFFER, cbo);
        glBufferData(GL_ARRAY_BUFFER, cb, GL_STATIC_DRAW);
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(0, 3, GL_INT, false, 0, 0);
        glBindAttribLocation(program, 1, "color");
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // indices
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, ib, GL_STATIC_DRAW);
        glEnableClientState(GL_INDEX_ARRAY);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        glUseProgram(program);

        while (glfwWindowShouldClose(this.window) == false) {
            glClearColor(1, 0, 0, 1);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glViewport(0, 0, width, height);
            glMatrixMode(GL_PROJECTION);

            float aspect = (float) width / height;
            glLoadIdentity();
            glOrtho(-aspect, aspect, -1, 1, -1, 1);

            // glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

            glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0L);

            glfwSwapBuffers(this.window);
            glfwPollEvents();

        }
        glDeleteBuffers(vbo);

    }

    private FloatBuffer toBuffer(float[] f) {
        FloatBuffer buffer = BufferUtils.createFloatBuffer(f.length);
        buffer.put(f);
        buffer.flip();
        return buffer;
    }

    private IntBuffer toBuffer(int[] i) {
        IntBuffer buffer = BufferUtils.createIntBuffer(i.length);
        buffer.put(i);
        buffer.flip();
        return buffer;
    }
    public static String getSimpleFragmentShaderCode() {
        StringBuilder sb = new StringBuilder();
        sb.append("uniform vec3 color;\n");
        sb.append("void main(void) {\n");
        sb.append("  gl_FragColor = vec4(color.rgb, 1.0);");
        sb.append("}\n");
        return sb.toString();
    }

    public static String getSimpleVertexShaderCode() {
        StringBuilder sb = new StringBuilder();
        sb.append("uniform vec2 position;\n");
        sb.append("void main(void) {\n");
        sb.append("  gl_Position = vec4(position.xy,0.0,0.0);\n");
        sb.append("}\n");
        return sb.toString();
    }
}

I have looked for documentation and examples, but either I find trivial examples without attributes, color, indices or shaders, or I find complex code using opengl 3+. I find it hard to locate a good example covering: opengl 2.1 including shader,index,attribute,color and texture.


Solution

  • If you want to draw a triangle with a color associated to each vertex coordinate then you need a vertex shader with 2 attributes. One for the vertex coordinate and one for the color. The color attribute has to be passed from the Vertex Shader to the Fragment Shader, by a varying variable.
    Note, attribute and varying are deprecated but have to be used in GLSL version 1.10, which corresponds to OpenGL version 2.0. In "moder" OpenGL the Type Qualifiers (GLSL) in and out are used:

    Vertex shader

    #version 110
    
    attribute vec2 in_pos;
    attribute vec3 in_col;  
    
    varying vec3 v_volor;
    
    void main()
    {
        v_color = in_col;
        gl_Position = vec4(in_pos.xy, 0.0, 1.0);
    }
    

    Fragment shader

    varying vec3 v_color;
    
    void main()
    {
        gl_FragColor = vec4(v_color.rgb, 1.0);
    }
    

    After the program is linked you have to get the attribute indices of in_pos and in_col by glGetAttribLocation:

    int attr_pos = glGetAttribLocation(program, "in_pos");
    int attr_col = glGetAttribLocation(program, "in_col");
    

    If you wat to set the attribute indices by glBindAttribLocation the this would have to be done, before the program is linked by glLinkProgram, because this is an information which is processed in the link process.

    The color channels in OpenGL Shading Language (GLSL) have to be in the range [0.0, 1.0] where RGB(0.0, 0.0, 0.0) is completely black and RGB(1.0, 1.0, 1.0) is white. So the color attributes have to be floating point values:

    float[] vertices = { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f };
    float[] color    = { 0, 0.5f, 0.5f, 0.5f, 0, 0.5f, 0.5f, 0.5f, 0 };
    int[]   indices  = { 0, 1, 2 };
    
    FloatBuffer vb = this.toBuffer(vertices);
    FloatBuffer cb = this.toBuffer(color);
    IntBuffer   ib = this.toBuffer(indices);
    

    Generate the array buffers and the index buffer:

    int vbo = glGenBuffers();
    int cbo = glGenBuffers();
    int ibo = glGenBuffers();
    
    // Position
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, vb, GL_STATIC_DRAW);
    
    // Color
    glBindBuffer(GL_ARRAY_BUFFER, cbo);
    glBufferData(GL_ARRAY_BUFFER, cb, GL_STATIC_DRAW);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    // indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, ib, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    Specify the arrays of generic vertex attribute data:

    // Position
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glEnableVertexAttribArray(attr_pos);
    glVertexAttribPointer(attr_pos, 2, GL_FLOAT, false, 0, 0);
    
    // Color
    glBindBuffer(GL_ARRAY_BUFFER, cbo);
    glEnableVertexAttribArray(attr_col);
    glVertexAttribPointer(attr_col, 3, GL_FLOAT, false, 0, 0);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    

    For drawing the object the program object has to installed by glUseProgram and the element buffer has to be bound:

    glUseProgram(program);
    
    while (glfwWindowShouldClose(this.window) == false) {
    
        // ....
    
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0L);
    
        // .....
    }
    

    Note, fixed-function attributes (glVertexPointer, ...), client capabilities (glEnableClientState/ glDisableClientState) and the fixed function matrix stack (glMatrixMode, glLoadIdentity, ...) have no effect, when a shader program like this is used.

    If you want to use matrix transformations, then you have to use Uniform variables of type mat4 and to setup your own projection, view and model matrices - e.g by Matrix4f library.