Schritt 1: Berechnung der Gesamttransformationsmatrizen
Wie bereits im Artikel über Kinematik besprochen wurde (siehe hierzu Animation von 3D-Modellen Teil 03), müssen vor der Berechnung der Gesamttransformationsmatrizen zuerst die Rotations- und Translationsmatrizen aller Gelenke aufgestellt werden.
Die Rotationsmatrizen werden hierbei aus den Gelenkwinkeln berechnet, welche die Drehungen der Gelenke um die x-, y- und z-Achse beschreiben.
Beim Aufstellen der Translationsmatrizen beginnt man mit demjenigen Gelenk, dass in der Hierarchie an höchster Stelle steht. Für dieses Gelenk beschreibt die Translationsmatrix die Verschiebung relativ zum Modellkoordinatenursprung.
Hinweis:
Für das Gelenk, das in der Hierarchie an höchster Stelle steht, entspricht die Translationsmatrix gleichsam der Gesamttransformationsmatrix.
Im Anschluss daran werden für alle Bewegungsgruppen angefangen beim zweiten Gelenk die Translationsmatrizen berechnet, die die Verschiebungen relativ zu den vorangehenden Gelenken (diese Gelenke stehen in der Hierarchie eine Stufe höher) beschreiben.
Als Teil einer zweiten Bewegungsgruppe mit höherer Gruppenhierarchie beschreibt die Translationsmatrix des ersten Gelenks einer Bewegungsgruppe die Verschiebung relativ zum vorangehenden Gelenk in der zweiten Bewegungsgruppe (Beispiel: das erste Gelenk in der Bewegungsgruppe rechter Arm ist ein Wirbelgelenk und damit zugleich Teil der Bewegungsgruppe Wirbelsäule, die in der Gruppenhierarchie höher steht. Die Translationsmatrix dieses Wirbelgelenks beschreibt die Verschiebung relativ zum vorangehenden Gelenk in der Wirbelsäule).
Nachdem die Rotations- und Translationsmatrizen für alle Gelenke berechnet worden sind, können im zweiten Schritt die Gesamttransformationsmatrizen (TotalTransformationMatrix) aufgestellt werden, mit deren Hilfe sich die animierten Gelenkpositionen im Modellkoordinatensystem ermitteln lassen. Zusätzlich dazu wird für jedes Gelenk die Gesamtrotationsmatrix (TotalRotationMatrix) berechnet, die die Drehungen aller in der Hierarchie höher stehenden Gelenke sowie die Drehung des Gelenks selbst beschreibt. Die Berechnungen erfolgen für jede Bewegungsgruppe separat, angefangen beim jeweils zweiten Gelenk (vorwärts Kinematik).
void CSkeletalAnimatedBody::Set_AnimationPose(
CSkeletalAnimationPose* pAnimationPose,
D3DXVECTOR3* pWorldSpacePosition, D3DXMATRIXA16* pOrientationMatrix)
{
WorldSpacePosition = *pWorldSpacePosition;
long i, j;
D3DXMATRIXA16 tempMatrix1, tempMatrix2, tempMatrix3;
// Gelenk-Rotationsmatrizen berechnen:
for(i = 0; i < NumJoints; i++)
{
JointDesc[i].JointWorldSpacePosLastFrame =
JointDesc[i].ActualJointWorldSpacePos;
CalcRotXMatrix(&tempMatrix1, pAnimationPose->JointRotationAngleX[i]);
CalcRotYMatrix(&tempMatrix2, pAnimationPose->JointRotationAngleY[i]);
CalcRotZMatrix(&tempMatrix3, pAnimationPose->JointRotationAngleZ[i]);
JointDesc[i].JointRotationMatrix = tempMatrix1*tempMatrix2*tempMatrix3;
}
// Translations- und Rotationsmatrix für das Gelenk berechnen,
// das in der Hierarchie an höchster Stelle steht:
// Hinweis:
// Die einzelnen Bewegungsgruppen werden durch JointHierarchy-Instanzen
// repräsentiert!!
long tempLong = JointHierarchy[0].HierarchyElement[0];
D3DXVECTOR3 tempVektor3 = JointDesc[tempLong].NonAnimatedJointModelSpacePos;
JointDesc[tempLong].JointRotationMatrix *= *pOrientationMatrix;
CreateTranslationMatrix(
&JointDesc[tempLong].TranslationMatrixRelToPrevJoint, &tempVektor3);
JointDesc[tempLong].AnimatedJointModelSpacePos =
JointDesc[tempLong].NonAnimatedJointModelSpacePos;
JointDesc[tempLong].TotalTransformationMatrix =
JointDesc[tempLong].TranslationMatrixRelToPrevJoint;
JointDesc[tempLong].TotalRotationMatrix = g_identityMatrix;
// Translationsmatrizen für alle weiteren Gelenke berechnen, angefangen
// beim jeweils zweiten Gelenk einer Bewegungsgruppe:
for(j = 0; j < NumJointHierarchies; j++)
{
tempLong = JointHierarchy[j].NumHierarchyElements;
for(i = 1; i < tempLong; i++)
{
tempVektor3 = JointDesc[JointHierarchy[j].HierarchyElement[i]].
NonAnimatedJointModelSpacePos -
JointDesc[JointHierarchy[j].HierarchyElement[i-1]].
NonAnimatedJointModelSpacePos;
CreateTranslationMatrix(
&JointDesc[JointHierarchy[j].HierarchyElement[i]].
TranslationMatrixRelToPrevJoint, &tempVektor3);
}}
// Gesamttransformations- und Rotationsmatrizen berechnen:
long temp1Long, temp2Long;
for(j = 0; j < NumJointHierarchies; j++)
{
// Gesamttransformationsmatrix für das erste Gelenk einer
// Bewegungsgruppe:
tempMatrix1 = JointDesc[JointHierarchy[j].HierarchyElement[0]].
TotalTransformationMatrix;
// Gesamtrotationsmatrix für das erste Gelenk einer Bewegungsgruppe:
tempMatrix2 = JointDesc[JointHierarchy[j].HierarchyElement[0]].
TotalRotationMatrix;
tempLong = JointHierarchy[j].NumHierarchyElements;
for(i = 1; i < tempLong; i++)
{
temp1Long = JointHierarchy[j].HierarchyElement[i];
temp2Long = JointHierarchy[j].HierarchyElement[i-1];
// Gesamttransformationsmatrix für das i-te Gelenk einer
// Bewegungsgruppe:
tempMatrix1 = JointDesc[temp1Long].
TranslationMatrixRelToPrevJoint*
JointDesc[temp2Long].JointRotationMatrix*tempMatrix1;
// Gesamtrotationsmatrix für das i-te Gelenk einer Bewegungsgruppe:
tempMatrix2 = JointDesc[temp2Long].JointRotationMatrix*tempMatrix2;
JointDesc[temp1Long].TotalTransformationMatrix = tempMatrix1;
JointDesc[temp1Long].TotalRotationMatrix = tempMatrix2;
}
}
// Positionen (sowohl im Modell- als auch im Weltkoordinatensystem)
// der animierten Gelenke berechnen:
// Hinweis:
// die Weltkoordinatenpositionen können im Rahmen von Treffer- und
// Kollisionstests verwendet werden!!
for(i = 0; i < NumJoints; i++)
{
Multiply3DVectorWithMatrix(&JointDesc[i].AnimatedJointModelSpacePos,
&g_NullVector,
&JointDesc[i].TotalTransformationMatrix);
JointDesc[i].ActualJointWorldSpacePos =
JointDesc[i].AnimatedJointModelSpacePos + WorldSpacePosition;
// Zwischenspeichern der Gesamttransformations- und -rotationsmatrizen für
// die spätere Übergabe an den Vertex Shader des 3D-Modells:
JointTotalRotationMatrix[i] = JointDesc[i].TotalRotationMatrix;
JointTotalTransformationMatrix[i] = JointDesc[i].TotalTransformationMatrix;
}}
CSkeletalAnimationPose* pAnimationPose,
D3DXVECTOR3* pWorldSpacePosition, D3DXMATRIXA16* pOrientationMatrix)
{
WorldSpacePosition = *pWorldSpacePosition;
long i, j;
D3DXMATRIXA16 tempMatrix1, tempMatrix2, tempMatrix3;
// Gelenk-Rotationsmatrizen berechnen:
for(i = 0; i < NumJoints; i++)
{
JointDesc[i].JointWorldSpacePosLastFrame =
JointDesc[i].ActualJointWorldSpacePos;
CalcRotXMatrix(&tempMatrix1, pAnimationPose->JointRotationAngleX[i]);
CalcRotYMatrix(&tempMatrix2, pAnimationPose->JointRotationAngleY[i]);
CalcRotZMatrix(&tempMatrix3, pAnimationPose->JointRotationAngleZ[i]);
JointDesc[i].JointRotationMatrix = tempMatrix1*tempMatrix2*tempMatrix3;
}
// Translations- und Rotationsmatrix für das Gelenk berechnen,
// das in der Hierarchie an höchster Stelle steht:
// Hinweis:
// Die einzelnen Bewegungsgruppen werden durch JointHierarchy-Instanzen
// repräsentiert!!
long tempLong = JointHierarchy[0].HierarchyElement[0];
D3DXVECTOR3 tempVektor3 = JointDesc[tempLong].NonAnimatedJointModelSpacePos;
JointDesc[tempLong].JointRotationMatrix *= *pOrientationMatrix;
CreateTranslationMatrix(
&JointDesc[tempLong].TranslationMatrixRelToPrevJoint, &tempVektor3);
JointDesc[tempLong].AnimatedJointModelSpacePos =
JointDesc[tempLong].NonAnimatedJointModelSpacePos;
JointDesc[tempLong].TotalTransformationMatrix =
JointDesc[tempLong].TranslationMatrixRelToPrevJoint;
JointDesc[tempLong].TotalRotationMatrix = g_identityMatrix;
// Translationsmatrizen für alle weiteren Gelenke berechnen, angefangen
// beim jeweils zweiten Gelenk einer Bewegungsgruppe:
for(j = 0; j < NumJointHierarchies; j++)
{
tempLong = JointHierarchy[j].NumHierarchyElements;
for(i = 1; i < tempLong; i++)
{
tempVektor3 = JointDesc[JointHierarchy[j].HierarchyElement[i]].
NonAnimatedJointModelSpacePos -
JointDesc[JointHierarchy[j].HierarchyElement[i-1]].
NonAnimatedJointModelSpacePos;
CreateTranslationMatrix(
&JointDesc[JointHierarchy[j].HierarchyElement[i]].
TranslationMatrixRelToPrevJoint, &tempVektor3);
}}
// Gesamttransformations- und Rotationsmatrizen berechnen:
long temp1Long, temp2Long;
for(j = 0; j < NumJointHierarchies; j++)
{
// Gesamttransformationsmatrix für das erste Gelenk einer
// Bewegungsgruppe:
tempMatrix1 = JointDesc[JointHierarchy[j].HierarchyElement[0]].
TotalTransformationMatrix;
// Gesamtrotationsmatrix für das erste Gelenk einer Bewegungsgruppe:
tempMatrix2 = JointDesc[JointHierarchy[j].HierarchyElement[0]].
TotalRotationMatrix;
tempLong = JointHierarchy[j].NumHierarchyElements;
for(i = 1; i < tempLong; i++)
{
temp1Long = JointHierarchy[j].HierarchyElement[i];
temp2Long = JointHierarchy[j].HierarchyElement[i-1];
// Gesamttransformationsmatrix für das i-te Gelenk einer
// Bewegungsgruppe:
tempMatrix1 = JointDesc[temp1Long].
TranslationMatrixRelToPrevJoint*
JointDesc[temp2Long].JointRotationMatrix*tempMatrix1;
// Gesamtrotationsmatrix für das i-te Gelenk einer Bewegungsgruppe:
tempMatrix2 = JointDesc[temp2Long].JointRotationMatrix*tempMatrix2;
JointDesc[temp1Long].TotalTransformationMatrix = tempMatrix1;
JointDesc[temp1Long].TotalRotationMatrix = tempMatrix2;
}
}
// Positionen (sowohl im Modell- als auch im Weltkoordinatensystem)
// der animierten Gelenke berechnen:
// Hinweis:
// die Weltkoordinatenpositionen können im Rahmen von Treffer- und
// Kollisionstests verwendet werden!!
for(i = 0; i < NumJoints; i++)
{
Multiply3DVectorWithMatrix(&JointDesc[i].AnimatedJointModelSpacePos,
&g_NullVector,
&JointDesc[i].TotalTransformationMatrix);
JointDesc[i].ActualJointWorldSpacePos =
JointDesc[i].AnimatedJointModelSpacePos + WorldSpacePosition;
// Zwischenspeichern der Gesamttransformations- und -rotationsmatrizen für
// die spätere Übergabe an den Vertex Shader des 3D-Modells:
JointTotalRotationMatrix[i] = JointDesc[i].TotalRotationMatrix;
JointTotalTransformationMatrix[i] = JointDesc[i].TotalTransformationMatrix;
}}
Schritt 2: Vertex Skinning
Nachdem die Gelenk-Gesamttransformationsmatrizen der aktuellen Animationspose berechnet worden sind, kann nun auch das eigentliche 3D-Modell animiert werden. Die notwendigen Berechnungen im Rahmen des Vertex Skinnings werden hierbei ausschließlich im Vertex Shader durchgeführt:
// Transformationsmatrix:
uniform mat4 matWorldViewProjection;
uniform mat4 JointTotalTransformationMatrixArray[45];
void main()
{
// Index der zu verwendenden JointTotalTransformationMatrix
// für die Transformation der Vertx-Position:
int Index1 = int(gs_MultiTexCoord0.p);
// Index der zu verwendenden JointTotalRotationMatrix
// für die Transformation der Vertex-Normale:
int Index2 = int(gs_MultiTexCoord0.q);
// Animierte Vertex-Position im Modellkoordinatensystem:
vec4 AnimatedPos = JointTotalTransformationMatrixArray[Index1] *
gs_Vertex;
gl_Position = matWorldViewProjection * AnimatedPos;
gs_TexCoord[0] = gs_MultiTexCoord0;
mat3 JointTotalRotationMatrix;
// Extrahieren der JointTotalRotationMatrix aus der
// JointTotalTransformationMatrix:
JointTotalRotationMatrix[0] =
vec3(JointTotalTransformationMatrixArray[Index2][0][0],
JointTotalTransformationMatrixArray[Index2][0][1],
JointTotalTransformationMatrixArray[Index2][0][2]);
JointTotalRotationMatrix[1] =
vec3(JointTotalTransformationMatrixArray[Index2][1][0],
JointTotalTransformationMatrixArray[Index2][1][1],
JointTotalTransformationMatrixArray[Index2][1][2]);
JointTotalRotationMatrix[2] =
vec3(JointTotalTransformationMatrixArray[Index2][2][0],
JointTotalTransformationMatrixArray[Index2][2][1],
JointTotalTransformationMatrixArray[Index2][2][2]);
// Normal:
gs_TexCoord[1].xyz = JointTotalRotationMatrix * gs_Normal;
// Perpendicular1:
gs_TexCoord[2].xyz = JointTotalRotationMatrix * gs_MultiTexCoord1.xyz;
// Perpendicular2:
gs_TexCoord[3].xyz = JointTotalRotationMatrix * gs_MultiTexCoord2.xyz;
}
uniform mat4 matWorldViewProjection;
uniform mat4 JointTotalTransformationMatrixArray[45];
void main()
{
// Index der zu verwendenden JointTotalTransformationMatrix
// für die Transformation der Vertx-Position:
int Index1 = int(gs_MultiTexCoord0.p);
// Index der zu verwendenden JointTotalRotationMatrix
// für die Transformation der Vertex-Normale:
int Index2 = int(gs_MultiTexCoord0.q);
// Animierte Vertex-Position im Modellkoordinatensystem:
vec4 AnimatedPos = JointTotalTransformationMatrixArray[Index1] *
gs_Vertex;
gl_Position = matWorldViewProjection * AnimatedPos;
gs_TexCoord[0] = gs_MultiTexCoord0;
mat3 JointTotalRotationMatrix;
// Extrahieren der JointTotalRotationMatrix aus der
// JointTotalTransformationMatrix:
JointTotalRotationMatrix[0] =
vec3(JointTotalTransformationMatrixArray[Index2][0][0],
JointTotalTransformationMatrixArray[Index2][0][1],
JointTotalTransformationMatrixArray[Index2][0][2]);
JointTotalRotationMatrix[1] =
vec3(JointTotalTransformationMatrixArray[Index2][1][0],
JointTotalTransformationMatrixArray[Index2][1][1],
JointTotalTransformationMatrixArray[Index2][1][2]);
JointTotalRotationMatrix[2] =
vec3(JointTotalTransformationMatrixArray[Index2][2][0],
JointTotalTransformationMatrixArray[Index2][2][1],
JointTotalTransformationMatrixArray[Index2][2][2]);
// Normal:
gs_TexCoord[1].xyz = JointTotalRotationMatrix * gs_Normal;
// Perpendicular1:
gs_TexCoord[2].xyz = JointTotalRotationMatrix * gs_MultiTexCoord1.xyz;
// Perpendicular2:
gs_TexCoord[3].xyz = JointTotalRotationMatrix * gs_MultiTexCoord2.xyz;
}