0
\$\begingroup\$

I am currently implementing "instancing" into my game engine to allow the drawing of 1000 tree models following the tutorial found here: https://learnopengl.com/Advanced-OpenGL/Instancing.

I have the draw call:

glDrawElementsInstanced(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0, 100);

Then in my shader I have the uniform:

uniform vec3 A_offsets[100];

This uniform is then used like so:

vec3 offset = A_offsets[gl_InstanceID];
gl_Position = T_MVP * vec4(position + offset, 1.0);

A_offsets up to this point is empty, so my 100 models (I am testing with 100 first, then when it works I can make this 1000) are draw successfully but in the same position, this was just to test the setup at least works, which it does.

But now I need to fill each index of A_offsets array and I am a little confused on how to do that. My engine stores the name and type of all uniforms using a parser on my shader files (.glsl), and in my game loop I get the uniforms by name or type and set values to them in a updateUniforms() call before the above draw() call. A simple vec3 uniform is being filled like so:

if(uniformType == "vec3")
                SetUniformVector3f(uniformName, material.GetVector3f(uniformName));

The setUnifromVector3f function is as follows:

void Shader::SetUniformVector3f(const std::string& uniformName, const Vector3f& value) const
{
    glUniform3f(m_shaderData->GetUniformMap().at(uniformName), value.GetX(), value.GetY(), value.GetZ());
}

This would set the value of a vec3 unifrom before the draw() call, my question is this - how would I do the equivalent to set the value of each element (index) in my vec3 array uniform A_offsets? Ultimately I would want to assign each index of an array (std::vector<Vector3f>) I have already defined to each index of A_offsets array. I am yet to find an example online that uses perhaps glUniform3fv(...) to fill each index of a uniform that is an array.

UPDATE(One day after asking original question):

I have made some progress since asking this question and did not know quite how to convey this, creating a completely new GDSE question seemed silly and commenting on or answering the original question is not really what either of those features are for if all I am doing is giving an update on the problem, so I am just continuing the original question, in a way.

I have made developments that in my opinion have gotten instancing to work! I can see what looks like 1000 tree models drawn in my game with very low frame rate (16ms per frame draw), however, the rest of my game world (the terrain model, water model etc) are definitely "off", their positions are strange and my collision with them is way out of whack.

My slightly updated code is as follows:

Draw call:

glDrawElementsInstanced(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0, 1000);

Shader uniform:

uniform vec3 A_offsets[1000];

Uniform in use in the shader:

vec3 offset = A_offsets[gl_InstanceID];
gl_Position = T_MVP * vec4(position + offset, 1.0);

And now the new code, the filling of the uniform array A_offsets - First, I have a new variable:

glm::vec3 translations[1000];

This then gets filled when I create my simple shader program(I give a random value between a range for the x and z components of the vec3's, y component is just 0):

m_fileName = fileName;
    if (fileName == "forward-ambient") {
        for (int i = 0; i < 1000; i++) {
            translations[i] = glm::vec3(-550 + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (550 - (-550)))), 0, -550 + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (550 - (-550)))));
        }
    }

And then in my updateUniforms() call before the draw() call I pass the translations array to a function that passes it to the shader to set the values in uniform A_offsets array:

else if (uniformName.substr(0, 2) == "A_") {//"A_" for "A_offsets"
            SetUniformVector3fv(uniformName, translations);
        }

function setUniformVector3fv(...) is as follows:

void Shader::SetUniformVector3fv(const std::string& uniformName, const glm::vec3 test[]) const
{
    glUniform3fv(m_shaderData->GetUniformMap().at(uniformName), 1000, (const GLfloat *)&test[0]);
}

The above code (in my mind) fills each index of my uniform array A_offsets with the value in each index of the previously setup translations array. I believe this works in rendering 1000 instances of my tree model as can be seen in the following image:

enter image description here

The issue is now this - the rest of my meshes (the terrain and water) are all messed up, in that my collision with them is way off (you can see in the image I am floating quite high above the ground), not to mention the general position of them has changed quite a bit, and my lighting (Directional Light) is not hitting them correctly or at all by the look of it (you can see the whole scene is evenly lit very dimly almost as if only the ambient light is working).

I suspect that the issue is this line in the shader:

gl_Position = T_MVP * vec4(position + offset, 1.0);

Because I have it currently that for things that need drawn normally (the terrain and water meshes) they call this regular draw function:

glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);

But for meshes that need instancing (the trees) I call the new draw function:

glDrawElementsInstanced(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0, 1000);

Because of this when the terrain and water meshes are drawn they will enter the shader and at the line of code vec3 offset = A_offsets[gl_InstanceID]; and gl_Position = T_MVP * vec4(position + offset, 1.0); I have no idea what the value of offset will be that is effecting the position as gl_InstanceID wont even be a thing for the regular draw call (glDrawElements) so I have no idea what will happend at the line vec3 offset = A_offsets[gl_InstanceID];, and in fact for when things that dont need instancing are being drawn I dont even want them to use the uniform A_offsets at all. So I must assume this is effecting the position, collision and lighting of the terrain and water meshes. Ultimately for when the water and terrain meshes are being drawn the code:

uniform vec3 A_offsets[1000];
//...
vec3 offset = A_offsets[gl_InstanceID];
gl_Position = T_MVP * vec4(position + offset, 1.0);

Should just be:

//Not using uniform A_offsets at all
gl_Position = T_MVP * vec4(position, 1.0);

Does anyone have any ideas?

I think a solution could be: depending on weather I am drawing a tree model or not pass updateUniforms() a bool, true for the tree and false for every other type of mesh, then depending on the value of the bool update a uniform bool to be true or false, then in the shader depedning on that bool value (if else logic) use the following code for true(the model is my tree):

uniform vec3 A_offsets[1000];
//...
vec3 offset = A_offsets[gl_InstanceID];
gl_Position = T_MVP * vec4(position + offset, 1.0);

And for false (model is not the tree) use the follwing code:

//Not using uniform A_offsets at all
gl_Position = T_MVP * vec4(position, 1.0);
\$\endgroup\$
2
  • 1
    \$\begingroup\$ What problem are you having with glUniform3fv? Is your question answered here? stackoverflow.com/questions/29804488/… \$\endgroup\$ Mar 4, 2020 at 12:49
  • 3
    \$\begingroup\$ This is a completely different question and should have been asked as a separate question. \$\endgroup\$ Mar 5, 2020 at 2:50

1 Answer 1

0
\$\begingroup\$

My above suggested solution at the end of the question worked. I now have a new uniform:

uniform bool B_tree;

Which is used in the shader file as follows:

if(B_tree){
        vec3 offset = A_offsets[gl_InstanceID];
        gl_Position = T_MVP * vec4(position + offset, 1.0);
}else{
        gl_Position = T_MVP * vec4(position, 1.0);
}

This uniform is being set as follows - updateUniforms(...) is now passed a bool isTree, and this new argument is used as follows:

else if (uniformName.substr(0, 2) == "B_") {//"B_" for B_tree uniform
        if (isTree) {
                SetUniformi(uniformName, 1);//non-zero value passed equates to true
        }
        else {
                SetUniformi(uniformName, 0);//zero value passed equates to false
        }
}

setUniformi() function is as follows:

void Shader::SetUniformi(const std::string& uniformName, int value) const
{
    glUniform1i(m_shaderData->GetUniformMap().at(uniformName), value);
}

Now everything works.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .