PDA

View Full Version : draw multiple objects with different MVP



3DPrgmer
10-31-2016, 02:59 AM
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

john_connor
10-31-2016, 04:09 AM
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: \t" << 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

3DPrgmer
10-31-2016, 08:14 AM
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.

john_connor
10-31-2016, 09:00 AM
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.

maybe you "vertexattribpointers" arent set correctly or dont point to an existing buffer or so, why dont you use "vertex array objects" ??
explaination (https://www.opengl.org/discussion_boards/showthread.php/198966-Storing-all-Mesh-data-in-one-VAO?p=1284370&viewfull=1#post1284370)
my example "mesh viewer" (https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-07-multiple-models)
(take a look at the "Renderer_OpenGL" class)

GClements
10-31-2016, 09:16 AM
So i'd prefer the 2nd. But i don't really understand how it works.
See this (https://www.opengl.org/wiki/Vertex_Rendering#Instancing) 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.

3DPrgmer
11-01-2016, 02:44 AM
maybe you "vertexattribpointers" arent set correctly or dont point to an existing buffer or so, why dont you use "vertex array objects" ??
explaination (https://www.opengl.org/discussion_boards/showthread.php/198966-Storing-all-Mesh-data-in-one-VAO?p=1284370&viewfull=1#post1284370)
my example "mesh viewer" (https://sites.google.com/site/john87connor/advanced-opengl-tutorials/tutorial-07-multiple-models)
(take a look at the "Renderer_OpenGL" class)


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 :D 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

john_connor
11-01-2016, 06:23 AM
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??

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



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){...}

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

3DPrgmer
11-01-2016, 06:50 AM
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

2297
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??

john_connor
11-01-2016, 06:54 AM
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 ;)
(its far more easier to read then, just set it up once, bind it and forget it ..)

3DPrgmer
11-01-2016, 09:08 AM
take a look at my latest commit message ;)
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.

john_connor
11-01-2016, 09:59 AM
just an idea for your tutorial. it doesn't look finished.

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 ..



But maybe you wanna share your code on a public git repo.

i'll do that soon, but first i have to figure out how this github thing works ;)

3DPrgmer
11-02-2016, 09:27 AM
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 :D

3DPrgmer
11-04-2016, 08:37 AM
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 ;)

john_connor
11-04-2016, 09:24 AM
If you have any problems to view it, let me know. I'm new to that host ;)

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

3DPrgmer
11-04-2016, 10:31 AM
du sprichst deutsch??
Naja GitLab ist noch in der beta Phase und ich hab sowieso noch stress mit unserer IT deshalb :D 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 :D

john_connor
11-04-2016, 12:07 PM
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);

3DPrgmer
11-05-2016, 04:06 AM
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!)

:doh:



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

:doh:




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

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??

2) 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

john_connor
11-05-2016, 04:42 AM
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 ..

3DPrgmer
11-05-2016, 05:09 AM
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);
*

john_connor
11-05-2016, 05:23 AM
oh .. sorry! i missed that part :D

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

3DPrgmer
11-05-2016, 05:32 AM
Ah ok. Is it possible to make the size of the buffer dynamical?? Let's say after i read in the mesh i know exactly how many matrices i need. Can i adjust the size after reading in the mesh??

And still the question how do i tell the shader what vertex belongs to what instance??

I'm still crashing when at glfwPollEvents();
https://git.rwth-aachen.de/carstenf/OpenGL/commit/8a36fe10b163bbb5b4cf3fffdfc03833177667f1

john_connor
11-05-2016, 06:12 AM
memory re-allocation is possible, just dont use glBufferStorage(...) to allocate memory
https://www.opengl.org/wiki/Buffer_Object#Mutable_Storage


And still the question how do i tell the shader what vertex belongs to what instance??

why would you do that?

still crashing ?! maybe you got some pointers / delete[] wrong ..
you are already using std::vector etc, whats the point for those new / delete calls ? :)

