draw multiple objects with different MVP

Hi,

i’ve now read in most of my data from my 3D file i want to display. But my file can have multiple Model chunks each with different model matrices.

The rough structure of the project:
I have an instance of an OGL Controller class. This class has an load function to open a 3D file. In that function i get a new instance of an Object class that manages the import. The ogl controller gets an list with Models back. Each Model has its own vertices, UV, textures, translation and parameters.
Since now only the first Model is painted. I want to know how to display all of them in the best way. Do i need to put all vertices, uv,… into one buffer, and in the update function i have a loop that changes the MVP/texture/… for each section in my buffer (draw the first 5 vertices with MVP11, next draw the vertices 6-10 with MVP2,…) or is it better/possible to refill the buffer in each loop pass?

if you prefer to take a look into my code to understand what i mean you can do it here: https://github.com/GT-Anakin/MshViewer/tree/master/MshViewer

https://www.opengl.org/wiki/Vertex_Rendering#Instancing

there are 2 ways to send all the matrices at once to the vertex shader:
– put them into a buffer and bind that buffer to an “uniform block”
– put them into a buffer and stream these as “instanced attributes” to the vertex shader

example:

for (int instance = 0; instance < 100; instance ++)
{
glDrawArrays(..., ..., ...);
}

is the same as:

int instances = 100;
glDrawArraysInstanced(..., ..., ..., instances );

using the “glDrawArraysInstanced(…)” call, your vertex shader can determin what instance is currently being drawn:
– the build-in variable “gl_InstanceID” will be 0 … 99

you can use that variable to access an array of matrices:

uniform mat4 MVPmatrices[100];
...
mat4 currentMVP = MVPmatrices[gl_InstanceID];
// use currentMVP for vertex processing ...

the downside is that “uniform blocks” are limited in size, you can query the max size (in bytes):

int maxuniformblocksize = 0;
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxuniformblocksize);
cout << "uniform block size: 	" << maxuniformblocksize << endl;

the other way is to send matrices as instanced vertex attributes:
https://www.opengl.org/wiki/Vertex_Specification#Instanced_arrays

regarding the buffers:
i would put all the vertices into 1 static buffer (GL_STATIC_DRAW) and all the matrices into 1 dynamic buffer (GL_STREAM_DRAW)

regarding the textures:
i would put all the textures into 1 big “array texture”, that way you need to bind the texture only once
you only need to keep track of the layer index for each mesh
the upside is that you can draw meshes which have mutliple textures with just 1 drawcall (instead of dividing the mesh up into parts that use the same texture)
downside: all layers have the same resolution (e.g. 1024 x 1024), so you have to resize you textures beforehand
https://www.opengl.org/wiki/Array_Texture

Thank you for your help. The first option sounds “easy” and i have some ideas how to do it, but i don’t think it’s the best solution for me. So i’d prefer the 2nd. But i don’t really understand how it works. Can you give me an example for it, too??

And maybe i can ask you to have a look at this line, too: https://github.com/GT-Anakin/MshViewer/blob/master/MshViewer/Source/OpenGlController.cpp#L366
From time to time i get an memory access error and the program crashes. But i don’t know why and i really wonder, why it sometimes works and sometimes it doesn’t. I change nothing on the code. I just wait a few seconds and i restart it and it works. That’s so crazy.

maybe you “vertexattribpointers” arent set correctly or dont point to an existing buffer or so, why dont you use “vertex array objects” ??
explaination
my example “mesh viewer”
(take a look at the “Renderer_OpenGL” class)

See this for a brief overview of instanced rendering.

Basically: you need to use one of the “Instanced” drawing functions to draw multiple instances. Then you can use glVertexAttribDivisor() to indicate that a particular attribute is per-instance (or per N instances) rather than per-vertex, i.e. the same element from the attribute array will be used for all vertices within an instance.

[QUOTE=john_connor;1284464]maybe you “vertexattribpointers” arent set correctly or dont point to an existing buffer or so, why dont you use “vertex array objects” ??
explaination
my example “mesh viewer”
(take a look at the “Renderer_OpenGL” class)[/QUOTE]

I’m currently reading your example and i’m gonna read the tutorial next. Since now i used this tutorial: http://www.opengl-tutorial.org/beginners-tutorials/ (i think you gave it to me, too)
And now i’m wondering about a few things. In that tutorial the attributes are opened and closed every time the scene gets updated. In your example you open it once at the init and never close it again. So what are the benefits of open/close the attributes??

What language is your example in the tutorial?? It’s not c++. I don’t think that this is a valid c++ for-loop:
for (auto& object : scene.Objects){…}

