Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending bone data to glsl shader

Tags:

c++

opengl

Ok, so I'm able to load a basic model with vertices, texture coordinates, and normals and render it without issue.

However, when I try throwing in some bone information, the bone data seems to be getting corrupted (or something?) when I try and manipulate it in the shader.

Here's my code for loading and rendering the data into opengl (setting the shader and sending the view matrix, world matrix, etc are done in another class):

/*
 * Mesh.cpp
 *
 *  Created on: 2011-05-08
 *      Author: jarrett
 */

#include <boost/log/trivial.hpp>

#include "../common/utilities/AssImpUtilities.h"

#include "Mesh.h"

namespace glr {
namespace glw {
Mesh::Mesh(IOpenGlDevice* openGlDevice,
        const std::string path, 
        std::vector< glm::vec3 > vertices, 
        std::vector< glm::vec3 > normals,
        std::vector< glm::vec2 > textureCoordinates,
        std::vector< glm::vec4 > colors,
        std::vector<VertexBoneData > bones,
        BoneData boneData)
    : openGlDevice_(openGlDevice), vertices_(vertices), normals_(normals), textureCoordinates_(textureCoordinates), colors_(colors), bones_(bones), boneData_(boneData)
{
    BOOST_LOG_TRIVIAL(debug) << "loading mesh into video memory...";

    // create our vao
    glGenVertexArrays(1, &vaoId_);
    glBindVertexArray(vaoId_);

    // create our vbos
    glGenBuffers(5, &vboIds_[0]);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
    glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
    glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
    glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    std::cout << "SIZE: " << vertices_.size() << " " << sizeof(glm::ivec4) << " " << bones_.size() << " " << sizeof(VertexBoneData) << std::endl;

    // Convert data into simple vectors, then send to OpenGL
    std::vector< glm::ivec4 > boneIds = std::vector< glm::ivec4 >();
    std::vector< glm::vec4 > weights = std::vector< glm::vec4 >();

    for ( VertexBoneData& d : bones_ )
    {
        boneIds.push_back( d.boneIds );
        weights.push_back( d.weights );
    }

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
    glBufferData(GL_ARRAY_BUFFER, boneIds.size() * sizeof(glm::ivec4), &boneIds[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_INT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
    glBufferData(GL_ARRAY_BUFFER, weights.size() * sizeof(glm::vec4), &weights[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(4);
    glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 0, 0);

    // Disable our Vertex Array Object
    //glEnableVertexAttribArray(0);
    // Disable our Vertex Buffer Object
    glBindVertexArray(0);

    GlError err = openGlDevice_->getGlError();
    if (err.type != GL_NONE)
    {
        // TODO: throw error
        BOOST_LOG_TRIVIAL(error) << "Error loading mesh in opengl";
        BOOST_LOG_TRIVIAL(error) << "OpenGL error: " << err.name;
    }
    else
    {
        BOOST_LOG_TRIVIAL(debug) << "Successfully loaded mesh.";
    }
}

Mesh::~Mesh()
{
}

void Mesh::render()
{
    glBindVertexArray(vaoId_);

    glDrawArrays(GL_TRIANGLES, 0, vertices_.size());

    glBindVertexArray(0);
}

BoneData& Mesh::getBoneData()
{
    return boneData_;
}
}
}

My actual shader is this:

#version 150 core

struct Light {
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
    vec4 position;
    vec4 direction;
};


in vec3 in_Position;
in vec2 in_Texture;
in vec3 in_Normal;
in ivec4 in_BoneIds;
in vec4 in_BoneWeights;

out vec2 textureCoord;
out vec3 normalDirection;
out vec3 lightDirection;
out float bug;

layout(std140) uniform Lights 
{
    Light lights[ 1 ];
};

void main() {        
    gl_Position = pvmMatrix * vec4(in_Position, 1.0);

    vec4 lightDirTemp = viewMatrix * lights[0].direction;

    textureCoord = in_Texture;

    //vec4 normalDirTemp = boneTransform * vec4(in_Normal, 1.0);
    //normalDirection = normalize(normalMatrix * normalDirTemp.xyz);
    normalDirection = normalize(normalMatrix * in_Normal);

    lightDirection = normalize(vec3(lightDirTemp));



    // If we have any bugs, should highlight the vertex red or green
    bug = 0.0;
    float sum = in_BoneWeights[0] + in_BoneWeights[1] + in_BoneWeights[2] + in_BoneWeights[3];
    if (sum > 1.5f)
        bug = 1.0;
    else if (sum < 0.95f)
        bug = 2.0;
    // disable bug highlighting
    //bug = 0.0;        
}

If bug = 1.0, then the vertices are colored red, and if but = 2.0, then the vertices are colored green. I see all the vertices are colored red right now, which means the sum is greater than 1.5f.

I have confirmed that the sum of each set of weights is in fact <= 1.0f...so how in the world is this not the case in the shader?

Anyone see anything obvious I am missing?

EDIT: Looks like I found the problem - I wasn't calling glBindAttribLocation on any of my in variables - after adding:

glBindAttribLocation(programId_, 0, "in_Position");
    glBindAttribLocation(programId_, 1, "in_Texture");
    glBindAttribLocation(programId_, 2, "in_Color");
    glBindAttribLocation(programId_, 3, "in_BoneIds");
    glBindAttribLocation(programId_, 4, "in_BoneWeights");

to my shader loading code, it works fine. Odd thing is - I didn't have to do this for the first 3 (position, texture, and color) and it worked fine. But when I added the bone info, I suddenly need this...why?

like image 560
Jarrett Avatar asked Oct 21 '22 01:10

Jarrett


1 Answers

You need to use glVertexAttribIPointer (...) to give data to an ivec<N> vertex attribute. If you thought you were experiencing "corruption" before setting the attribute locations, just wait until you write code in your shader that actually needs the bone IDs... These values will be meaningless because you are using the wrong API call to associate your attribute data with a particular binding location.

The glVertexAttribPointer (...) variant has no trouble taking a GL_INT data type, but it is going to convert it to floating-point (and it may normalize fixed-point values in the process, hence the extra parameter). Thus, this variant is expected to supply the data to a vec<N> vertex attribute, not ivec<N>.

More concisely, use glVertexAttribIPointer for integer vertex attributes and glVertexAttribPointer for floating-point.

like image 143
Andon M. Coleman Avatar answered Nov 03 '22 21:11

Andon M. Coleman