Keyframe Animation: Difference between revisions
m Categorized |
No edit summary |
||
(4 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
For simplicity, let's assume our vertex format has vertex, normals and texcoords (aka VNT). | For simplicity, let's assume our vertex format has vertex, normals and texcoords (aka VNT). | ||
<source lang="c"> | |||
struct MyVertex_VNT | |||
{ | |||
float x, y, z; | float x, y, z; | ||
float nx, ny, nz; //Normals | float nx, ny, nz; // Normals | ||
float s0, t0; //Texcoords | float s0, t0; // Texcoords | ||
}; | |||
</source> | |||
What is the problem with that? The problem is that you don't have access to the "next keyframe". The solution is to have a special vertex format | What is the problem with that? The problem is that you don't have access to the "next keyframe". The solution is to have a special vertex format | ||
<source lang="c"> | |||
float x, y, z; | struct MyVertex_VNTVN | ||
float nx, ny, nz; | { | ||
float s0, t0; | float x, y, z; // Vertex for this keyframe | ||
float x1, y1, z1; | float nx, ny, nz; // Normals for this keyframe | ||
float nx1, ny1, nz1; | float s0, t0; // Texcoords | ||
float x1, y1, z1; // Vertex for the next keyframe | |||
float nx1, ny1, nz1; // Normals for the next keyframe | |||
}; | |||
</source> | |||
So when you allocate your VBO, you would have to generate that vertex format by copying some vertices and normals around. You can store all the keyframes in a single VBO if you want by making a large VBO. | So when you allocate your VBO, you would have to generate that vertex format by copying some vertices and normals around. You can store all the keyframes in a single VBO if you want by making a large VBO. | ||
Line 28: | Line 33: | ||
And finally, the vertex shader. This is in GLSL and it is GL 2.0 compatible but it is easily convertable to GL 3.0. The fragment shader is not important so it isn't listed here. | And finally, the vertex shader. This is in GLSL and it is GL 2.0 compatible but it is easily convertable to GL 3.0. The fragment shader is not important so it isn't listed here. | ||
<source lang="glsl"> | |||
// Vertex Shader | |||
// ATTRIBUTES | |||
attribute vec3 InVertex; | |||
attribute vec3 InNormal; | |||
attribute vec2 InMultiTexCoord0; | |||
attribute vec3 InVertex1; // The Next keyframe vertex | |||
attribute vec3 InNormal1; // The Next keyframe normal | |||
// UNIFORM | |||
uniform vec4 LightPosition0; | |||
uniform mat4 ModelviewMatrix; | |||
uniform float TweenFactor; | |||
uniform mat4 ProjectionModelviewMatrix; | |||
// -------------------- | |||
// VARYING | |||
varying vec2 TexCoord0; | |||
varying vec4 LightVector0; // xyz is lightvector and w is light distance from vertex | |||
varying vec3 HalfVector0; | |||
varying vec3 EyeNormal; | |||
// -------------------- | |||
void main() | |||
{ | |||
vec3 eyeVertex; | vec3 eyeVertex; | ||
vec3 lightVector, eyeVector; | vec3 lightVector, eyeVector; | ||
Line 54: | Line 60: | ||
vec4 newVertex; | vec4 newVertex; | ||
vec3 newNormal; | vec3 newNormal; | ||
//-------------------- | // -------------------- | ||
newVertex.xyz = mix(InVertex, InVertex1, TweenFactor); | newVertex.xyz = mix(InVertex, InVertex1, TweenFactor); | ||
newVertex.w = 1.0; | newVertex.w = 1.0; // Make sure w is exactly 1.0 | ||
//-------------------- | // -------------------- | ||
gl_Position = ProjectionModelviewMatrix * newVertex; | gl_Position = ProjectionModelviewMatrix * newVertex; | ||
TexCoord0 = InMultiTexCoord0; | TexCoord0 = InMultiTexCoord0; | ||
//-------------------- | // -------------------- | ||
eyeVertex = vec3(ModelviewMatrix * newVertex); | eyeVertex = vec3(ModelviewMatrix * newVertex); | ||
eyeVector = normalize(-eyeVertex); | eyeVector = normalize(-eyeVertex); | ||
Line 67: | Line 73: | ||
mysqrtdistance = sqrt(distance(LightPosition0.xyz, eyeVertex)); | mysqrtdistance = sqrt(distance(LightPosition0.xyz, eyeVertex)); | ||
LightVector0.w = mysqrtdistance * sqrt(mysqrtdistance); | LightVector0.w = mysqrtdistance * sqrt(mysqrtdistance); | ||
HalfVector0 = lightVector + eyeVector; | HalfVector0 = lightVector + eyeVector; // No need to normalize the sum | ||
//-------------------- | // -------------------- | ||
newNormal = mix(InNormal, InNormal1, TweenFactor); | newNormal = mix(InNormal, InNormal1, TweenFactor); | ||
EyeNormal = vec3(ModelviewMatrix * vec4(newNormal, 0.0)); | EyeNormal = vec3(ModelviewMatrix * vec4(newNormal, 0.0)); | ||
} | |||
</source> | |||
So we have the line that says newVertex.xyz = mix(InVertex, InVertex1, TweenFactor) which blends the current keyframe with the next keyframe. mix() does a lerp operation. | So we have the line that says newVertex.xyz = mix(InVertex, InVertex1, TweenFactor) which blends the current keyframe with the next keyframe. mix() does a lerp operation. |
Latest revision as of 11:16, 2 January 2018
Keyframe Animation is easier to do than Skeletal Animation. This technique is used when skeletal animation is not appropriate for example, when you want to deform a mesh in some radical way. The downside is that keyframe animation can consume a lot of VBO space.
The concept is the following. You have snapshots of a mesh with different poses. These snapshots are called the keyframes. As time passes, you compute the "current frame" using the old vertex position and newer vertex position. You would also have to compute the normals. Texcoords don't change. If you have tangent vectors, you have to compute these as well. The indices never change between keyframes. The number of triangles never change between keyframes. The number of vertices never change between keyframes.
For simplicity, let's assume our vertex format has vertex, normals and texcoords (aka VNT).
struct MyVertex_VNT
{
float x, y, z;
float nx, ny, nz; // Normals
float s0, t0; // Texcoords
};
What is the problem with that? The problem is that you don't have access to the "next keyframe". The solution is to have a special vertex format
struct MyVertex_VNTVN
{
float x, y, z; // Vertex for this keyframe
float nx, ny, nz; // Normals for this keyframe
float s0, t0; // Texcoords
float x1, y1, z1; // Vertex for the next keyframe
float nx1, ny1, nz1; // Normals for the next keyframe
};
So when you allocate your VBO, you would have to generate that vertex format by copying some vertices and normals around. You can store all the keyframes in a single VBO if you want by making a large VBO.
When you run your program, based on the amount of time passed, you have to decide which keyframe will be shown. You also need a tween factor. The tween factor is a value that goes from 0.0 to 1.0.
And finally, the vertex shader. This is in GLSL and it is GL 2.0 compatible but it is easily convertable to GL 3.0. The fragment shader is not important so it isn't listed here.
// Vertex Shader
// ATTRIBUTES
attribute vec3 InVertex;
attribute vec3 InNormal;
attribute vec2 InMultiTexCoord0;
attribute vec3 InVertex1; // The Next keyframe vertex
attribute vec3 InNormal1; // The Next keyframe normal
// UNIFORM
uniform vec4 LightPosition0;
uniform mat4 ModelviewMatrix;
uniform float TweenFactor;
uniform mat4 ProjectionModelviewMatrix;
// --------------------
// VARYING
varying vec2 TexCoord0;
varying vec4 LightVector0; // xyz is lightvector and w is light distance from vertex
varying vec3 HalfVector0;
varying vec3 EyeNormal;
// --------------------
void main()
{
vec3 eyeVertex;
vec3 lightVector, eyeVector;
float mysqrtdistance;
vec4 newVertex;
vec3 newNormal;
// --------------------
newVertex.xyz = mix(InVertex, InVertex1, TweenFactor);
newVertex.w = 1.0; // Make sure w is exactly 1.0
// --------------------
gl_Position = ProjectionModelviewMatrix * newVertex;
TexCoord0 = InMultiTexCoord0;
// --------------------
eyeVertex = vec3(ModelviewMatrix * newVertex);
eyeVector = normalize(-eyeVertex);
lightVector = normalize(LightPosition0.xyz - eyeVertex);
LightVector0.xyz = lightVector;
mysqrtdistance = sqrt(distance(LightPosition0.xyz, eyeVertex));
LightVector0.w = mysqrtdistance * sqrt(mysqrtdistance);
HalfVector0 = lightVector + eyeVector; // No need to normalize the sum
// --------------------
newNormal = mix(InNormal, InNormal1, TweenFactor);
EyeNormal = vec3(ModelviewMatrix * vec4(newNormal, 0.0));
}
So we have the line that says newVertex.xyz = mix(InVertex, InVertex1, TweenFactor) which blends the current keyframe with the next keyframe. mix() does a lerp operation.
newVertex.w = 1.0; because we must always set a vertex w to 1.0
And the other line of interest is this one : newNormal = mix(InNormal, InNormal1, TweenFactor) which compute the normal. mix() does a lerp operation.
The code above doesn't show how to load the keyframe from a file. That is left up to you. Chose whatever file format you want. Create your own if you have to.
The code above also doesn't show the preparation of the VBOs. This is easy enough.