6
\$\begingroup\$

I'm trying to separate the vertex format specification from the vertex data. I was able to do that for the mesh vertices successfully. For instanced rendering I wanted to further separate the instance data (i.e. model matrices). Here is what I'm trying out, but nothing is rendered.

Loading the data:

glGenBuffers(1, &vbo_instanced);
glBindBuffer(GL_ARRAY_BUFFER, vbo_instanced);
glBufferData(GL_ARRAY_BUFFER, n_instances * sizeof(glm::mat4), ModelArray, GL_STATIC_DRAW);

Vertex format:

glGenVertexArrays(1, &vao_instanced);

glBindVertexArray(vao_instanced);

glEnableVertexAttribArray(0);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(0, 0);

glEnableVertexAttribArray(1);
glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3));
glVertexAttribBinding(1, 0);

glVertexBindingDivisor(1, 1);
for(int i = 0; i < 4; i++){ //Model Matrices
    glEnableVertexAttribArray(2 + i);
    glVertexAttribBinding(2 + i, 1);
    glVertexAttribFormat(2 + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4));
}

When rendering I do the following,

glBindVertexArray(vao_instanced);
glBindVertexBuffer(0, vbo_mesh, 0, sizeof(Vertex));
glBindVertexBuffer(1, vbo_instanced, 0, sizeof(glm::mat4));
glDrawArraysInstanced(GL_TRIANGLES, 0, n_vertices, n_instances);

but I'm pretty sure the stride argument in glBindVertexBuffer of vbo_instanced should be wrong because it is the stride per vertex.

Does anyone know what I'm doing wrong and how to fix it?

EDIT 1: It seems I made a mistake in the last argument of glVertexAttribFormat, it should instead be,

glVertexAttribFormat(2 + i, 4, GL_FLOAT, GL_FALSE, 4 * i * sizeof(float));

But now, although everything gets rendered, my normals are wrong. Even if I don't transform them, they have a single value per instance instead of a single value per face.

This is what the normals look like, enter image description here

And this is what it should actually look like, enter image description here

Note that the normals are fine as long as I instead use the glVertexAttribPointer method, i.e. bind the vbo_instanced directly to the vao.

For reference, here is how I declare the attributes in my shader,

#version 330 core

layout(location = 0) in vec3 in_Position;
layout(location = 1) in vec3 in_Normal;
layout(location = 2) in mat4 ModelMatrix;

EDIT 2: I wanted to add that I have checked that all offset values are correct. Additionally, I used apitrace and checked that all vertex buffer data are correctly uploaded. Valgrind also doesn't show any errors.

Now for a weird finding, if I don't use vertex attribute array with index 1 and instead bind vertex positions at 0, vertex normals at 2, and Model Matrices at 3, everything is rendering correctly. Could this be some driver bug? I tried on two linux systems, both having nvidia gpus, with exactly the same results.

EDIT 3: I put together a minimal working example which you can find here. It uses SDL and GLEW and is written in C++11. If I set SKIP to 1 (which means it skips attribute 1), I get what I expect, but if I set it to 0, the normals become messed up.

\$\endgroup\$
4
  • \$\begingroup\$ So in_Position and ModelMatrix are okay, except in_Normal. In your code you set glVertexBindingDivisor(1, 1); - which refers to in_Normal location. What happen if you remove this or add divisors for others bindings? \$\endgroup\$ Jul 21, 2015 at 11:43
  • \$\begingroup\$ @AdrianKrupa If I'm not mistaken, the first argument of glVertexBindingDivisor refers to the binding index, the vbo is bound to index 1 when glBindVertexBuffer is called. But I will try it out just in case. \$\endgroup\$ Jul 21, 2015 at 17:14
  • \$\begingroup\$ There's quite a lot wrong with this - I mean well but it's VERY hard to read and I cannot find evidence you even bind to 3 buffers, which would result in undefined behaviour \$\endgroup\$
    – Alec Teal
    Sep 23, 2015 at 19:48
  • 1
    \$\begingroup\$ @AlecTeal Note the VertexAttribBinding calls that associate attribute indices with buffer indices. It's not at all uncommon to have multiple attributes refer to a single interleaved or concatenated buffer via offsets and strides. \$\endgroup\$ Jan 22, 2016 at 8:27

2 Answers 2

2
\$\begingroup\$

So, I am going to assume that all the instances share the same model data (vertex positions, vertex normals.)

In that case, the Divisor() call is incorrect: currently you tell OpenGL that each instance has its own normal: this is typically incorrect: the instances share positions and normals, and have a matrix each.

The slots for the attributes don't matter: it is fine to have position at 0, normal at 1, and matrix at 3,4,5,6.

What you need to do is tell OpenGL that for the matrices you will provide one matrix for every instance, this means a Divisor of value 1.

Personally I would use:

glVertexAttribDivisor( o+0, 1 );
glVertexAttribDivisor( o+1, 1 );
glVertexAttribDivisor( o+2, 1 );
glVertexAttribDivisor( o+3, 1 );

...where o is the location where you bind the matrices.

You seem to use glVertexBindingDivisor() instead, which I don't know, but assume is roughly the same as glVertexAttribDivisor(). But whatever it is, I am pretty sure you should be setting it on the matrix attributes and not on attributes 0 and 1.

TL;DR: you need to take out the glVertexBindingDivisor(1, 1) because normals are not per instance.

\$\endgroup\$
2
  • \$\begingroup\$ glVertexBindingDivisor() is new to core in OpenGL 4.3. It's part of ARB_vertex_attrib_binding, which allows programs to map buffers to different bind points, and then map those bind points to attribute locations, instead of mapping buffers to the attributes directly. Yes, it is fundamentally equivalent to glVertexAttribDivisor(), which has been around since OpenGL 3.3; it just supports an extra layer of indirection between the attribute and the buffer. \$\endgroup\$ Mar 11, 2018 at 0:19
  • 1
    \$\begingroup\$ And just to be extra clear (@Bram mentioned this in prose, but not in code, so I worry it'll be overlooked), the "one-normal-per-instance" problem is being caused by this line of code: glVertexBindingDivisor(1, 1);, which tells OpenGL to interpret the 'normal' vertex attribute as having just one value per instance, instead of one per vertex. To fix that problem and get the vertex normals working properly, that line should be changed to glVertexBindingDivisor(1, 0); \$\endgroup\$ Mar 11, 2018 at 0:29
0
\$\begingroup\$

It's possible that your attribute offsets supplied to glVertexAttribFormat are failing to take data structure padding into account, leading to incorrectly-read vertex normals once your interleaved vertex buffer is bound. When using glVertexAttribPointer, did you calculate the offset pointer in the same way as described above, or were you taking it from the actual data structure layout (e.g. via offsetof)?

You can test for this by measuring the offsets of your actual interleaved vertex structure (via offsetof), and comparing those offsets to your calculated offsets.

\$\endgroup\$
1
  • \$\begingroup\$ Julian, I thought about that, I should have talked about it in the question. The offsets are calculated the same way I did with glVertexAttribPointer. I even tried using offsetof which gave exactly the same offsets. \$\endgroup\$ Jul 21, 2015 at 17:19

You must log in to answer this question.

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