==EDIT==
i just changed the code, to use just one buffer to pass the data (uv and position) of the vertex to the shader.
It does not crash, that’s the good news :smiley: but it’s not 100% correctly. There is one triangle wrong positioned and 2 are not drawed. Maybe i can ask you once more to look at my code:
https://github.com/GT-Anakin/MshViewer/commit/9d35634c0cf1f1d77a9b7fde85d21398907358a9
This is the changelog of the latest commit that contains only the changes i did to use only one buffer

if an attribute is enabled, the vertex array object allows the shader to read from the buffer behind that attribute
if an attribute is disabled, the vertex shader cant access the buffers data (the streamed data will be replaced by a constant value)

in my example there is no need to disable any atrribute
for example lets say you have 2 meshes, 1 of them dsoesn have texcoords at all, then disabling the “in_texcoords” attribute would make sense …

for your mesh viewer -> use a vertex array object just let them all enabled :wink:

[QUOTE=3DPrgmer;1284467]What language is your example in the tutorial?? It’s not c++. I don’t think that this is a valid c++ for-loop:
for (auto& object : scene.Objects){…}[/QUOTE]

thats c++ 11, i think (?)

“auto” is a generic / undefined type
“auto&” is just a “alias” for a value (that shouldnt be copy-constucted)
the for-loop with that “:” means “for each … in …”
you can use that with std::map, std::vector, … almost all std container types

its the same as:

for (unsigned int i = 0; i < scene.Objects.size(); i++)
{
Object& object = scene.Objects[i];
...}

ok i haven’t made much with c++11. There are a few cool new functions such as std::to_string(). But i haven’t learned much about the main new things.

Back to my problem. I put my data to the buffer here: https://github.com/GT-Anakin/MshViewer/blob/master/MshViewer/Source/OpenGlController.cpp#L378

[ATTACH=CONFIG]1408[/ATTACH]
And on the picture you can see that 2 triangles are not painted and from one triangle the position is wrong. Everything else (that you cannot see, because it’s behind) is correctly displayed. On the miss displayed triangle you can see that the UV are wrong too. But only for that triangle.
Any idea what could be wrong?? Is there a way to look into the values after i gave them to OGL?? maybe a print function for the shaders??

sure:

glBufferData(
GL_ARRAY_BUFFER,
sizeof(tempBufferData) * tempBufferData.size(),
tempBufferData.data(),
GL_STATIC_DRAW);

you pass the size of a “std::vector” instead of the sizeof(Vertex)

Is there a way to look into the values after i gave them to OGL?? maybe a print function for the shaders??

transform feedback: you can capture the processed vertex data into another buffer, download the bufferdata from openGL and print that in your c++ application
but you’d have to setup the next “openGL object”: a transform feedback object

just wrap a VAO around your buffers and everything will be good :wink:
(its far more easier to read then, just set it up once, bind it and forget it …)

take a look at my latest commit message :wink:
https://github.com/GT-Anakin/MshViewer/commit/3af886450f2797563ded14edba64d3228dcbca83

just an idea for your tutorial. it doesn’t look finished. But maybe you wanna share your code on a public git repo. You can add new branches for each tutorial, so it’s much more easier to follow your code. Furthermore the commit’s show exactly the differences to the previous versions.

it isnt, its more a collection of sample code pieces for those who already have understood the basics
there are many “finished” and good tutorials out there, but some of them are (in my view) poorly coded
from time to time i add some new examples / improve existing ones …

i’ll do that soon, but first i have to figure out how this github thing works :wink:

if you need any help with github, let me know. I’m a beginner, too, but i know how to make a new project, etc.
Currently i’m moving all to gitLab because i can use private repos there :smiley:

Can you please take an other look at my latest changes?? I tried to give the MVP via buffer to the shader, but for some reason nothing is displayed. In the next step i wanted to get that ready before i try the instanced draw.

I moved my project to a different host. so here is the link to the latest commit:
https://git.rwth-aachen.de/carstenf/OpenGL/commit/9c12598bf5710330cafc21456249ba34ffe25b9b

If you have any problems to view it, let me know. I’m new to that host :wink:

page not found
lad’s lieber bei github hoch … oder post vertex array setup + vertex shader source

du sprichst deutsch??
Naja GitLab ist noch in der beta Phase und ich hab sowieso noch stress mit unserer IT deshalb :smiley: dann erstmal wieder bei GitHub:
https://github.com/GT-Anakin/MshViewer/commit/9c12598bf5710330cafc21456249ba34ffe25b9b

==EDIT==

der erste sollte jetzt auch gehen. Auch wenn es mir nicht ganz gefällt, dass jetzt jeder auch pushen kann. Denke da muss ich nochmal an die IT schreiben :smiley:

a hidden error:
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 0));
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 4));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 8));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 12));
GL_FLOAT is not a floating-point variable (GLfloat is!), its an openGL-internal enum to identify the variable type, like GL_INT (not GLint!)

