PDA

View Full Version : Vertex data pipeline?



stimulate
10-08-2016, 06:37 AM
Hello forums,

i need some help understanding the "VAO Pipeline". i have had it working many times, but often times i change something, opengl would not render my models anymore. in the end it was often a simple issue like the order in which i call glBindBuffer and glEnableVertexAttribArray. now that my engine grows larger, i am getting these problems repeatedly. so here is the code i am having issues with:


//...COMPILE SHADER PROGRAMS AND USE THEM
//THIS IS A FUNCTION WHICH IS SUPPOSED TO DRAW A WIREFRAME PLANE
//IT IS CALLED EVERY FRAME
void Graphic::renderGrid()
{
static GLuint gridvID = 0;
static GLuint gridiID = 0;

static bool loaded = false;
//SOME HARDCODED VARIABLES
int scale = 10;
unsigned int vertexNum = scale * scale;
unsigned int indexNum = scale * scale * 6;
static unsigned int indexArray[600];
static Vertex vertexArray[100];

//ONLY LOAD THE GRID ONCE, USING A "LOADED" BOOLEAN
if (!loaded) {
glCreateBuffers(1, &gridvID);
glCreateBuffers(1, &gridiID);

//FILL VERTEX ARRAY WITH THIS ALGORITHM
for (int vert = 0; vert < vertexNum; vert++) {
static int itx = 0;
static int itz = 0;
vertexArray[vert].position.x = -1.0f*(scale / 2 - itx);
vertexArray[vert].position.y = 0.0f;
vertexArray[vert].position.z = -1.0f*(scale / 2 - itz);

vertexArray[vert].color.r = 1.0f;
vertexArray[vert].color.g = 1.0f;
vertexArray[vert].color.b = 1.0f;
vertexArray[vert].color.a = 1.0f;
if (itx == scale) {
itx = 0;
itz++;
}
else {
itx++;
}
}

//FILL INDEX ARRAY WITH THIS ALGORITHM
int runner = 0;
for (unsigned int row = 0; row < scale; row++) {
for (unsigned int col = 0; col < scale; col++) {
indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale;
indexArray[runner++] = scale *row + col + scale + 1;

indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale + 1;
indexArray[runner++] = scale *row + col + 1;
}

}

//UPLOAD VERTEX AND INDEX DATA
//RIGHT ORDER?
glBindBuffer(GL_ARRAY_BUFFER, gridvID);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertexNum, vertexArray, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridiID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*indexNum, indexArray, GL_STATIC_DRAW);

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
loaded = true;
}
//USER CONTROLS THIS BOOLEAN TO SHOW OR HIDE THE GRID
if (showGrid) {
//I WOULD LIKE THIS TO WORK WITHOUT ANY MATRICES, BECAUSE THE GRID SHOULD BE STATIC(OR CHANGE WITH THE PLAYER POSITION)
glm::mat4 translate = glm::translate(Camera::camMatrix(), glm::vec3(0.0f, 0.0f, 0.0f));
//IS THERE ANY WAY TO MAKE GLM::ROTATE WORK WITH A 0 VECTOR? WHENEVER I HAD A ROTATION VECTOR OF 0,0,0 THE MESH DISAPPEARED (BUT THATS A DIFFERENT PROBLEM)
glm::mat4 rotate = glm::rotate(translate, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 gridMatrix = glm::scale(rotate, glm::vec3(1.0f, 1.0f, 1.0f));



glBindBuffer(GL_ARRAY_BUFFER, gridvID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridiID);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
ShaderProgram shader;
//UPDATE THE SHADER UNIFORMS EVERY FRAME (IS THERE A MORE EFICIENT WAY?)
if (ShaderProgram::find("ColorShader", shader)) {
shader.addUniform("transformationMatrix", gridMatrix);
shader.addUniform("timeMS", Time::programMS);
}
else {
printf("ColorShader shader not found.\n");
}


//I HAD THIS CALL WORKING BEFORE WITHOUT GIVING THE LAST PARAMETER, I JUST BOUND THE INDEX ARRAY ID
glDrawElements(GL_LINES, indexNum, GL_UNSIGNED_INT, indexArray);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

}
}


