openglglslshadervbovao

what is the correct way to use glBindAttribLocation()?


I have been trying to learn OpenGL recently. I am a quite confused on the correct usage of glBindAttribLocation because I thought that it sets attributes in the shader(such as in vec3 position;) to data in the VAO (for example attribute 0 in the VAO). However when I comment out the line(line 136) the triangle still renders fine with it's colours so the shader must know about the position and colour data some other way, but I'm confused as to how. What line tells the shader about the data or does the shader just automatically read the attributes in from a VAO?

I was told in a previous question that the line needs to be before the shader linking so I moved it around but the line still doesn't seem to make a difference in my program.

here is the link to my code (its 215 lines): https://github.com/OneEgg42/opengl/blob/main/Main.java

my code:

public static void main(String[] args) {
    //see text file for all the comments
    String vertexSource = "#version 400 core\n"
            + "in vec3 position;\n"
            + "in vec3 colour;\n"
            + "out vec3 vertexColour;\n"
            + "uniform mat4 model;\n"
            + "uniform mat4 view;\n"
            + "uniform mat4 projection;\n"
            + "void main() {\n"
            + "vertexColour = colour;\n"
            + "mat4 mvp = projection * view * model;\n"
            + "gl_Position = vec4(position, 1.0);\n"
            + "}\n";
    
    String fragmentSource = "#version 400 core\n"
            + "in vec3 vertexColour;\n"
            + "out vec4 colours;\n"
            + "void main()\n"
            + "{\n"
            + "    colours = vec4(vertexColour, 1);\n"
            + "}";
    
    glfwSetErrorCallback(errorCallBack);
    
    if (!glfwInit()) {
        throw new IllegalStateException("Unable to initialize GLFW");
    }
        
    glfwDefaultWindowHints();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
    
    glfwWindowHint(GLFW_VISIBLE, GL_TRUE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    
    long window = glfwCreateWindow(640, 480, "my window!", 0, 0);

    glfwSetKeyCallback(window, keyCallback);
    if (window == 0) {
        glfwTerminate();
        throw new RuntimeException("Failed to create the GLFW window");
    }
    
    glfwMakeContextCurrent(window);
    GL.createCapabilities();
    
    //the openGL positions
    float[] vertexPoints = {
        0f,0.5f,0f,
        -0.5f,-0.5f,0f,
        0.5f,-0.5f,0f
    };
    float[] colours = {
            0.1f,0.5f,0.5f,
            0.3f,0.7f,0.9f,
            0.4f,0.9f,0.1f
        };
    
    //so that we can delete the vbos and vaos later
    ArrayList<Integer> vaos = new ArrayList<Integer>();
    ArrayList<Integer> vbos = new ArrayList<Integer>();
    
    //creating an empty VAO and it returns the id of that vao
    int vaoID = glGenVertexArrays();
    vaos.add(vaoID);
    glBindVertexArray(vaoID);
    
    FloatBuffer vertices = BufferUtils.createFloatBuffer(vertexPoints.length);
    vertices.put(vertexPoints);
    vertices.flip();
    
    int vboID = GL33.glGenBuffers();
    vbos.add(vboID);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
    
    //and one for the colour data
    vboID = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glBufferData(GL_ARRAY_BUFFER, colours, GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
    
    int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);      
    int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
    int shaderProgramID = glCreateProgram();
    
    int floatSize = 4;//4 bytes

    int positionAttribute = glGetAttribLocation(shaderProgramID, "position");
    glEnableVertexAttribArray(positionAttribute);
    glBindAttribLocation(shaderProgramID, positionAttribute, "position");
    
    //setting colour attribute in the shader:
    int colourAttribute = glGetAttribLocation(shaderProgramID, "colour");
    glEnableVertexAttribArray(colourAttribute);
    //glVertexAttribPointer(colourAttribute, 3, GL_FLOAT, false, 6 * floatSize, 3 * floatSize);
    glBindAttribLocation(shaderProgramID, colourAttribute, "colour");
    
    //vertex
    glShaderSource(vertexShaderID, vertexSource);
    glCompileShader(vertexShaderID);
    
    if (!checkForSuccess(vertexShaderID)) {
        
        glfwTerminate();
        throw new IllegalStateException("vertex shader failed: " + glGetShaderInfoLog(vertexShaderID));
    }
    //fragment
    glShaderSource(fragmentShaderID, fragmentSource);
    glCompileShader(fragmentShaderID);
    
    if (!checkForSuccess(vertexShaderID)) {
        glGetShaderInfoLog(vertexShaderID);
        glfwTerminate();
        throw new IllegalStateException("fragment shader failed");
    }
    
    //shader program
    
    glAttachShader(shaderProgramID, vertexShaderID);
    glAttachShader(shaderProgramID, fragmentShaderID);
    glLinkProgram(shaderProgramID);
    //error test
    int status = glGetProgrami(shaderProgramID, GL_LINK_STATUS);
    if (status != GL_TRUE) {
        glfwTerminate();
        throw new RuntimeException(glGetProgramInfoLog(shaderProgramID));
    }
    
    glUseProgram(shaderProgramID);
    

////////////////////////////////////////////////////////////////////////////// glClearColor(0.5f,0.5f,0.5f,1f);

    //this will create a wire frame view
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    glfwShowWindow(window); //optional
    
    while (!glfwWindowShouldClose(window)) {
        double time = glfwGetTime();
        glfwPollEvents();
        glClear(GL_COLOR_BUFFER_BIT);
        
        glBindVertexArray(vaoID);
        glDrawArrays(GL_TRIANGLES, 0, vertexPoints.length);
        
        glfwSwapBuffers(window);
    }
    
    //clear vaos and vbos
    for (int vao : vaos) {
        glDeleteVertexArrays(vao);
    }
    for (int vbo : vbos) {
        glDeleteBuffers(vbo);
    }
    //delete shaders
    glDetachShader(shaderProgramID, vertexShaderID);
    glDetachShader(shaderProgramID, fragmentShaderID);
    glDeleteShader(vertexShaderID);
    glDeleteShader(fragmentShaderID);
    glDeleteProgram(shaderProgramID);
    
    Callbacks.glfwFreeCallbacks(window);
    
    glfwDestroyWindow(window);
    glfwTerminate();
   
}

public static boolean checkForSuccess(int shaderID) {
    int status = glGetShaderi(shaderID, GL_COMPILE_STATUS);
    return status == GL_TRUE;
}

Thanks in advance for any help!


Solution

  • Your code doesn't really make sense. If you haven't already linked shaderProgramID, then you cannot call glGetAttribLocation. And if you have linked shaderProgramID, then calls to glBindAttribLocation will do nothing, since they only have an effect on subsequent linking operations.

    Lastly, if you already have a location for the attribute (that is, if glGetAttribLocation worked), there is no point in calling glBindAttribLocation, since you already know the answer. So there is never any point to calling both get and bind on the same program ever. Either you correctly told OpenGL what attribute location to use (and therefore have no reason to ask later) or you want to query the attribute location (and therefore don't want to specify it).

    A linked GLSL program contains a mapping between attribute names and locations. You can define this mapping in various ways. But if you don't provide an attribute with a location before you link the program, then OpenGL will assign that attribute a location. And once that happens, there is nothing you can do about it for that program.

    The best way to set the attribute location is from within the shader, with a layout(location = #) specifier. That way, you don't have to query anything from glGetAttribLocation, and there's no need to use glBindAttribLocation. Pick a standard convention for attribute indices and go from there. For example, you could say that standard colors all use attribute 2. You don't need to ask the program what attribute index their color is in; you know it's 2.

    This is ultimately no different from having a convention for naming attributes. Your above code assumes that the attributes are named "position" and "colour". If the maker of a shader uses the wrong names, your code won't work. Using a number instead of a name is no different, and you get to avoid asking for the attribute location.