Jump to content
  • Advertisement
Sign in to follow this  
ileonte

Problems doing skeletal animation (Milkshape models) using vertex shader

This topic is 2891 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to implement skeletal animation for my Milkshape model loader. I'm trying to do this by computing the transform matrix for each joint on the CPU and pass the data as an uniform array of mat4's to the vertex shader. The problem I'm facing is that my code seems to work on my laptop but not on my desktop. Both systems are running Gentoo Linux (x86_64). Both have nVIDIA video cards - the laptop has an 8600M and the desktop has a GTX 460. Both are using the same drivers (260.19.29).

As a test I've tried setting all joint matrices to the identity matrix just to see if my shader is working. The C++ code can be found here (mldr_ms3d.cpp) and here (mldr_ms3d.h) (the drawing code is located in the .cpp file at line 370 - the engineModelDraw() function). The shaders are as following:
VERTEX SHADER
#version 330 core

struct EglobalLight {
vec3 direction;
vec4 ambient;
vec4 specular;
vec4 diffuse;
};

uniform mat4 matWorld;
uniform mat3 matWorld3;
uniform mat4 matModel;
uniform mat3 matNormalMatrix;
uniform mat4 matProjection;
uniform EglobalLight globalIllum;
uniform mat4 aniKeyframes[128];
uniform int aniEnabled;
uniform vec3 camPosition;

in vec2 attrTC;
in vec3 attrPosition;
in vec3 attrNormal;
in int attrBoneId;

out vec2 fragTexCoords;
out vec3 fragNormal;
out vec3 halfVector;
out vec3 lightDir;

void main( void )
{
vec4 pos;
if ( aniEnabled != 0 ) {
if ( attrBoneId >= 0 ) {
pos = matWorld * matModel * aniKeyframes[attrBoneId] * vec4( attrPosition, 1.0 );
} else {
pos = matWorld * matModel * vec4( attrPosition, 1.0 );
}
} else {
pos = matWorld * matModel * vec4( attrPosition, 1.0 );
}
fragTexCoords = attrTC;
fragNormal = normalize( matNormalMatrix * attrNormal );
lightDir = normalize( matWorld3 * -globalIllum.direction );
halfVector = normalize( normalize( matWorld3 * camPosition ) + lightDir );
gl_Position = matProjection * pos;
}

FRAGMENT SHADER
#version 330 core

struct EglobalLight {
vec3 direction;
vec4 ambient;
vec4 specular;
vec4 diffuse;
};

struct Ematerial {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 emissive;
float shininess;
float transparency;
sampler2D texture;
};

uniform EglobalLight globalIllum;
uniform Ematerial material;
uniform int lightingEnabled;
uniform int texturingEnabled;

in vec2 fragTexCoords;
in vec3 fragNormal;
in vec3 halfVector;
in vec3 lightDir;

out vec4 fragColor;

void main( void )
{
vec4 texcolor;
if ( texturingEnabled != 0 ) {
if ( fragTexCoords.s < 0.0 || fragTexCoords.t < 0.0 ) {
texcolor = clamp( material.emissive * material.diffuse, 0.3, 1.0 );
} else {
texcolor = texture( material.texture, fragTexCoords );
}
} else {
texcolor = clamp( material.emissive * material.diffuse, 0.3, 1.0 );
}
vec4 color = vec4( 1.0, 1.0, 1.0, 1.0 );

if ( lightingEnabled != 0 ) {
float NdotL = dot( fragNormal, lightDir );
float f = ( NdotL != 0 ? 1 : 0 );

color = material.emissive;
color += material.ambient * globalIllum.ambient;
color += material.ambient * vec4( 0.2, 0.2, 0.2, 1.0 );
color += NdotL * material.diffuse * globalIllum.diffuse;
color += f * pow( dot( fragNormal, halfVector ), material.shininess ) * material.specular * globalIllum.specular;
}

color = clamp( color, 0.3, 1.0 );
fragColor = vec4( vec4( texcolor * color ).rgb, material.transparency );

gl_FragDepth = gl_FragCoord.w;
}


As you can see I can turn skeletal animation on/off by modifying the value passed via the 'aniEnabled' uniform in the vertex shader. On my laptop (the 8600M) everything works as expected: when I turn skeletal animation on nothing changes (because all joint matrices are the identity matrix). On the desktop (the 460 GTX) this is what happens:

SKELETAL ANIMATION DISABLED
ani_off.png


SKELETAL ANIMATION ENABLED
ani_on.png


Any pointers as to why the code behaves differently on the two machines are greatly appreciated.

Share this post


Link to post
Share on other sites
Advertisement
Have you tried any debugging on shader to figure out exactly what is the problem?

Is it the matrices that are bad? The index variable? The if statement?

I'd try replacing this line:
pos = matWorld * matModel * aniKeyframes[attrBoneId] * vec4( attrPosition, 1.0 );
with:

1) pos = matWorld * matModel * aniKeyframes[0] * vec4( attrPosition, 1.0 );
2) pos = matWorld * matModel * vec4( attrPosition, 1.0 );

and see if you can learn anything from that.

Share this post


Link to post
Share on other sites
Also:


glVertexAttribPointer( m->Aids.boneId, 1, GL_INT, GL_FALSE, sizeof( vbo_item_t ), ( const GLvoid * )32 );


Is wrong. If you want to upload an int type, you need to use glVertexAttrib I Pointer. Passing an integer type with glVertexAttribPointer will convert your integer to a float internally, and then strictly interpret those bytes as an integer in your shader, which is a bug nightmare.

So if you upload an int "1" (0x00000001), it converts this to a floating point representation of 1 (0x3F800000), and then strictly casts that to an int in the shader, which will end up being 1,065,353,216 in your shader. That happened to me before when I was doing skeletal and it took me a couple days to figure out.

Share this post


Link to post
Share on other sites
Everything was fine except me calling glVertexAttribPointer() instead of glVertexAttribIPointer() on the bone ID attribute. Thank you yet again karwosts, I've lost track of the virtual beers I owe you at this point :)

// EDIT: it was the fact that it worked fine on the 8600 that threw me off. I checked and double checked everything, would have taken me ages to see I was calling the wrong function :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!