How to pass Array to Shader like Vertices?

Following a tutorial on importing animations using ASSIMP I’ve come across a problem in that the developers code was intended for a mesh where every vertex was affected by no more than 4 bones, which he thusly passed as two vec4’s in the layout section.

Here is presumably his code:


   	glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[BONE_VB]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Bones[0]) * Bones.size(), &Bones[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(BONE_ID_LOCATION);

    glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
    glEnableVertexAttribArray(BONE_WEIGHT_LOCATION);    
    glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)16);

VertexBoneData is a struct:


    struct VertexBoneData
    {        
        uint IDs[NUM_BONES_PER_VEREX];
        float Weights[NUM_BONES_PER_VEREX]; }

and Bones is a vector of VertexBoneData, his shader looks like:


#version 330                                                                        
                                                                                    
layout (location = 0) in vec3 Position;                                             
layout (location = 1) in vec2 TexCoord;                                             
layout (location = 2) in vec3 Normal;                                               
layout (location = 3) in ivec4 BoneIDs;
layout (location = 4) in vec4 Weights;

out vec2 TexCoord0;
out vec3 Normal0;                                                                   
out vec3 WorldPos0;                                                                 

const int MAX_BONES = 100;

uniform mat4 gWVP;
uniform mat4 gWorld;
uniform mat4 gBones[MAX_BONES];

void main()
{       
	mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
	BoneTransform     += gBones[BoneIDs[1]] * Weights[1];
	BoneTransform     += gBones[BoneIDs[2]] * Weights[2];
	BoneTransform     += gBones[BoneIDs[3]] * Weights[3];

    vec4 PosL    = BoneTransform * vec4(Position, 1.0);
    gl_Position  = gWVP * PosL;
    TexCoord0    = TexCoord;
    vec4 NormalL = BoneTransform * vec4(Normal, 0.0);
    Normal0      = (gWorld * NormalL).xyz;
    WorldPos0    = (gWorld * PosL).xyz;                                
}

I believe my mesh ends up with 6 bones but I had 12 as the max size to be safe.

My idea to get around this is to pass two matrices and have the shader look like this:


#version 330                                                                        
                                                                                    
layout (location = 0) in vec3 Position;                                             
layout (location = 1) in vec2 TexCoord;                                             
layout (location = 2) in vec3 Normal;                                               
layout (location = 3) in mat4 BoneIDs;
layout (location = 4) in mat4 Weights;

out vec2 TexCoord0;
out vec3 Normal0;                                                                   
out vec3 WorldPos0;                                                                 

const int MAX_BONES = 100;

uniform mat4 gWVP;
uniform mat4 gWorld;
uniform mat4 gBones[MAX_BONES];

void main()
{       

	ivec4 BoneIDvec1 = ivec4(BoneIDs[0][0], BoneIDs[0][1], BoneIDs[0][2], BoneIDs[0][3]);
	ivec4 BoneIDvec2 = ivec4(BoneIDs[0][0], BoneIDs[0][1], BoneIDs[0][2], BoneIDs[0][3]);
	ivec4 BoneIDvec3 = ivec4(BoneIDs[0][0], BoneIDs[0][1], BoneIDs[0][2], BoneIDs[0][3]);
	ivec4 BoneIDvec4 = ivec4(BoneIDs[0][0], BoneIDs[0][1], BoneIDs[0][2], BoneIDs[0][3]);
    mat4 BoneTransform = gBones[BoneIDvec1[0]] * Weights[0][0];
   BoneTransform     += gBones[BoneIDvec1[1]] * Weights[0][1];
    BoneTransform     += gBones[BoneIDvec1[2]] * Weights[0][2];
  BoneTransform     += gBones[BoneIDvec1[3]] * Weights[0][3];
	BoneTransform     += gBones[BoneIDvec2[0]] * Weights[1][0];
	BoneTransform     += gBones[BoneIDvec2[1]] * Weights[1][1];
	BoneTransform     += gBones[BoneIDvec2[2]] * Weights[1][2];
	BoneTransform     += gBones[BoneIDvec2[3]] * Weights[1][3];
	BoneTransform     += gBones[BoneIDvec3[0]] * Weights[2][0];
	BoneTransform     += gBones[BoneIDvec3[1]] * Weights[2][1];
	BoneTransform     += gBones[BoneIDvec3[2]] * Weights[2][2];
	BoneTransform     += gBones[BoneIDvec3[3]] * Weights[2][3];

    vec4 PosL    = BoneTransform * vec4(Position, 1.0);
    gl_Position  = gWVP * PosL;
    TexCoord0    = TexCoord;
    vec4 NormalL = BoneTransform * vec4(Normal, 0.0);
    Normal0      = (gWorld * NormalL).xyz;
    WorldPos0    = (gWorld * PosL).xyz;                                
}

Would this work in theory? It seems like a lot of work, as I would need I think, to enter in all of his vertex data into two 4v4 matrices, and even then that’s a maximum of 16 slots.

Thoughts?

You could use Uniform Buffer Objects (available from GL 3.1, may have smallish size limit) or Shader Storage Buffer Objects (available from 4.3, much larger) and use the vertex index to look up the bone/weight data.
Other than that using extra vertex attributes should work as well. I think you have to assign location 7 to the Weights mat4, because locations 4,5,6,7 are taken up by the BoneIDs mat4.

Wait what, a mat4 uses up 4 spots?

Otherwise though, there’s a different between passing a uniform and passing whats in the layout section right performance wise?

Wait what, a mat4 uses up 4 spots?

That’s what I remember, I might confuse it with something else, but I thought matrix vertex attributes are just syntactic sugar for passing the columns as separate attributes, so they consume the same amount of layout slots.

Sorry, no idea about the performance trade-offs.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.