3DPrgmer
11-05-2016, 08:17 AM
Let's say i have 2 models and each with a different mvp. How can i tell the shader that vertex 1-10 belong to MVP 1 and the vertex 11-20 belong to mvp 2? You said i should put the vertex all into one buffer. So i need to tell the shader the size of instance 1, 2,...

The program only crashes since i changed to the instanced draw.

you are already using std::vector etc, whats the point for those new / delete calls ?
what do you mean??

GClements
11-05-2016, 03:04 PM
Let's say i have 2 models and each with a different mvp. How can i tell the shader that vertex 1-10 belong to MVP 1 and the vertex 11-20 belong to mvp 2? You said i should put the vertex all into one buffer. So i need to tell the shader the size of instance 1, 2,...

Instancing renders multiple copies of a single set of vertices. It can't be used to render multiple, distinct models.

If you want to coalesce multiple models into a single draw call, and you don't want to replicate per-model data (e.g. a transformation) for each vertex, the usual solution is to add an integer vertex attribute containing the model number, and use that to index into uniform arrays containing per-model data.

3DPrgmer
11-06-2016, 03:04 AM
That sounds easy. So i give a static array of mvp matrices to the shader and for each vertex the information what matrix need to be used, right?
with just one matrix it works :D so i'm gonna do the step to 2 matrices now. But how would i access the different matrices in the shader??

here is my shader:


#version 450 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;


// Output
out vec2 UV;

// Values that stay constant for the whole mesh.
uniform mat4 MVP;

void main(){

// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace, 1);

UV = vertexUV;

}

how do i need to modify it?? Something like this?



#version 450 core

// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in unsigned int instanceIndex;

// Output
out vec2 UV;

// Values that stay constant for the whole mesh.
uniform mat4 MVP;

void main(){

// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP[instanceIndex] * vec4(vertexPosition_modelspace, 1);

UV = vertexUV;

}

GClements
11-06-2016, 03:24 AM
how do i need to modify it?? Something like this?


uniform mat4 MVP;



That needs to be an array:


uniform mat4 MVP[2];


If you have many models, you can quickly exhaust the maximum number of uniforms in the default uniform block (each mat4 is 4 vectors or 16 components), in which case you need to use named uniform blocks backed by uniform buffer objects (UBOs).

3DPrgmer
11-06-2016, 03:43 AM
i don't really understand what you mean with that UBOs. I implement some code in a different branch:
https://git.rwth-aachen.de/carstenf/OpenGL/commit/e54f439b0e61315c736d4c97924cea701f3e7afe

But i get an error:


Vertex Info
-------------
0(19): error C7559: OpenGL requires constant indexes for unsigned array access (MVP)


I tried to change the shader to this:


layout(location = 2) in const uint index;


and i get this error:


0(7): error C7522: OpenGL requires constants to be initialized


if i access the MVP array by hardcoded value it works:


gl_Position = MVP[0] * vec4(vertexPosition_modelspace, 1);


So how can i choose a dynamically index??

==EDIT==

i tried to run the program with some hardcoded index. And i noticed that this:


std::vector<const float*> mvpMatrices;
for (unsigned int i = 0; i < vModels.size(); i++)
mvpMatrices.push_back(glm::value_ptr(getMVPMatrix( i)));

glUniformMatrix4fv(gluiMatrixID, vModels.size(), GL_FALSE, *mvpMatrices.data());

doesn't work. There is only one matrix given to the shader

john_connor
11-06-2016, 04:19 AM
i tried to run the program with some hardcoded index. And i noticed that this:


std::vector<const float*> mvpMatrices;
for (unsigned int i = 0; i < vModels.size(); i++)
mvpMatrices.push_back(glm::value_ptr(getMVPMatrix( i)));

glUniformMatrix4fv(gluiMatrixID, vModels.size(), GL_FALSE, *mvpMatrices.data());

