Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing custom vertex structure to GLSL shaders

Tags:

c++

opengl

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

like image 757
UnTraDe Avatar asked Sep 29 '13 18:09

UnTraDe


Video Answer


1 Answers

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)
like image 130
amnn Avatar answered Sep 19 '22 17:09

amnn