Difference between revisions of "Skeletal Animation"

From OpenGL Wiki
Jump to: navigation, search
m
Line 16: Line 16:
 
Let's assume our entire model will have 10 bones. Let's assume each vertex will have 2 bones.<br>
 
Let's assume our entire model will have 10 bones. Let's assume each vertex will have 2 bones.<br>
 
So your vertex structure would look something like this :<br>
 
So your vertex structure would look something like this :<br>
  struct MyVertex
+
<source lang="c">
  {
+
struct MyVertex
     float x, y, z;      //Vertex
+
{
     float nx, ny, nz;  //Normal
+
     float x, y, z;      // Vertex
     float s0, t0;      //Texcoord
+
     float nx, ny, nz;  // Normal
     float index0, index1;    //Index into the bone/offset matrix array (2 bones)
+
     float s0, t0;      // Texcoord
     float weight0, weight1;  //The blend factor for each bone/offset matrix (2 bones)
+
     float index0, index1;    // Index into the bone/offset matrix array (2 bones)
  };
+
     float weight0, weight1;  // The blend factor for each bone/offset matrix (2 bones)
 +
};
 +
</source>
  
 
You would be boning your vertex and normals. The texcoords just pass through.<br>
 
You would be boning your vertex and normals. The texcoords just pass through.<br>
Line 35: Line 37:
 
Notice that in the shader example unlike the examples on the other Wiki pages, we aren't using any built in features of GL. We aren't using gl_Vertex, gl_MultiTexCoord0, ftransform(), gl_ProjectionModelViewMatrix, gl_Normal, etc. We are using our own attribute names and our own varying and our own uniforms.
 
Notice that in the shader example unlike the examples on the other Wiki pages, we aren't using any built in features of GL. We aren't using gl_Vertex, gl_MultiTexCoord0, ftransform(), gl_ProjectionModelViewMatrix, gl_Normal, etc. We are using our own attribute names and our own varying and our own uniforms.
  
  //[Vertex Shader]
+
<source lang="glsl">
  attribute vec4 Vertex;
+
// Vertex Shader
  attribute vec3 Normal;
+
attribute vec4 Vertex;
  attribute vec2 TexCoord;
+
attribute vec3 Normal;
  attribute vec2 Index;
+
attribute vec2 TexCoord;
  attribute vec2 Weight;
+
attribute vec2 Index;
  uniform mat4 ModelviewMatrix;
+
attribute vec2 Weight;
  uniform mat4 ProjectionModelviewMatrix;
+
uniform mat4 ModelviewMatrix;
  uniform mat4 Bone[10]; //Array of bones that you compute (animate) on the CPU and you upload to the shader
+
uniform mat4 ProjectionModelviewMatrix;
  //-----------------------
+
uniform mat4 Bone[10]; // Array of bones that you compute (animate) on the CPU and you upload to the shader
  varying vec2 TexCoord0;
+
// --------------------
  varying vec3 EyeNormal;
+
varying vec2 TexCoord0;
  //-----------------------
+
varying vec3 EyeNormal;
  void main()
+
// --------------------
  {
+
void main()
 +
{
 
     vec4 newVertex;
 
     vec4 newVertex;
 
     vec4 newNormal;
 
     vec4 newNormal;
 
     int index;
 
     int index;
     //-----------------------
+
     // --------------------
     index=int(Index.x);   //Cast to int
+
     index=int(Index.x); // Cast to int
 
     newVertex = (Bone[index] * Vertex) * Weight.x;
 
     newVertex = (Bone[index] * Vertex) * Weight.x;
 
     newNormal = (Bone[index] * vec4(Normal, 0.0)) * Weight.x;
 
     newNormal = (Bone[index] * vec4(Normal, 0.0)) * Weight.x;
Line 63: Line 66:
 
     gl_Position = ProjectionModelviewMatrix * newVertex;
 
     gl_Position = ProjectionModelviewMatrix * newVertex;
 
     TexCoord0 = TexCoord;
 
     TexCoord0 = TexCoord;
  }
+
}
 +
</source>
  
 
Now that you have studied the code above, what's the problem?<br>
 
Now that you have studied the code above, what's the problem?<br>
    gl_Position = ProjectionModelviewMatrix * newVertex;
+
<source lang="glsl">
 +
gl_Position = ProjectionModelviewMatrix * newVertex;
 +
</source>
  
 
There is a risk that newVertex.w might not be exactly 1.0. Just to be safe
 
