Reputation: 2280
I'm writing and OpenGL application where I have a GrassPatch class that represents patches of grass in the scene. I don't want to provide any unnecessary details, so the GrassPatch.cpp looks roughly like this:
GrassPatch::GrassPatch(GLuint density)
{
m_density = density;
generateVertices();
}
void GrassPatch::generateVertices()
{
const int quadVertexCount = 64;
GLfloat bladeWidth, bladeHeight, r;
GLfloat randomX, randomZ;
m_vertices = new GLfloat[quadVertexCount * m_density];
srand(time(NULL));
for (int i = 0; i < m_density; i++)
{
// generate 64 float values and put them into their respective indices in m_vertices
}
glGenBuffers(1, &m_VBO);
glGenVertexArrays(1, &m_VAO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_density * quadVertexCount, m_vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(5 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 8, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat)));
glEnableVertexAttribArray(3);
glBindVertexArray(0);
}
void GrassPatch::draw()
{
glBindVertexArray(m_VAO);
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawArrays(GL_PATCHES, 0, 4 * m_density);
glBindVertexArray(0);
}
In short, the vertex array object (VAO) for each grass patch is generated inside generatVertices. My data is tightly packed and the attributes for each vertex are at indices 0, 3, 5, 8, where each vertex is composed of 16 float
's. Each grass blade consists of 4 vertices, hence quadVertexCount is set to 64. The vertex shader I use is pretty straighforward and looks like this:
#version 440 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 centerPos;
layout (location = 3) in float randomValues[8];
out vec2 TexCoord_CS;
void main()
{
TexCoord_CS = texCoord;
gl_Position = vec4(position, 1.0f);
}
The problem here is, when I try to draw each grass blade using the draw()
method, I get an access violation error. However, if I slightly change the attribute indices to 0, 4, 8, 12 and make the necessary variable type changes in the vertex shader, the problem disappears and everything renders fine.
What am I missing here, what would cause a problem like this? I've spent hours on the Internet, trying to find the reason but couldn't come up with anything yet. I'm working with Visual Studio 2015 Community Edition. The graphics card I use is NVIDIA GTX 770 and all drivers are up to date.
Upvotes: 2
Views: 283
Reputation: 473352
layout (location = 3) in float randomValues[8];
This is not a single input value. This is an array of input values. While this is perfectly legal, it does change what this means.
In particular, it means that this input array is filled in by eight separate attributes. Yes, each one of those floats is a separate attribute, from the OpenGL side. They are assigned locations sequentially, starting with the location you specified. So the input randomValues[4]
comes from attribute location 7 (3 + 4).
So your attempt to provide 8 values with one glVertexAttribPointer
call will not work. Well, it was never going to work, since the number of components per attribute must be on the range [1, 4]. But it double-doesn't work, since you're not filling in the other 7.
If you want to pass these 8 elements as 8 attributes like this, you therefore need eight independent calls to glVertexAttribPointer
:
for(int ix = 0; ix < 8; ++ix)
glVertexAttribPointer(3 + ix, 1, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)((8 + ix) * sizeof(GLfloat)));
But quite frankly, you shouldn't do that. Instead of passing 8 independent attributes, you should pass 2 vec4
's:
layout (location = 3) in vec4 randomValues[2];
That way, you only need 2 attributes in your OpenGL code:
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat)));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(12 * sizeof(GLfloat)));
Upvotes: 1
Reputation: 54592
This is not a valid call:
glVertexAttribPointer(3, 8, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat)));
The second argument (size) needs to be 1, 2, 3, or 4. If you call glGetError()
, you should see a GL_INVALID_VALUE
error code from this call.
Vertex attributes can only have up to 4 components, matching a vec4
type in the shader code. If you need 8 values for an attribute, you'll have to split it into 2 attributes of 4 values each, or use uniforms instead of attributes.
Upvotes: 2