an actual error: i think you are missing:
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);

and there is nowhere a vertexattribdivisor set, so the openGL will send PER-VERTEX a model matrix, thats certainly not what you want
these instanced “pipes” (attributes) have to have trhe divisor “1”, meaning that only per drawcall instance the vertex shader will get a new model-to-world matrix

glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);

[QUOTE=john_connor;1284491]a hidden error:
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 0));
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 4));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 8));
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GL_FLOAT) * 12));
GL_FLOAT is not a floating-point variable (GLfloat is!), its an openGL-internal enum to identify the variable type, like GL_INT (not GLint!)
[/QUOTE]
:doh:

:doh:

[QUOTE=john_connor;1284491]
and there is nowhere a vertexattribdivisor set, so the openGL will send PER-VERTEX a model matrix, thats certainly not what you want
these instanced “pipes” (attributes) have to have trhe divisor “1”, meaning that only per drawcall instance the vertex shader will get a new model-to-world matrix

glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);[/QUOTE]

I wanted to first test to not drawing instanced. But i think that’s a stupid idea. So i now changed it to instanced. It crashes -.-
But i think i know why it does. I have a few questions:

  1. why do we need 4 pipes for just one matrix??
    in the shader (from your example codes):
layout (location = 2) in mat4 in_model;
//layout (location = 3) in use ...
//layout (location = 4) in use ...
//layout (location = 5) in use ...

in the cpp:

glVertexAttribPointer(2, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));
glVertexAttribPointer(3, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));
glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));
glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));

For me it looks like you give the matrix on 4 pipes, but take it just from one. Why don’t we put the whole matrix on one pipe and take it from one??

  1. Where and how to i tell the shader when to use what instance and who does it know what vertex belong to which instance?? I gave no information about it to the shader and i don’t find something about it in your example code. I think that this is the reason for my crash.

I changed the permission for gitlab. You can now view my code, pull and clone, but not push (i made the master branch a protected branch)
https://git.rwth-aachen.de/carstenf/OpenGL/tree/master/MshViewer

i dont see where you allocated memory for your instancebuffer:
glBufferSubData(…) just sets the memory for that buffer, it doesnt allocate memory
glBufferData(…) allocates memory

later on when you have to deal with much more instanced buffer data, you can replace glBufferSubData with glMapBuffer which is in general faster

once again: i’d use a VAO


GLuint myVAO = 0;
glGenVertexArrays(1, &myVAO);
glBindVertexArray(myVAO);

	// open attribute position 	
	glBindBuffer(GL_ARRAY_BUFFER, gluiVertexBufferID); 	
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof(GLfloat) * 0)); 	
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof(GLfloat) * 3)); 	
	glBindBuffer(GL_ARRAY_BUFFER, 0); 	
	
	glBindBuffer(GL_ARRAY_BUFFER, gluiInstanceBufferID); 	
	glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GLfloat) * 0)); 	
	glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GLfloat) * 4)); 	
	glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GLfloat) * 8)); 	
	glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(GLfloat) * 12)); 	
	glBindBuffer(GL_ARRAY_BUFFER, 0); 	
	
	// enable position, UV and mvp 
	glEnableVertexAttribArray(0); 	
	glEnableVertexAttribArray(1); 	
	glEnableVertexAttribArray(2); 	
	glEnableVertexAttribArray(3); 	
	glEnableVertexAttribArray(4); 	
	glEnableVertexAttribArray(5); 	
	// MPV only once per instance 	
	glVertexAttribDivisor(2, 1); 	
	glVertexAttribDivisor(3, 1); 	
	glVertexAttribDivisor(4, 1); 	
	glVertexAttribDivisor(5, 1);

glBindVertexArray(0);

by binding that VAO later when you want to draw, you are enabling all the stuff you’ve setup there effectively
if you only want to render the mesh, bind it once and forget it …

i thought i use this VAO: https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L82
Or is that something different??

So glBufferSubData needs to be done only once after this??

glVertexAttribDivisor(2, 1); 	
	glVertexAttribDivisor(3, 1); 	
	glVertexAttribDivisor(4, 1); 	
	glVertexAttribDivisor(5, 1);
*

oh … sorry! i missed that part :smiley:

while your instancebuffer is bound to any target (lets say GL_ARRAY_BUFFER), just call once:

int maxbuffersize = 1000 * sizeof(mat4);
glBufferData(GL_ARRAY_BUFFER, maxbuffersize, NULL, GL_STREAM_DRAW);
– now you buffer can contain 1000 mat4 matrices, but is empty

thats all, later in your OpenGLController::updateScene() you already put matrices into that buffer, that should work