There is a risk that newVertex.w might not be exactly 1.0. Just to be safe
    gl_Position = ProjectionModelviewMatrix * vec4(newVertex.xyz, 1.0);
+
<source lang="glsl">
 +
gl_Position = ProjectionModelviewMatrix * vec4(newVertex.xyz, 1.0);
 +
</source>
  
 
[[Category:Rendering Techniques]]
 
[[Category:Rendering Techniques]]
 
[[Category:Algorithm]]
 
[[Category:Algorithm]]

Revision as of 11:16, 2 January 2018

This document will talk about doing skeletal based animation but it will be brief since the subject is complex and there are also other sources (websites and books and demos at http://developer.nvidia.com in the SDK, the tutorial here, and individual downloads, http://content.gpwiki.org/index.php?title=OpenGL:Tutorials:Basic_Bones_System).
You would need to model the object or human or animal in the software of your choice and export it in the file format of your choice.

Here is a description of one way to do it :
1. Your object will be in a default pose.
2. All the vertices are in their respective places.
3. You have an array of bones.
3. You have an array of offsets.
4. What's a bone? It is a rotation matrix. You only need a 3x3 matrix.
5. What's an offset? It is a XYZ value you use to translate your vertex.
6. A bone and its offset together make a 4x4 matrix.
7. Each vertex can have a certain amount of bones that influence it. 0, 1, 2, ...you name it!
8. For each bone that influences your vertex, you need to decide how much it influences. This is called a weight or blend factor. 1 weight per bone/offset matrix
9. In order to animate your object, you need to change the bones and offset matrix. Normally, you would not change the weights.

Let's assume our entire model will have 10 bones. Let's assume each vertex will have 2 bones.
So your vertex structure would look something like this :

struct MyVertex
{
    float x, y, z;      // Vertex
    float nx, ny, nz;   // Normal
    float s0, t0;       // Texcoord
    float index0, index1;     // Index into the bone/offset matrix array (2 bones)
    float weight0, weight1;   // The blend factor for each bone/offset matrix (2 bones)
};

You would be boning your vertex and normals. The texcoords just pass through.
If you have tangent vectors, you would bone them as well.
Warning : notice that each bone matrix will consume a lot of register space on the GPU. Each matrix takes 4 registers.
Since our example will have 10 bones (mat4), that makes 40 registers (vec4), which isn't really a problem for any GPU but typically, your model may have way more bones, perhaps 200 bones.
Instead of uploading a mat4 for each bone, upload a pair of quaternion and offset. A quat uses a vec4 and offset uses a vec4.
As a result, you'll cut the register need of your shader in half.
Of course, your would have to convert them to a mat4 in your shader which will cost you a few clock cycles.
Here, we'll use a mat4 matrix.
Notice that in the shader example unlike the examples on the other Wiki pages, we aren't using any built in features of GL. We aren't using gl_Vertex, gl_MultiTexCoord0, ftransform(), gl_ProjectionModelViewMatrix, gl_Normal, etc. We are using our own attribute names and our own varying and our own uniforms.

// Vertex Shader
attribute vec4 Vertex;
attribute vec3 Normal;
attribute vec2 TexCoord;
attribute vec2 Index;
attribute vec2 Weight;
uniform mat4 ModelviewMatrix;
uniform mat4 ProjectionModelviewMatrix;
uniform mat4 Bone[10]; // Array of bones that you compute (animate) on the CPU and you upload to the shader
// --------------------
varying vec2 TexCoord0;
varying vec3 EyeNormal;
// --------------------
void main()
{
    vec4 newVertex;
    vec4 newNormal;
    int index;
    // --------------------
    index=int(Index.x); // Cast to int
    newVertex = (Bone[index] * Vertex) * Weight.x;
    newNormal = (Bone[index] * vec4(Normal, 0.0)) * Weight.x;
    index=int(Index.y);    //Cast to int
    newVertex = (Bone[index] * Vertex) * Weight.y + newVertex;
    newNormal = (Bone[index] * vec4(Normal, 0.0)) * Weight.y + newNormal;
    EyeNormal = vec3(ModelviewMatrix * newNormal);
    gl_Position = ProjectionModelviewMatrix * newVertex;
    TexCoord0 = TexCoord;
}

Now that you have studied the code above, what's the problem?

gl_Position = ProjectionModelviewMatrix * newVertex;

There is a risk that newVertex.w might not be exactly 1.0. Just to be safe

gl_Position = ProjectionModelviewMatrix * vec4(newVertex.xyz, 1.0);