8

I'm trying to pass a structure to a simple GLSL vetrex shader. Here's how the structure look like on the C++ side:

struct Vertex
{
    float position[3];
    char boneIndex[4];
    float weights[4];
    float normals[3];
    float textureCords[2];
};

Is it possible to pass array of this vertex to the vertex shader without creating a separate array for each component?

Can I do something like this:

#version 330 core

uniform mat4 MVP;

layout(location = 0) in struct Vertex
{
    vec3 position;
    uint boneIndex;
    vec4 weights;
    vec3 normals;
    vec2 textureCords;
} vertex;

out vec2 UV;

void main()
{
    gl_Position =  MVP * vec4(vertex.position, 1.0f);
    UV = vertex.textureCords;
}

(Dont mind that not all of the components are in use, it's just for the example.)

And if I can, how can I pass the data to it from the C++ side using the glVertexAttribPointer() function? (According to my understanding you can pass only 1,2,3,4 to the size parameter).

Is it the even the right way to do things? I'm a beginner in OpenGL programming so if you have an answer please don't hesitate to include obvious details.

Edit:

I ended up doing something like this:

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glEnableVertexAttribArray(3);
    glEnableVertexAttribArray(4);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);  //float position[3]
    glVertexAttribPointer(1, 1, GL_INT, GL_FALSE, 12, (void*)0);    //char boneIndex[4]
    glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 16, (void*)0); //float weights[4]
    glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 32, (void*)0); //float normals[3]
    glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 44, (void*)0); //float textureCords[2]


    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);

    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, (void*)0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glDisableVertexAttribArray(3);
    glDisableVertexAttribArray(4);

On the C++ size, and on the GLSL vertex shader:

    #version 330 core

uniform mat4 MVP;

layout(location = 0) in vec3 position;
layout(location = 1) in int boneIndex;
layout(location = 2) in vec4 weight;
layout(location = 3) in vec3 normal;
layout(location = 4) in vec2 UVCords;

out vec2 UV;

void main()
{
    gl_Position =  MVP * vec4(position, 1.0f);
    UV = UVCords;
}

But it still won't work, the model wont render correctly

5
  • 1
    Each of the fields of your structure when using an interface block is assigned a sequential location. Anything vec4 or smaller uses a single attribute location, things like mat4 use 4 locations (it might help to think of them as an array of 4 vec4s). So assigning with glVertexAttribPointer (...) should be pretty straight forward. Commented Sep 29, 2013 at 18:39
  • So actually I can use the same C++ code used to pass the data to this shader? layout(location=0) in vec3 position; layout(location=1) in uint boneIndex; C++ OpenGL: glVertexAttribPointer(0, ... glVertexAttribPointer(1, ... Will it work the same? Commented Sep 29, 2013 at 18:42
  • I have not seen the actual C++ code you use for this, but it should work. I would also point out that the bone index should be passed with glVertexAttribIPointer (...) because it is an integer field whereas all of your other vertex data are floating-point. Commented Sep 29, 2013 at 18:45
  • @Andon M. Coleman Please see the updated question, can you see something wrong in that code? Commented Jan 11, 2014 at 13:12
  • You have to add subsequent offsets to the attributes and correct stride information. When using two attributes of size n, first will have offset 0 and stride 2n, and the second offset n and the very same stride. Once you get that it'll be easy to do it for more than 2 attributes. Commented Jan 11, 2014 at 16:40

1 Answer 1

10
+50

The method you've used to transfer the interleaved data is correct (that is, using glVertexAttribPointer, however, your last two parameters are incorrect).

The penultimate is the stride (which is the same for every item in the struct, and should be the size of the struct), and the last is the offset, which should be different for each element (as they are at different offsets in the struct itself).

Also it is best not to use constants here, but instead, use the sizeof() operator, to make your code as platform independent as possible.

Here's what it should look like:

glVertexAttribPointer(
    0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr
); //float position[3]

glVertexAttribIPointer(
    1, 1, GL_INT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, boneIndex))
); //char boneIndex[4]

glVertexAttribPointer(
    2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, weights))
); //float weights[4]

glVertexAttribPointer(
    3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, normals))
); //float normals[3]

glVertexAttribPointer(
    4, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    std::reinterpret_cast<void *>(offsetof(Vertex, textureCoords))
); //float textureCoords[2]

Also, you should probably ensure that your Vertex struct is not packed to fit into a nice word boundary. This is done by using #pragma pack(0), if your compiler supports it. (Remember to reset this after after your struct, because otherwise the compiler will use this directive throughout the rest of the compilation process, and all your aggregate data structures will be structured in a way that doesn't yield to the best possible fit with regards to world alignment.), here's what that looks like:

#pragma pack(push, 0)

struct Vertex {
    float position[3];
    char  boneIndex[4];
    float weights[4],
          normals[4],
          textureCoords[2];
};

#pragma pack(pop)
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks alot! It solved my problem! I read the documentation for this functions on: opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml And it got me a bit confused about those lase two paramteres. About the pack directive, it is not needed because the struct is organized in a way the does not require padding.
Glad to hear it, I've just changed it to use the offsetof(...) function, seeing as it makes a bit more sense than hardcoding the sizeof() values. Read about it here
You can't be sure about how a system will pack your structs, so it's always safer to specify the directive and have nothing go wrong, than not and get odd errors when you try and port to some obscure system with 3 word boundaries.
Why would any compiler insert padding in this struct? Where will he put them? And for what reason?
Well put it this way, you stand to lose nothing by adding #pragma pack(...) directives, and what's more it is made clear to those reading your code that this data is not intended to be padded, whereas if you don't add them, it is possible that later on down the line, you'll get an obscure bug, either because you added an extra element to your struct, or because your compiler padded it and you weren't expecting it. I'm not telling you that the compiler will pad your struct, just warning you to be prepared if it does. In the end it is your call, my recommendation, however, stands :P
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.