使用 glBindAttribLocation() 的正确方法是什么?

what is the correct way to use glBindAttribLocation()?

我最近一直在努力学习OpenGL。我对 glBindAttribLocation 的正确用法感到很困惑,因为我认为它将着色器中的属性(例如 in vec3 position;)设置为 VAO 中的数据(例如 VAO 中的属性 0)。但是,当我注释掉该行(第 136 行)时,三角形仍然可以很好地渲染它的颜色,因此着色器必须以其他方式了解位置和颜色数据,但我对如何做到这一点感到困惑。哪一行告诉着色器有关数据的信息,还是着色器只是自动从 VAO 中读取属性?

我在上一个问题中被告知该行需要在着色器 linking 之前,所以我移动了它,但该行似乎仍然没有对我的程序产生影响。

这是我的代码的 link(它的 215 行):https://github.com/OneEgg42/opengl/blob/main/Main.java

我的代码:

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;
}

在此先感谢您的帮助!

您的代码没有任何意义。如果您还没有 linked shaderProgramID,那么您不能调用 glGetAttribLocation。如果您 linked shaderProgramID,那么调用 glBindAttribLocation 将不会执行任何操作,因为它们只会影响后续的 linking 操作。

最后,如果您已经有了属性的位置(也就是说,如果 glGetAttribLocation 有效),那么调用 glBindAttribLocation没有意义 ,因为你已经知道答案了。所以在同一个程序 ever 上调用 getbind 从来没有任何意义。您要么正确地告诉 OpenGL 要使用什么属性位置(因此以后没有理由询问),要么您想要查询属性位置(因此不想指定它)。

linked GLSL 程序包含属性名称和位置之间的映射。你可以define this mapping in various ways。但是,如果在 之前 没有提供带有位置的属性 link 程序,则 OpenGL 将 分配 该属性的位置.一旦发生这种情况,没有什么你可以为那个程序做些什么。

设置属性位置的最佳方法是在着色器中使用 layout(location = #) 说明符。这样,您就不必从 glGetAttribLocation 查询任何内容,也无需使用 glBindAttribLocation。为属性索引选择一个标准约定,然后从那里开始。例如,您可以说标准颜色都使用属性 2。您无需询问程序它们的颜色在哪个属性索引中;你知道它是 2.

这最终与命名属性的约定没有什么不同。您上面的代码假定属性被命名为“position”和“colour”。如果着色器的制造商使用了错误的名称,您的代码将无法运行。使用数字 而不是 的名称没有什么不同,您可以避免询问属性位置。