PDA

View Full Version : I don't understand how to use Buffers



aligator
11-08-2015, 06:23 AM
I would like to display a triangle.

After reading some things about buffers, I tried it myself, but it doesn't work and I think I don't really understand them:

here is what I have:


void Data::render() {

const GLfloat color[] = { 1.0f, 0.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, color);

glUseProgram(renderingProgram);

// The type used for names in OpenGL is GLuint
GLuint buffer;

// Create a buffer
glCreateBuffers(1, &buffer);

// Specify the data store parameters for the buffer
glNamedBufferStorage(
buffer, // Name of the buffer
1024 * 1024, // 1 MiB of Space
NULL, // No Initial Data
GL_MAP_WRITE_BIT); // Allow map for writing

// Now bind to the context using the GL_ARRAY_BUFFER binding point
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Place Data into the buffer object
static const float data[] =
{
0.25, -0.25, 0.5, 1.0,
-0.25, -0.25, 0.5, 1.0,
0.25, -0.25, 0.5, 1.0
};

// Get a pointer to the buffer's data store
void * ptr = glMapNamedBuffer(buffer, GL_WRITE_ONLY);

// Copy our data into it...
memcpy(ptr, data, sizeof(data));

// Tell OpenGL that we're done with this pointer
glUnmapNamedBuffer(GL_ARRAY_BUFFER);

// First bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(
vertexArrayObject, // Vertex array object
0, // First vertex buffer binding
buffer, // Buffer object
0, // Start from the beginning
sizeof(vmath::vec4)); // Each vertex is one vec4

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(
vertexArrayObject, // Vertex array object
0, // First attribute
4, // Four components
GL_FLOAT, // Floating-point data
GL_FALSE, // Normalized - ignored for floats
0); // First element of the vertex
glEnableVertexArrayAttrib(vertexArrayObject, 0);

glDrawArrays(GL_TRIANGLES, 0, 3);
}

and my vertex shader:


#version 430 core

layout (location = 0) in vec4 position;

void main(void)
{
gl_Position = position;
}


In the shader I'am not sure how to access the data.
The data comes as float-array. but how can I access the data for one vertex?
Because each of the three vertex uses one vector of the data (for float-values).

but how can I tell the first vertex to use the first vec4 in data and the seccond vertex to use the seccond vec4?


Can anyone explain me how to do this correctly, please?

Alfonse Reinheart
11-08-2015, 06:54 AM
First, let's fix your code.

The way you wrote `render`, you will allocate 1MB of GPU memory every frame. You will use that 1MB exactly once, then you will drop it off the stack and forget about it. You will never deallocate this memory. So in about 500 to 4000 frames, you're going to run out of GPU memory.

You're supposed to allocate buffers before your render loop. And unless you're streaming vertices into them, you fill them with data outside of the render loop too. Then you just use the buffer to render your object.

Equally importantly, your VAO setup (https://www.opengl.org/wiki/Vertex_Specification#Vertex_Array_Object) should (mostly) be outside `render` as well. The only part that you should be doing inside the render loop is the attachment of the buffer to the VAO, the glVertexArrayVertexBuffer call.

Oh, and you never called glVertexArrayAttribBinding, which specifies which vertex attribute gets its data from which vertex buffer binding point.

Now, let's talk about vertex shaders (https://www.opengl.org/wiki/Vertex Shader). A vertex shader gets invoked (https://www.opengl.org/wiki/Shader#Execution_and_invocations) once for each vertex in the rendering command (mostly (https://www.opengl.org/wiki/Vertex_Shader#Invocation_frequency)). Think of a vertex shader as a function that gets called. Each time it gets called, its input variables are filled with data for that particular vertex, taken from the buffer object.

So your vertex shader does not go out and fetch the vertex attributes for a particular vertex; it is given them.

aligator
11-08-2015, 07:30 AM
Thank you for your answer.


The way you wrote `render`, you will allocate 1MB of GPU memory every frame. You will use that 1MB exactly once, then you will drop it off the stack and forget about it. You will never deallocate this memory. So in about 500 to 4000 frames, you're going to run out of GPU memory.

You're supposed to allocate buffers before your render loop. And unless you're streaming vertices into them, you fill them with data outside of the render loop too. Then you just use the buffer to render your object.

Equally importantly, your VAO setup should (mostly) be outside `render` as well. The only part that you should be doing inside the render loop is the attachment of the buffer to the VAO, the glVertexArrayVertexBuffer call.

Oh, and you never called glVertexArrayAttribBinding, which specifies which vertex attribute gets its data from which vertex buffer binding point.


so do I have to do this once, when starting the Program?


void Data::startup() {
renderingProgram = compile_shaders();
glCreateVertexArrays(1, &vertexArrayObject);
glBindVertexArray(vertexArrayObject);

// (buffer is now public)
// Create a buffer
glCreateBuffers(1, &buffer);

// Specify the data store parameters for the buffer
glNamedBufferStorage(
buffer, // Name of the buffer
1024 * 1024, // 1 MiB of Space
NULL, // No Initial Data
GL_MAP_WRITE_BIT); // Allow map for writing

// Now bind to the context using the GL_ARRAY_BUFFER binding point
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Place Data into the buffer object
static const float data[3][4] =
{
{0.25, -0.25, 0.5, 1.0},
{-0.25, -0.25, 0.5, 1.0},
{0.25, -0.25, 0.5, 1.0}
};

// Get a pointer to the buffer's data store
void * ptr = glMapNamedBuffer(buffer, GL_WRITE_ONLY);

// Copy our data into it...
memcpy(ptr, data, sizeof(data));

// Tell OpenGL that we're done with this pointer
glUnmapNamedBuffer(GL_ARRAY_BUFFER);
}

and the render function:

You said glVertexArrayVertexBuffer has to be here. And what about glVertexArrayAttribFormat, glVertexArrayAttribBinding and glEnableVertexArrayAttrib?
Should these be in render or startup-function?

void Data::render(double currentTime) {

const GLfloat color[] = { 1.0f, 0.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, color);

glUseProgram(renderingProgram);

// First bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(
vertexArrayObject, // Vertex array object
0, // First vertex buffer binding
buffer, // Buffer object
0, // Start from the beginning
sizeof(vmath::vec4)); // Each vertex is one vec4

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(
vertexArrayObject, // Vertex array object
0, // First attribute
4, // Four components
GL_FLOAT, // Floating-point data
GL_FALSE, // Normalized - ignored for floats
0); // First element of the vertex


glVertexArrayAttribBinding(vertexArrayObject, 0, 0);

glEnableVertexArrayAttrib(vertexArrayObject, 0);

glDrawArrays(GL_TRIANGLES, 0, 3);
}


