I am trying to make instanced rendering work with multiple differently-sized 'quads' (two connected triangles). I am setting up a VAO with a vertex data VBO and a transform data VBO, along side an index EBO to stitch these vertices together. For some reason, using the transform VBO makes the glDrawElementsInstanced ignore the actual sizes of the vertex data and just uses one set for all 'quads'. It looks like this.
The quad on the right should be half the size of the left one. It seems like opengl picks the larger size regardless of how I add the vertex data.
Here is my opengl render init:
Renderer::Renderer(Shader* staticShader)
: m_ShaderStatic(staticShader)
{
// Generate all needed buffers
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VertVBO);
glGenBuffers(1, &m_TransVBO);
glGenBuffers(1, &m_EBO);
// Bind VAO
glBindVertexArray(m_VAO);
// Bind and set up vertex VBO
glBindBuffer(GL_ARRAY_BUFFER, m_VertVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_FLOAT) * 2, NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, (void*)0);
glEnableVertexAttribArray(0);
// Bind and set up transform VBO
glBindBuffer(GL_ARRAY_BUFFER, m_TransVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GL_FLOAT) * 2, NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribDivisor(1, 1);
// Bind and set up EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 6, NULL, GL_DYNAMIC_DRAW);
// Unbind everything
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Here is the AddStatic function to give the buffers data:
void Renderer::AddStatic(float x, float y, float width, float height, float rotation)
{
// Bind VAO
glBindVertexArray(m_VAO);
// Configure vertex data
for (int i = 0; i < 8; i+=2)
{
m_Vertices.push_back(QuadVertices[i] * width);
m_Vertices.push_back(QuadVertices[i+1] * height);
}
// Set vertex data
glBindBuffer(GL_ARRAY_BUFFER, m_VertVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * m_Vertices.size(), m_Vertices.data(), GL_DYNAMIC_DRAW);
// Configure transform data
m_Transforms.push_back(x);
m_Transforms.push_back(y);
// Set transform data
glBindBuffer(GL_ARRAY_BUFFER, m_TransVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * m_Transforms.size(), m_Transforms.data(), GL_DYNAMIC_DRAW);
// Configure index data
m_Indices.push_back(0 + 4 * m_QuadCount);
m_Indices.push_back(1 + 4 * m_QuadCount);
m_Indices.push_back(3 + 4 * m_QuadCount);
m_Indices.push_back(3 + 4 * m_QuadCount);
m_Indices.push_back(2 + 4 * m_QuadCount);
m_Indices.push_back(0 + 4 * m_QuadCount);
// Set index data
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * m_Indices.size(), m_Indices.data(), GL_DYNAMIC_DRAW);
// Unbind all
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Increment the amount of quads loaded
m_QuadCount++;
}
Here is the actual draw call:
void Renderer::Render()
{
// Bind VAO
glBindVertexArray(m_VAO);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
// Bind Shader
m_ShaderStatic->use();
// Draw
glDrawElementsInstanced(GL_TRIANGLES, m_Indices.size(), GL_UNSIGNED_INT, 0, m_QuadCount);
}
And finally, here is my vertex shader if that is needed.
#version 330 core
layout (location = 0) in vec2 vertex;
layout (location = 1) in vec2 transform;
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
out vec4 color;
void main()
{
gl_Position = projection * view * vec4(vertex + transform, 0.0, 1.0);
color = vec4(1.0, 1.0, 1.0, 1.0);
}
I dont know why a simple transform would affect the actual size of my quads, all it does is just add a set x/y to them.
Edit: If I set the transform values i put into the vector to 0, it renders the squares at the correct width/height, albeit at the wrong location.
I think you're fundamentally misunderstanding instanced rendering. But, as with everything graphics related, a visual explanation is worth more than a thousands words. So:
With the following quads:
void add_quad(const MATH::Vector2f& position, const MATH::Vector2f& size, const MATH::Vector4f& color);
add_quad(MATH::Vector2f(100.0f, 100.0f), MATH::Vector2f(10.0f, 10.0f), MATH::Vector4f(1.0f, 0.0f, 0.0f, 0.5f));
add_quad(MATH::Vector2f(200.0f, 200.0f), MATH::Vector2f(20.0f, 20.0f), MATH::Vector4f(0.0f, 1.0f, 0.0f, 0.5f));
add_quad(MATH::Vector2f(300.0f, 300.0f), MATH::Vector2f(30.0f, 30.0f), MATH::Vector4f(0.0f, 0.0f, 1.0f, 0.5f));
And fully additive blending (glBlendFunc(GL_ONE, GL_ONE)
).
As you can see, the three quads are being rendered at every transform/position. This is exactly what instanced rendering does: It renders a mesh (in this case, the three quads together form a single mesh) multiple times, with separate vertex attributes that change for each consecutive mesh (in this case the transform, which is applied to every quad). In simple words, every quad is being rendered at every transform position.
In order to achieve what you want to do with instanced rendering, you only need a single quad mesh for the usual vertex and index data. The transform (translation AND scaling) would then be the instanced vertex attributes. Using a 3x3 matrix for this would probably be easier than trying to split this up into many different individual transforms. This is essentially the same as making the model matrix be an instanced attribute rather than a uniform.