doesn't work. There is only one matrix given to the shader

because you didnt specify a uniform block in your shader
https://www.opengl.org/wiki/Interface_Block_(GLSL)#Block_buffer_binding

short description:
--> in your (vertex) shader:


layout (std140, binding = 0) uniform MVP_MatrixBlock
{
// declare here some variables
mat4 MVParray[100];
};


--> means that you ant to have a "uniform block" with a certain "memory layout", backed by a buffer object which has to be bound at "uniform buffer binding point 0"

to understand that topic, read this chapter:
https://www.opengl.org/wiki/Interface_Block_(GLSL)#Memory_layout

to bind a buffer object at "uniform buffer binding point 0", you call this:
glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
https://www.opengl.org/sdk/docs/man/html/glBindBufferBase.xhtml

--> "buffer" is the memory container which has all the data for the variables declared in between the { brackets } in your (vertex) shader, in this case: mat4 MVParray[100];


that fixed "100" array size is a restriction of uniform blocks, you have to define the array size
there is another kind of "block" which allows you to access an "unsized" array, its called "shader storage block":
https://www.opengl.org/wiki/Interface_Block_(GLSL)#Shader_storage_blocks

the downside is that accessing it is generally slower than accessing an uniform block


by the way:
std::vector<const float*> ...
is an array of float pointers, not an array of float variables !!!

it should be:
std::vector<mat4> ...


if you want to figure out what's the maximum uniform block size (which depends on your hardware):


int maxuniformblocksize_in_bytes = 0;
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxuniformblocksize_in_bytes);
cout << "max uniform block size (in bytes): " << maxuniformblocksize_in_bytes<< endl;

3DPrgmer
11-06-2016, 06:00 AM
I see we already were at that point, right?? And i decided to use the buffer instead. btw the max size is for me: 65536

So let's stop coding around without understanding. I still do not understand how the instanced buffer thing works and how it could solve my Problem.

If i understand it right, the instanced draw takes one mesh and draws it multiple times with different settings (for example i have a tree model and want to display a forest, so i have one model draw multiple times each time with a different MVP).


My Problem:
I have one .msh file that contains multiple models. Each model has a number of vertices/UV (all triangles for now) and all the vertices of one model belong to one MVP that is special for each model. For the beginning there will be only one texture for all models. But later there will be multiple textures that can be used for one or multiple models and even for only a part of one model. But let's forget about the texture and concentrate on the Model.
What is the best way to display each model with it's right MVP??

My idea:
1)


update{
....
for (each model)
{
vertexBuffer.fill(model.vertex, model.uv)
uniformMatrix(model.mvp)
drawtriangles()
}
}

But john told me that is not a good idea and much easier that way:



loadmesh{
....

for (each model)
{
vertexBuffer.fill(model.vertex, model.uv)
}
}

update{
....

for (each model)
{
instanceBuffer.fill(model.mvp)
}

drawInstanced()

}


