Reputation: 73
I have been learning OpenGL recently and I have got bit confused about shaders and the VBOs.
Basically, I am confused on how my vertex shader knows about the colour and position data in my VBOs.
To my knowledge glBindAttribLocation(program, attribute, variable);
is what sets the attribute in the shader but when I comment out that code, the triangle still renders fine with it's colours.
Here is my code:
public static void main(String[] args) {
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();
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
};
ArrayList<Integer> vaos = new ArrayList<Integer>();
ArrayList<Integer> vbos = new ArrayList<Integer>();
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);
//add it to the vao at position 0, size 3, with data types of float, normalised = false,
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
//unbind currently bound vbo
glBindBuffer(GL_ARRAY_BUFFER, 0);
//and one for the colour data
vboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, colours, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//finished with vao now unbind it
glBindVertexArray(0);
//vao and vbo stuff is now finished with
//now for the shader stuff
int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderID, vertexSource);
glCompileShader(vertexShaderID);
if (!checkForSuccess(vertexShaderID)) {
glfwTerminate();
throw new IllegalStateException("vertex shader failed: " + glGetShaderInfoLog(vertexShaderID));
}
int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderID, fragmentSource);
glCompileShader(fragmentShaderID);
if (!checkForSuccess(vertexShaderID)) {
glGetShaderInfoLog(vertexShaderID);
glfwTerminate();
throw new IllegalStateException("fragment shader failed");
}
//shader program
int shaderProgramID = glCreateProgram();
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);
//setting position attribute in the shader:
int positionAttribute = glGetAttribLocation(shaderProgramID, "position");
glEnableVertexAttribArray(positionAttribute);
glBindAttribLocation(shaderProgramID, positionAttribute, "position");
//setting colour attribute in the shader:
int colourAttribute = glGetAttribLocation(shaderProgramID, "colour");
glEnableVertexAttribArray(colourAttribute);
glBindAttribLocation(shaderProgramID, colourAttribute, "colour");
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);
//enables the position attribute in the vertex shader
glEnableVertexAttribArray(0);
//enables the colour attribute in the vertex shader
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, vertexPoints.length);
//disables the position and colour attribute in the vertex shader
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
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();
}
I have provided my full code file on github (250 lines) here: https://github.com/OneEgg42/opengl
Would someone be so kind and explain to me how this works?
Thanks in advance!
Upvotes: 2
Views: 1326
Reputation: 22168
The relation between a VBO and a shader is not established by glBindAttribLocation
.
The relevant lines are actually
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
where you tell OpenGL that the attribute with location 0 (the first parameter in the first glVertexAttribPointer
call) should read the first three floats from vboID
.
glBindAttribLocation
is responsible for binding a certain attribute to a specific location. Since this has to be done before linking the shader, they aren't doing anything in your code. Even more so, because this code tells the attribute to use the location that it already has:
int positionAttribute = glGetAttribLocation(shaderProgramID, "position");
glEnableVertexAttribArray(positionAttribute);
glBindAttribLocation(shaderProgramID, positionAttribute, "position");
You basically query the location, and then assign the location again to the attribute.
Two side notes:
At the moment your code works by pure luck. You assume in the glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
line that the "position" attribute is at location 0, which it probably is as long as it is the first attribute written in the shader. Same for colors. It is very likely that your code breaks when you exchange the order of the two attribute definitions in your shader.
Either query the attribute location with glGetAttribLocation
and use the result instead of 0 and 1, or use glBindAttribLocation
before linking to make sure that the attributes are at the location you expect them.
The calls to glEnableVertexAttribArray
should be in the VAO setup code and not in the render loop.
Setup:
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
Render:
glBindVertexArray(vaoID);
glDrawArrays(GL_TRIANGLES, 0, vertexPoints.length);
Upvotes: 1