This is basically how i do all my rendering in my engine, but the world models are created in a seperate class from my Graphic class and are being passed as a Model object with pointers to the heap allocated dynamic arrays. It worked before, but appearently i changed something and now it doesnt work any more. Could anybody point me the bug or give me some advice on how to do this in a better way?
Thanks :)

EDIT:
when i use glDrawArrays it works fine, so it seems to be something with the indexing. Also one thing i noticed is that it once worked when i bound the index array buffer ID (gridiID) as a GL_ARRAY_BUFFER instead of a GL_ELEMENT_ARRAY_BUFFER, what suprised me.

Alfonse Reinheart
10-08-2016, 11:28 AM
Hello forums,

i need some help understanding the "VAO Pipeline".

That may be because there is no such thing. I mean, there are certainly vertex array objects, but they do not represent anything that would be called a "pipeline".

Also, your code doesn't seem to be using a VAO at all; you're just using context vertex array state. Unless you're binding a VAO elsewhere, in which case, it's not clear why you keep disabling and enabling the attribute arrays.

In any case, there are many things that could be going wrong. Since this object doesn't have its own VAO (or at least, it doesn't appear to from the code snippet you've provided us), if you change vertex attribute state elsewhere, you'll be bashing the changes that your loading function made. So the rendering code will need to redo the vertex attribute setup. Not the loading of data into buffer objects; the glVertexAttribPointer calls (preceded by the GL_ARRAY_BUFFER binding, of course (https://www.opengl.org/wiki/Vertex_Specification#Vertex_Buffer_Object)).

Lastly, it's never a good idea to load data in the middle of your render loop. That's something you should do at a designated place and time before rendering starts.

stimulate
10-08-2016, 11:43 AM
i have now spent all day trying to get it to work, and in the process ive cleared up my misconceptions about VAOs, VBOs, etc.

now i have my updated code, which seems to be fine, regarding to all of my sources.




void Graphic::initializeShaders() {
ShaderProgram colorShader("ColorShader");
colorShader.addShader("Shaders\\colorShader.vert.txt");
colorShader.addShader("Shaders\\colorShader.frag.txt");
colorShader.build(); //IN THE BUILD FUNCTION I BIND ATTRIBUTE LOCATIONS FOR "vertexPosition" to 0 and "vertexColor" to 1
colorShader.use();
colorShader.addUniform("ambientLight", ambientLight);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}

void Graphic::renderGrid()
{

//USED VARIABLES
static GLuint gridvaoID = 0;
static GLuint gridvboID = 0;
static GLuint gridiboID = 0;
static bool loaded = false;
static const int scale = 10;
static const unsigned int vertexNum = 100;
static const unsigned int indexNum = 600;
static int indexArray[600];
static Vertex vertexArray[100];


//CREATE GL HANDLES AND LOAD THE VERTEX/INDEX ARRAYS
if (!loaded) {
glGenVertexArrays(1, &gridvaoID);
glCreateBuffers(1, &gridvboID);
glCreateBuffers(1, &gridiboID);



for (int vert = 0; vert < vertexNum; vert++) {
static int itx = 0;
static int itz = 0;
vertexArray[vert].position.x = -1.0f*(scale / 2 - itx);
vertexArray[vert].position.y = 0.0f;
vertexArray[vert].position.z = -1.0f*(scale / 2 - itz);

vertexArray[vert].color.r = 1.0f;
vertexArray[vert].color.g = 1.0f;
vertexArray[vert].color.b = 1.0f;
vertexArray[vert].color.a = 1.0f;
if (itx == scale) {
itx = 0;
itz++;
}
else {
itx++;
}
}


int runner = 0;
for (unsigned int row = 0; row < scale; row++) {
for (unsigned int col = 0; col < scale; col++) {
indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale;
indexArray[runner++] = scale *row + col + scale + 1;

indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale + 1;
indexArray[runner++] = scale *row + col + 1;
}

}

//BIND THE VAO AND FILL VBO AND IBO WHILE VAO IS BOUND
glBindVertexArray(gridvaoID);

glBindBuffer(GL_ARRAY_BUFFER, gridvboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray), &vertexArray[0], GL_STATIC_DRAW);

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gridiboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexArray), indexArray, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
loaded = true;
}

if (showGrid) {
glm::mat4 translate = glm::translate(Camera::camMatrix(), glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 rotate = glm::rotate(translate, 20.0f, glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 gridMatrix = glm::scale(rotate, glm::vec3(1.0f, 1.0f, 1.0f));

ShaderProgram shader;
if (ShaderProgram::find("ColorShader", shader)) {
shader.addUniform("transformationMatrix", gridMatrix);
shader.addUniform("timeMS", Time::programMS);
}
else {
printf("ColorShader shader not found.\n");
}

glBindVertexArray(gridvaoID);

glDrawElements(GL_TRIANGLES, indexNum, GL_UNSIGNED_INT, 0);

glBindVertexArray(0);
}
}



it still does not work though and i have no clue why not

Lastly, it's never a good idea to load data in the middle of your render loop. That's something you should do at a designated place and time before rendering starts.
this is just kind of the prototype for my renderGrid() function. my other, main render() function uses Mesh objects which have a load() memberfunction.

Alfonse Reinheart
10-08-2016, 12:09 PM
it still does not work though and i have no clue why not

It doesn't work because of this line:



glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


VAO state includes the buffer bound to GL_ELEMENT_ARRAY_BUFFER (https://www.opengl.org/wiki/Vertex_Specification#Index_buffers). So you don't need to unbind it.

That being said, I see you're using glCreateBuffers instead of glGenBuffers. In which case, you're relying on OpenGL 4.5 or ARB_direct_state_access (https://www.opengl.org/wiki/Direct_State_Access). So allow me to rewrite your loading code so that it makes real use of this functionality. And incidentally, so that it'll be a lot easier to understand what's going on:



if (!loaded) {
for (int vert = 0; vert < vertexNum; vert++) {
static int itx = 0;
static int itz = 0;
vertexArray[vert].position.x = -1.0f*(scale / 2 - itx);
vertexArray[vert].position.y = 0.0f;
vertexArray[vert].position.z = -1.0f*(scale / 2 - itz);

vertexArray[vert].color.r = 1.0f;
vertexArray[vert].color.g = 1.0f;
vertexArray[vert].color.b = 1.0f;
vertexArray[vert].color.a = 1.0f;
if (itx == scale) {
itx = 0;
itz++;
}
else {
itx++;
}
}

int runner = 0;
for (unsigned int row = 0; row < scale; row++) {
for (unsigned int col = 0; col < scale; col++) {
indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale;
indexArray[runner++] = scale *row + col + scale + 1;

indexArray[runner++] = scale *row + col;
indexArray[runner++] = scale *row + col + scale + 1;
indexArray[runner++] = scale *row + col + 1;
}

}

glCreateBuffers(1, &gridvboID);

//With DSA, no need to glBind* anything.
//Completely GPU-side buffer object
glNamedBufferStorage(gridvboID, sizeof(vertexArray), &vertexArray[0], 0);

//Must use Create rather than Gen here.
glCreateVertexArrays(1, &gridvaoID);

//Attach buffer to binding 0.
glVertexArrayVertexBuffer(gridvaoID, 0, gridvboID, 0, sizeof(Vertex));

//Positions come from buffer binding 0.
glVertexArrayAttribBinding(gridvaoID, 0, 0);
glVertexArrayAttribFormat(gridvaoID, 0, 4, GL_FLOAT, GL_FALSE, (void*)offsetof(Vertex, position));

//Colors also come from buffer binding 0.
glVertexArrayAttribBinding(gridvaoID, 1, 0);
glVertexArrayAttribFormat(gridvaoID, 1, 4, GL_FLOAT, GL_FALSE, (void*)offsetof(Vertex, color));

glCreateBuffers(1, &gridiboID);

//Completely GPU-side buffer object
glNamedBufferStorage(gridiboID, sizeof(indexArray), indexArray, 0);
//Attach element buffer directly to VAO.
glVertexArrayElementBuffer(gridvaoID, gridiboID);

loaded = true;
}


The rendering code need not change.

stimulate
10-08-2016, 12:40 PM
a lot of thanks for the quick lesson, but it still does not work :( by now i think the bug has to be somewhere else in my program, because nothing seems to change anything. but i dont recall making a significant change to anything in my program..

i will keep looking some more, here is the loading and rendering code i have made from your guide.



if (!loaded) {
glCreateVertexArrays(1, &gridvaoID);
glCreateBuffers(1, &gridvboID);
glCreateBuffers(1, &gridiboID);



//*LOAD vertexArray AND indexArray*



//associate vertexArray with vboID
glNamedBufferStorage(gridvboID, sizeof(vertexArray), &vertexArray[0], 0);

//vaoID binding location 0 -> vboID
glVertexArrayVertexBuffer(gridvaoID, 0, gridvboID, 0, sizeof(Vertex));

//vao binding location 0 -> attribute 0
glVertexArrayAttribBinding(gridvaoID, 0, 0);
glVertexArrayAttribFormat(gridvaoID, 0, 4, GL_FLOAT, GL_FALSE, offsetof(Vertex, position));

glVertexArrayAttribBinding(gridvaoID, 1, 0);
glVertexArrayAttribFormat(gridvaoID, 1, 4, GL_FLOAT, GL_FALSE, offsetof(Vertex, color));

glVertexArrayElementBuffer(gridvaoID, gridiboID);

loaded = true;
}

if (showGrid) {
glm::mat4 translate = glm::translate(Camera::camMatrix(), glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 rotate = glm::rotate(translate, 20.0f, glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 gridMatrix = glm::scale(rotate, glm::vec3(1.0f, 1.0f, 1.0f));

ShaderProgram shader;
if (ShaderProgram::find("ColorShader", shader)) {
shader.addUniform("transformationMatrix", gridMatrix);
shader.addUniform("timeMS", Time::programMS);
}
else {
printf("ColorShader shader not found.\n");
}
glBindVertexArray(gridvaoID);

glDrawElements(GL_TRIANGLES, indexNum, GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_LINES, 0, vertexNum);

glBindVertexArray(0);

}

Alfonse Reinheart
10-08-2016, 01:19 PM
You might have copied from an older version of my example, where I omitted a `glNamedBufferStorage` call for `gridiboID`. The current version should work, but the code you posted is missing that particular statement.

Also, check for OpenGL errors (https://www.opengl.org/wiki/OpenGL_Error), or use debug output (https://www.opengl.org/wiki/Debug_Output).

stimulate
10-08-2016, 02:05 PM
nice reference, i will have good use for it. i havent gotten the glDebugMessageCallback to work yet, but i will find some time for it tomorrow. This bug really grinds my gears though...

john_connor
10-08-2016, 02:20 PM
to render a grid in the xz-level of the scene, i would do it like that: (without the index buffer)



struct Vertex {
vec3 Position;
vec4 Color;
};

// global variables
unsigned int buffer = 0;
unsigned int vertexarray = 0;
unsigned int program = 0;
unsigned int vertexcount = 0;

std::string vertexshader_source = {
"#version 450 core\n"
"layout (location = 0) in vec3 in_position;"
"layout (location = 1) in vec4 in_color;"
"uniform mat4 MVP = mat4(1);"
"out vec4 color;"
"void main() {"
"gl_Position = MVP * vec4(in_position, 1);"
"color = in_color;"
"}"
};

std::string fragmentshader_source = {
"#version 450 core\n"
"in vec4 color;"
"layout (location = 0) out vec4 out_color;"
"void main() {"
"out_color = color;"
"}"
};


void Initialize()
{
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
glEnable(GL_DEPTH_TEST);


// setup program
program = glCreateProgram();

unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);

CompileShader(vertexshader, vertexshader_source);
CompileShader(fragmentshader, fragmentshader_source);
LinkProgram(program, { vertexshader, fragmentshader });

glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);

mat4 matrix = perspective(radians(45.0f), 1.33f, 0.1f, 100.0f) * lookAt(vec3(2, 3, 5), vec3(0), vec3(0, 1, 0));
int location = glGetUniformLocation(program, "MVP");
glProgramUniformMatrix4fv(program, location, 1, false, &matrix[0][0]);


// setup vertex array
glGenBuffers(1, &buffer);
glGenVertexArrays(1, &vertexarray);

glBindVertexArray(vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);


// setup vertex buffer
std::vector<Vertex> vertices;
for (float i = -10; i < +10; i++)
{
vertices.push_back({ { -10, 0, i },{ 1, 0, 0, 1 } });
vertices.push_back({ { +10, 0, i },{ 1, 0, 0, 1 } });
vertices.push_back({ { i, 0, -10 },{ 0, 0, 1, 1 } });
vertices.push_back({ { i, 0, +10 },{ 0, 0, 1, 1 } });
}

vertexcount = vertices.size();

glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}


this "void Iniitialize()" function has to be called only once
then the "void Render()" (that has to be called each frame) function would look like this:


void Render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(program);
glBindVertexArray(vertexarray);
glDrawArrays(GL_LINES, 0, vertexcount);
glBindVertexArray(0);
glUseProgram(0);

// swap buffers
}


the vertex array object contains all the necessary settings on how to feed the vertex shader
at attribute location 0, it will send a "vec3" (which is the vertex position)
at attribute location 1, it will send a "vec4" (which is the vertex color)

these locations are set directly in the vertexshader sourcecode:
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec4 in_color;

the vertex array object knows from which buffer to read the data:
it is the buffer that was bound to "GL_ARRAY_BUFFER" when you called "glVertexAttribPointer(...)"

in addition to that, the vertex array object "knows" which attributes are enabled / disabled
by default, all attribute are disabled, thats why you have to enable the explicitly:
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

thats all, you can easily add / set new vertices in the buffer, the "vertex array object" doesnt care (as long as it is still the same buffer)


rendering the grid is then easy:
glUseProgram(program);// activate program
glBindVertexArray(vertexarray);// activate vertex array object
glDrawArrays(GL_LINES, 0, vertexcount);// draw..

stimulate
10-08-2016, 02:32 PM
there is a lot of ways to set the vertices of the grid but i dont think my problem is with the vertices, because i had already gotten it rendered once with the algorithm i use. my problem might be rather with the shader or the rendering or what do i know. i will try to change the shader tomorrow and then see. i wont to use vaos because they seem to be efficient and i need practice with these. but i will look through your code in more detail tomorrow and match it up with mine, maybe that helps me find the bug :)

stimulate
10-09-2016, 09:34 AM
i cant believe it but i found the bug :sorrow: the problem was that i never initialized the w - value of my 4 dimensional position vectors.. they screwed up my matrix multiplication and probably rendered my vertices somewhere out in space. also i have to call glEnableVertexArrayAttrib(vaoID, 0) and -,1) before glDrawElements. for some reason it would only work when i call it while my vao is bound, even though i tell it the vao with the first parameter.