But if i understand the instanced draw right, that way (let's say my msh contains 2 model - one is the roof and the other the walls of a house) i get the walls and roof painted each 2 times one time at the wall's position and one time at the roof's position and that's not what i need. I want the wall to be painted with the wall's MVP and the roof with the roof's MVP

Maybe i didn't got the point and i hope you can explain it to me before i try to continue coding.

john_connor
11-06-2016, 07:02 AM
the "trick" is to keep track of each models "vertexoffset" and "vertexcount" parameter since all models share the same buffer

when drawing:


for each model
{
int vertexoffset = ...
int vertexcount = ...

instancecount = whatever ...
// FILL THE INSTANCEBUFFER HERE WITH ALL MVP MATRICES FOR THIS MODEL

glDrawArraysInstanced(GL_TRIANGLES, vertexoffset , vertexcount , instancecount);
}

3DPrgmer
11-06-2016, 07:31 AM
2298 Ahh, so i call the glDrawArraysInstanced one time for each model?? I thought i call it only once. The instancecount is the max number of instances right? or is it the current instance?

john_connor
11-06-2016, 07:47 AM
the number of instances you want to draw
https://sites.google.com/site/john87connor/home/tutorial-11-1-uniform-block
https://sites.google.com/site/john87connor/home/tutorial-12-instanced-rendering

GClements
11-06-2016, 01:48 PM
But i get an error:


0(19): error C7559: OpenGL requires constant indexes for unsigned array access (MVP)


Did you declare MVP with a size?

The array size must be known at compile time, so you either have to specify the size explicitly or use constant indices.

If you declared the array with a fixed size, try changing the attribute from uint to int.

3DPrgmer
11-07-2016, 03:23 AM
@GClements: That's the problem i know the size only at run time. Only way would be to change the shader code after reading in the mesh and recompile it.

@John:
Init my pipes:
https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L89

Question: Why do i need 4 pipes and why can't i simply move the whole mat4 over one pipe? I don't understand how i can put the data onto 4 pipes and catch it from only one.

fill the buffer:
https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L389

draw multiple times instanced:
https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L284

Question: not sure about the number of instance. shouldn't it be only 1??

Result: nothing displayed :mad:

GClements
11-07-2016, 04:20 AM
@GClements: That's the problem i know the size only at run time. Only way would be to change the shader code after reading in the mesh and recompile it.
The last element of a shader storage block may be an unsized array. All other arrays require the size to be known at compile time, by either being explicitly sized in the declaration or being indexed only with constant expressions.

Another option is to use a texture or image as an array.

If you can put a reasonable upper bound on the maximum number of matrices, you can just size of the array for the maximum. There's no requirement to use all of the entries.

If you use a uniform array in the default uniform block, bear in mind that the limit on the maximum number of components may be as low as 512 (corresponding to 32 mat4 variables), and may actually be slightly lower due to drivers "stealing" a few entries for internal use.

So you could just figure out how many matrices you can fit in the available space and size the array for that.

GClements
11-07-2016, 04:39 AM
Question: Why do i need 4 pipes and why can't i simply move the whole mat4 over one pipe? I don't understand how i can put the data onto 4 pipes and catch it from only one.

The size argument to glVertexAttribPointer(), glVertexAttribFormat(), etc can be 1, 2, 3 or 4. I.e. you can't have anything larger than a vec4 as a single attribute. You can declare vertex shader inputs as matrices, but that's little more than syntactic sugar; each column is essentially treated as a separate attribute.

john_connor
11-07-2016, 06:07 AM
there is no buffer bound at GL_ARRAY_BUFFER when you try to allocate memory
line 115


// set buffer size
glBufferData(GL_ARRAY_BUFFER, MAX_MODEL_COUNT * sizeof(glm::mat4), NULL, GL_STREAM_DRAW);


bind the instancebuffer before that line, and unbind it after that line again
i personallly find it easier to read if all the essential steps are separated, like:
1. setup VAO (configure attributes / buffers)
2. setup VBO (allocate memory / fill in data)
3. ...
and since memory allocation isnt part of te VAO, i remove the "glBufferData(...)" call while doing step 1



Question: not sure about the number of instance. shouldn't it be only 1??

1 means you will get 1 triangle (or any mesh), 2 means 2 triangles (or any mesh), etc ...

example:
you want to draw those "star wars soliders" right ?
assuming 1 solider has 9999 vertices, and assuming you already have some 500 vertices before that mesh in your vertex buffer (for any reason, doesnt matter for now), and assuming you want to draw 10 x 10 = 100 soliders, you would call:


glDrawArraysInstanced(GL_TRIANGLES, 500, 9999, 100);

but before you call that line, you have to myke sure that 100 proper MVP matrices are in your instance buffer
for (int x = -5; x < 5; x++)
for (int y = -5; y < 5; y++)
temp_instances.push_back(glm::translate(glm::vec3( x, y, 0)));

glBindBuffer(GL_ARRAY_BUFFER, instancebuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(mat4) * instances.size(), instances.data());
glBindBuffer(GL_ARRAY_BUFFER, 0);

3DPrgmer
11-07-2016, 07:07 AM
there is no buffer bound at GL_ARRAY_BUFFER when you try to allocate memory
line 115


// set buffer size
glBufferData(GL_ARRAY_BUFFER, MAX_MODEL_COUNT * sizeof(glm::mat4), NULL, GL_STREAM_DRAW);


bind the instancebuffer before that line, and unbind it after that line again
i personallly find it easier to read if all the essential steps are separated, like:
1. setup VAO (configure attributes / buffers)
2. setup VBO (allocate memory / fill in data)
3. ...
and since memory allocation isnt part of te VAO, i remove the "glBufferData(...)" call while doing step 1



oops i forgot about binding the buffer first. Fixed it and moved it to the openMesh function, since i now in that function actually how many memory i need and so i don't need to allocate first and fill the data later.
So removed it here: https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L115
and now it is here: https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L385




1 means you will get 1 triangle (or any mesh), 2 means 2 triangles (or any mesh), etc ...

example:
you want to draw those "star wars soliders" right ?
assuming 1 solider has 9999 vertices, and assuming you already have some 500 vertices before that mesh in your vertex buffer (for any reason, doesnt matter for now), and assuming you want to draw 10 x 10 = 100 soliders, you would call:


glDrawArraysInstanced(GL_TRIANGLES, 500, 9999, 100);

but before you call that line, you have to myke sure that 100 proper MVP matrices are in your instance buffer
for (int x = -5; x < 5; x++)
for (int y = -5; y < 5; y++)
temp_instances.push_back(glm::translate(glm::vec3( x, y, 0)));

glBindBuffer(GL_ARRAY_BUFFER, instancebuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(mat4) * instances.size(), instances.data());
glBindBuffer(GL_ARRAY_BUFFER, 0);
but i don't wanna draw 100 soldiers. i want just one soldier :(

I would use 1 instead of vModels.size()); here: https://git.rwth-aachen.de/carstenf/OpenGL/blob/master/MshViewer/Source/OpenGlController.cpp#L281