Now, let's talk about vertex shaders. A vertex shader gets invoked once for each vertex in the rendering command (mostly). Think of a vertex shader as a function that gets called. Each time it gets called, its input variables are filled with data for that particular vertex, taken from the buffer object.

So your vertex shader does not go out and fetch the vertex attributes for a particular vertex; it is given them.
Is the shader I have correct? Does OpenGL simply give the shader allways the next four floats as vec4?

Alfonse Reinheart
11-08-2015, 09:49 AM
You said glVertexArrayVertexBuffer has to be here. And what about glVertexArrayAttribFormat, glVertexArrayAttribBinding and glEnableVertexArrayAttrib?

You should think about what is changing. Don't rely on someone telling you want to do; understand what the functions are doing.

I said that you should create your buffer in pre-rendering setup because you use the exact same data every time. So you don't need to be creating a new buffer object at every render loop invocation. You create it once, upload to it once, and use it as many times as you need to.

The same goes for your vertex arrays/format; you use the same format, so it should be set up once. You just bind the VAO you want to use and render from it.

The only reason I said to call glVertexArrayVertexBuffer in the render function is because you're very likely to want to change buffer objects later on. If you want to render two objects, then either each object is going to have its own buffer, or you'll put both objects in different areas of the same buffer. Either way, you're going to need to call glVertexArrayVertexBuffer to tell OpenGL which buffer/region to read from.

But if both objects use the same format, you don't need to set the format at render time. They can use the same VAO, and you just call glVertexArrayVertexBuffer to set the buffer/region to use.

Try to understand what the functions are doing, and it will become more obvious why you put code where you do.


Does OpenGL simply give the shader allways the next four floats as vec4?

You told OpenGL what to give the vertex shader. It was part of your vertex format, remember?



0, // First attribute
4, // Four components
GL_FLOAT, // Floating-point data


Right there, you told OpenGL that vertex attribute location 0 is being given 4 components per vertex, and the data type in the array for those components is floating-point.

That's the point of glVertexArrayVertexFormat; you're telling OpenGL what each input's data should be (https://www.opengl.org/wiki/Vertex_Specification#Vertex_format).

aligator
11-08-2015, 12:23 PM
Ok.
I did some more searching in the internet. Here is what I have now. I wrote to the functions what I think that they do. If there is something wrong, please tell me.


void Buffer::startup() {
renderingProgram = compile_shaders();



// 1. Create vertexArrayObject
// Create '1' new vertexArrayObject and write its pointer to 'vertexArrayObject'
glCreateVertexArrays(1, &vertexArrayObject);
// bind the vertexArrayObject
glBindVertexArray(vertexArrayObject);




// 2. Create Buffer
// setup data
static const float vertexPositions[] =
{
0.25, -0.25, 0.5, 1.0,
-0.25, -0.25, 0.5, 1.0,
0.25, -0.25, 0.5, 1.0
};

// Create '1' new buffer and write its pointer to 'vertexArrayObject'
glCreateBuffers(1, &buffer);
// bind the buffer as GL_ARRAY_BUFFER
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// add Data to the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); //GL_STATIC_DRAW -> data is set up one time and used very often


// 3. setup the vertex Attribute Array (where to find the data)
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0, // index of the VertexAttribute
4, // number of items
GL_FLOAT, // using float as datatype
GL_FALSE, // no normalization
0, // the floats are directly behind each other
0); // begin on address 0
}


void Buffer::render() {

const GLfloat color[] = { 0.0f, 1.0f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, color);

glUseProgram(renderingProgram);


// bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(
vertexArrayObject, // Vertex array object
0, // First vertex buffer binding
buffer, // Buffer object
0, // Start from the beginning
0); // the vec4 are directly behind each other

glVertexArrayAttribBinding(vertexArrayObject, 0, 0); // Associate attrib 0 (first 0) with binding 0 (second 0).

glDrawArrays(GL_TRIANGLES, 0, 3);
}

It is still not working.