and to be honest i don't see a real different to my old idea calling n time the glDrawArrays function :-/

But still the same problem. Nothing is displayed (ich dreh langsam am Rad)

john_connor
11-07-2016, 07:17 AM
but according to the title of this topic, you want to draw multiple "objects" (which can be translated into "instances of a certain model"), is that correct ?

if so, glDrawArraysInstanced() is the way to do that for many instances
otherwise you can use glDrawArrays() as you did previously


was für ne art "mesh viewer" stellste dir vor?
.obj dateien z.B. haben öfters mehrere unterschiedlich "objekte"
http://tf3dm.com/3d-model/the-city-39441.html
das hier z.B ist ne ganze stadt in ner einzigen datei

3DPrgmer
11-07-2016, 08:13 AM
it should be something like that: http://www.nexusmods.com/battlefront2/mods/1061/?

And i think there was a miss understanding. I have one mesh file and that contains multiple different model chunks. Such as one for the head, one for the left arm, one for the right arm, one for the upper body/lower body and a backpack. And each have their own MVP. So i want to draw the backpack with the backpack's mvp and the upper body with the upper body's mvp,...
The msh format is a bit different from .obj if you are interessted in the structure, you can take a look at http://schlechtwetterfront.github.io/ze_filetypes/msh.html

And yeah i found the problem, why nothing was displayed at the latest tries. I used the MVP variable (coming from uniform) instead of mvp (coming over the pipes) :doh:

now everything is like it should be: https://git.rwth-aachen.de/carstenf/OpenGL/tree/master

If you download the release folder, you can try the program :D it's a stable release build

congviec0123
11-09-2016, 06:47 PM
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

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