Boneanimation per Vertexshader: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Kompression der Matrizen)
K (Offline-Hinweis durch Hinweis auf Tutorial Charakteranimation ersetzt)
 
(27 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
{{Offline}}
+
{{Hinweis|Dieses Tutorial ist unvollständig. Ein ausführlicheres Tutorial zum Thema Bone-Animation (mit Bildern und Vertexshader-Code) findest du hier:<br>
 +
[[Tutorial Charakteranimation]]}}
 
==Vorwort==
 
==Vorwort==
  
Dieser Artikel richtet sich an alle, die schon weit Fortgeschritten sind. Ein sicherer Umgang mit Shadern und Vertexbufferobjekten ist hier Vorrausetzung. Bei der Boneanimation per Vertexshader geht es darum, möglichst viel Arbeit auf die Grafikkarte auszulagern.
+
Dieser Artikel richtet sich an alle, die schon weit Fortgeschritten sind. Ein sicherer Umgang mit Shadern und Vertexbufferobjekten ist hier Vorrausetzung. Auch Kenntnisse über Quaternionen oder Matrizen sind vorausgesetzt.
Wer schon immer mal einen hochdetailierten Dinosaurier oder Octobus sein Unwesen treiben lassen will ist hier genau richtig.
+
 
 +
Bei der Boneanimation per Vertexshader geht es darum, möglichst viel Arbeit auf die Grafikkarte auszulagern. Jedoch sollten variablen die über das ganze Model gleich bleiben, auf der CPU berechnet werden.
 +
 
 +
Wer schon immer mal einen hochdetailierten Dinosaurier oder Octobus sein Unwesen treiben lassen wollte, ist hier genau richtig.
  
 
==Grundlagen==
 
==Grundlagen==
Zeile 9: Zeile 13:
 
===Was sind Bones===
 
===Was sind Bones===
  
Bones entsprechen nicht nur Knochen. Mathematisch gesehen repräsenteriert jeder Bone eine Transformationsmatrix, der einen gewichteten Einfluss auf verschiedene Vertices nehmen kann. Da Bones sehr häufig in einem Animationsskelet zusammengefast sind, werden sie durch Gelenke verbunden, die Drehpunkten der Matrizen entsprechen.  
+
Bones entsprechen nicht nur Knochen. Mathematisch gesehen repräsenteriert jeder Bone eine Transformationsmatrix, der einen gewichteten Einfluss auf verschiedene Vertices nehmen kann. Da Bones sehr häufig in einem Animationsskelet zusammengefast sind, werden sie durch Gelenke (Joints) verbunden, die Drehpunkten der Matrizen entsprechen.  
Sehr häufig werden Bones als Stab oder ähnliches dargestellt, sie können jedoch auch komplexer Formen haben (z.B. das Becken einen Menschen). Bones die durch die durch ein festes unbewegliches Gelenk verbunden sind sollten umbedingt vermieden werden, da sie nur Resurcen kosten ohne einen Nutzen zu bringen.
+
Sehr häufig werden Bones als Stab oder ähnliches dargestellt, sie können jedoch auch komplexer Formen haben (z.B. das Becken einen Menschen). Bones die durch ein festes unbewegliches Gelenk verbunden sind sollten umbedingt vermieden werden, da sie nur Resurcen kosten ohne einen Nutzen zu bringen.
 
Grundsätzlich sollte alles was sich unabhängig voneinander bewegen können muss durch einen eigenen Bone dargestellt werden. Neben den nachgebildeten Kochen, gehören auch andere bewegliche Modelteile dazu wie Mimik und Augen.
 
Grundsätzlich sollte alles was sich unabhängig voneinander bewegen können muss durch einen eigenen Bone dargestellt werden. Neben den nachgebildeten Kochen, gehören auch andere bewegliche Modelteile dazu wie Mimik und Augen.
  
Zeile 19: Zeile 23:
 
===Limits===
 
===Limits===
  
Aktuelle Grafikkarten erlauben 256 bis 1024 Uniformvariablend des Typs Float4. Für einen Bone werden zwei oder drei Float4 benötigt. Damit lassen sich etwa 80 bis 500 Bones gleichzeitig verwenden. Wird dies schon beim Entwurf des Modells beachtet, stellen diese Werte kaum ein Limit dar. Bei einem symetrischem Modell lässt sich dieser Wert durch Spiegelung fast verdoppeln. Wenn die nicht ausreicht macht es Sinn Objekte mit vielen Bones wie Hände oder Gesichter getrennt zu Rendern.
+
Aktuelle Grafikkarten erlauben 256 bis 1024 Uniformvariablend des Typs Float4. Für einen Bone werden zwei bis vier Float4 vektoren benötigt. Damit lassen sich etwa 80 bis 500 Bones gleichzeitig verwenden. Wird dies schon beim Entwurf des Modells beachtet, stellen diese Werte kaum ein Limit dar. Bei einem symetrischem Modell lässt sich dieser Wert durch Spiegelung fast verdoppeln. Wenn das nicht ausreicht macht es Sinn bei Objekte mit vielen Bones Teile wie Hände oder Gesichter getrennt zu Rendern.
Vorausgesetzt werden sollte eine Grafikkarte, die wenigstends das Shadermodel 2.0 unterstützt. Besser ist jedoch eine Grafikkarte, die Shadermodel3.0 unterstützt, damit der Code durch dynamisches Branching teilweise übersprungen werden kann.
+
Vorausgesetzt werden sollte eine Grafikkarte, die wenigstends das Shadermodel 2.0 unterstützt.
 +
 
  
==Technik==
+
==Modeldaten==
===Modeldaten===
 
  
 
Das Vertexbufferobjekt muss zu den sonst verwendeten Daten wie Vertices, Normalen, Texturkoordinaten und gegebenfalls Tangent und Bitangent noch zusätzliche Daten über die Abhängigkeit zu den Bones gespeichert werden.  
 
Das Vertexbufferobjekt muss zu den sonst verwendeten Daten wie Vertices, Normalen, Texturkoordinaten und gegebenfalls Tangent und Bitangent noch zusätzliche Daten über die Abhängigkeit zu den Bones gespeichert werden.  
Zeile 29: Zeile 33:
 
Im einfachstem Fall kann jedem Vertex nur ein Bone zugewiesen werden. Hier genügt ein einzelner Integer, der als Index dient.
 
Im einfachstem Fall kann jedem Vertex nur ein Bone zugewiesen werden. Hier genügt ein einzelner Integer, der als Index dient.
  
Wenn zwischen meheren Bones interpoliert werden soll, ist als erstes ein Wert nötig nötig, der angibt wie viele Bones einen Einfluss auf den Vertex haben. Dann muss für jeden Bone die Gewichtung als Float und der Index als Int gespeichert werden. In den meisten Fällen sollte der Einfluss von vier Bones ausreichen. Sollten die Atributevariablen knapp werden ist es sinvoll die Gewichtung mit 0.999 zu Multiplizieren und die Indexnummer dazuzuaddieren. Solang dies nicht nötig ist koste es jedoch nur unötig Performance beim trennen.
+
Wenn zwischen meheren Bones interpoliert werden soll, ist als erstes ein Wert nötig, der angibt wie viele Bones einen Einfluss auf den Vertex haben. Dann muss für jeden Bone die Gewichtung als Float und der Index als Int gespeichert werden. In den meisten Fällen sollte der Einfluss von vier Bones ausreichen.
 +
Um die größe der Geometriedaten (Attribute) klein zu halten macht es Sinn, den Index mit der Gewichtung zu addieren. Da die Summe aller gewichungen 1.0 ist, ist das trennen im shader kein problem. Lediglich der Sonderfall der Gewichtung von 1.0, muss auf zwei Gewichtungen zu 0.5 aufgeteilt werden.
 +
 
 +
In den Meisten Fällen beinflussen nur zwei Bones einen Vertex. Ein Knie sieht etwas seltsam aus wenn am Gelenk linear interpoliert wird. Hier kann ein Quaternion basierendes Animationsystem zu mehr Qualität helfen.
 +
 
 +
==Vertexshader==
 +
 
 +
Hier werden die beiden Varianten von Vertexshadern beschrieben.
 +
 
 +
===Matrix basierende Boneanimation===
 +
 
 +
Zum Gewichten werden pro Matrix 4 Vektor MADDs (Multiplikation und Addition) benötigt.
 +
 
 +
<source lang="glsl">
 +
attribute vec4 Bones;
 +
attribute vec3 Tangent;
 +
uniform mat4 Pose[32];
 +
varying vec3 T,B,N;
 +
 +
void main(void){
 +
mat4 mat = 0.0;
 +
 +
for ( int i = 0; i < 4; i++){
 +
mat += Pose[int(Bones[i])] * fract(Bones[i]);
 +
}
 +
 +
gl_Position = gl_ModelViewProjectionMatrix * (mat *gl_Vertex);
 +
 
 +
mat3 m3 = mat3(mat[0].xyz, mat[0].xyz, mat[0].xyz); // "mat3(mat)"
 +
 +
N = gl_NormalMatrix * (m3 * gl_Normal);
 +
//T = gl_NormalMatrix * (m3 * Tangent);
 +
//B = cross (T,N);
 +
gl_TexCoord[0] = gl_MultiTexCoord0;
 +
}
 +
</source>
  
In den Meisten Fällen beinflussen nur zwei Bones einen Vertex. Ein Knie sieht etwas seltsam aus wenn am Gelenk linear interpoliert wird. Hier lohnt sich der Aufwand die Gelenkposition zu speichern und den Abstand nach der Interpolation zu korrigieren.
 
  
===Gewichten der Bones===
+
Als Optimierung kann es sinnvoll sein die Modelviewmatrix mit den Bonematrizen zu multiplizieren, da so der Vertexshader entlastet werden kann:
 +
<source lang="glsl">
 +
gl_Position = gl_ProjectionMatrix * (mat *gl_Vertex);
  
Gerade im Vertexshader muss man genau überlegen, wo man Multiplikationen oder höhere Operationen einsparen kann prinzipell gibt es zwei Möglichkeiten. Die erste ist, dass die Matrizen anhand Ihrer Gewichtungen zu einer durchschnittlichen Matrix zusammenzurechnet werden. Pro Matrix werden 12 Multiplikationen und 12 Additionen benötigt, von denen je 3 aus Symetriegründen Wegrationalisiert werden können.
+
N = m3 * gl_Normal;
Bei der zweiten Variante werden erst die Vektoren mit den Matrizen multipliziert und anschließend gewichtet. Jedoch werden dann pro Matrix * Vektor Multiplikation 12 Multiplikationen gebraucht. Da aber neben dem Vertex auch noch der Normal und gegebenfals Tangentvektoren Multipliziert werden müssen, ist der Aufwand der ersten Variante deutlich geringer.
+
//T = m3 * Tangent;
 +
</source>
  
 
===Quaternionen im Vertexshader===
 
===Quaternionen im Vertexshader===
  
Der Sinn von Quaternionen im Vertexshader ist sehr fraglich. Sie wären möglich, jedoch langsamer, lediglicht die Multiplikation von Quaternionen mit einem zweitem Quartenion ist deulich schneller als eine Matrix * Matrixmultiplikation. Gerade diese Operationen werden sollten jedoch vor dem Vertexshader durchgeführt werden, da sie die Form des ganzen Skelletes beeinflussen und sich über das Frame nicht ändern.
+
Lange gab es die diskusion ob Quaternion in shadern überhaupt Sinn machen, bei der Boneanimation im Vertexshader, hat sich herausgestellt, das die Anzahl der benötigten Instruktionen ähnlich ist wie bei der Matrix basierenden Variante. Der entscheidene Vorteil ist das sie  für organische strukturen qualitativ besser ist und das nur zwei Uniform Float4 Vektoren pro Joint/Bone verbraucht werden.
Eine gepackte Matrix kommt mit 8 Floats aus. Für Quarternionen wäre mit zusätzlichem Translationsteil 7 Floats nötig. Einen wirklichenSinn machen Quarternionen im Vertexshader also definitiv nicht
+
Etwas problematisch ist, dass die Vertexpositionen nicht mehr absolut zum Modelnullpunkt, sondern relativ zu dem Joint angegeben werden muss um das eine spherische Interpolation durchgeführt werden soll. Die W-komponente wird dabei durch die Indexnummer des Joints ersetzt.  
  
===Kompression der Matrizen===
+
<source lang="glsl">
 +
attribute vec4 Bones;
 +
attribute vec3 Tangent;
 +
 +
uniform vec3 Joints[32];
 +
uniform vec4 Quaternions[32];
 +
varying vec3 T,B,N;
 +
 +
vec3 qrot( vec4 q, vec3 v ){
 +
return v + 2.0*cross(q.xyz, cross(q.xyz ,v) + q.w*v);
 +
}
 +
 +
void main(void){
 +
vec4 quaternion = vec4(0.0, 0.0, 0.0, 0.0);
 +
 +
for ( int i = 0; i < 4; i++){
 +
quaternion += Quaternions[Bones[i]] * fract(Bones[i]);
 +
}
  
Eine vollständige Transformationsmatrix benötigt 16 Komponenten. 4 können direkt entfallen, da uns die W Komponente der Vektoren nicht interresiert. Der Rotationsteil der Matrix ist jedoch immernoch sehr redundant. Die 3 Sinuskomponenten sind mit umgekehrtem Vorzeichen doppelt verhanden. Es bleiben noch 9 Komponenten über, die sich dummerweise nicht auf zwei float4 Vektoren aufteilen lassen. Es wäre möglich die Cosinuswerte mit Hilfe des Pytagoras neu zu berechnen, allerdings geht hierbei das Vorzeichen verloren, so dass keine Rotationen von mehr als 90 Grad mehr möglich wären.  
+
normalize(quaternion);
 +
 +
vec4 vert = vec4(qrot(quaternion,gl_Vertex.xyz) + Joints[gl_Vertex.w] ,1.0)
 +
gl_Position = gl_ModelViewProjectionMatrix * vert;
 +
 +
N = gl_NormalMatrix * qrot(quaternion, gl_Normal);
 +
//T = gl_NormalMatrix * qrot(quaternion, Tangent);
 +
//B = cross (T,N);
 +
 +
gl_TexCoord[0] = gl_MultiTexCoord0;
 +
}
 +
</source>
 +
Die qrot Funktion rotiert einen Vektor mit hilfe eines Quaternions.  
  
Sinvoller ist es einen Vektor aus dem Rotationsteil mit Hilfe eines Kreutzproduktes neu zu berechnen. Da bei reinen Rotations/Skalierungsmatrizen Die SUmmer von rot01 und rot10 Null ergibt, kann einer von beiden den den dritten Wert des Translationsteil aufnehmen.
 
  
Da nun nur noch 8 Werte gemittel werden müssen und der Aufwand für die Rekonstruktion sehr einfach ist, kann hier sowohl Platz in den Uniforvariablen als auch Rechenleistung eingespart werden. Wichtig ist, das beachtet wird, das Opengl transponierte Matrizen verwendet.
+
Auch hier kann es sinnvoll sein, die Joints in den Modelviewspace zu transformieren und die Quaternionen mit der Normalmatrix (gegebenfals in ein Quaternion umwandeln) zu rotieren:
 +
<source lang="glsl">
 +
gl_Position = gl_ProjectionMatrix * vert;
 +
 +
N = qrot(quaternion, gl_Normal);
 +
//T = qrot(quaternion, Tangent);
 +
</source>
  
{| align="center"
+
==CPU side Stuff==
|Normale Transformationsmatrix:
 
  
{| border="1" cellspacing="0" align="center"
+
Wer gedacht hat das war es schon, wird nun leider entäuscht werden, es ist zwar nur ein geringes Problem um die uniform arrays mit fertigen Posen zu füllen, die man z.B. mit einem Blender exporter erzeugen könnte, jedoch macht es mehr Sinn eine Armature zu bauen über die man das Modell manipulieren kann. Zur inversen Kinematik ist es dann nur noch ein kleiner Schritt. Die Armature kann Quaternion als auch Matrix basierend aufgebaut werden. Für alle die Quaternionen noch etwas fremdes sind, hilft es sehr wenn man diese als eine andere Form von Rotationsmatrizen betrachted, auch wenn sie etwas andere Eigenschaften haben.
|rot00 ||rot10 ||rot20 ||0
 
|-
 
|rot01 ||rot11 ||rot21 ||0
 
|-
 
|rot02 ||rot12 ||rot22 ||0
 
|-
 
|trans0 ||trans1 ||trans2 ||1
 
|}
 
  
| Gepackte Transformationsmatrix:
+
Grundsätzlich sind folgende Eigenschaften für jeden Bone unverzichtbar:
{| border="1" cellspacing="0" align="center"
 
|rot00 ||rot10 ||rot20 ||rot11
 
|-
 
|trans0 ||trans1 ||trans2 ||rot21
 
|}
 
|}
 
  
===Überlagerung meherer Animationen===
+
Initial Joint Position, einem Positionsvektor mit 3 Kompoinenten.
 +
Aktuelle Joint Position, ebenfalls einem Positionsvektor.
 +
Die Rotation, sie kann durch ein Quaternion oder eine Rotationsmatrix dargestellt werden.
 +
Eine Liste aller Children.
  
Sicher werden am Anfang statische Posen ausreichen. Jedoch ist es mit einer Matrixmultiplikation pro Bone möglich zwei Posen zu überlagern. z.B. kann man so verschiedene Posen einer Hand an einen Arm binden. In diesem Fall müssen die Matrizen der Handanimation mit den Matrizen der Körperanimation Multipliziert werden.
+
Unnötig sind dagegen:
Alle von der Teilanimation nicht betroffenen Matrizen müssen dabei einheitsmatrizen entsprechen, damit die Multiplikation keine Auswirkung hat.
+
Ein zweites Boneende, wie viele Editoren es verwenden.
  
==Code==
 
Einfacher Vertexshader für Bones (ca 48 Instruktions):
 
<glsl>
 
attribute vec4 weight;
 
attribute vec4 index; // ivec4 doesn't work. So I use vec4. Why does ivec4 not work?
 
attribute vec3 tangent;
 
  
uniform vec4 bones[2*32];
+
Wird ein Bone rotiert, müssen alle Children mit rotiert werden. Das beiduetet, das sich deren Rotationen und Jointpositionen ändern. Alle Joints müssen dabei um den eigenen Joint mit der entsprechnden Rotation rotiert werden.
  
varying vec3 T,B,N;
 
varying vec4 color;
 
void main(void){
 
vec4 temp1 = vec4(0.0, 0.0, 0.0, 0.0);
 
vec4 temp2 = vec4(0.0, 0.0, 0.0, 0.0);
 
  
for (int i = 0; i<4; i++){
+
To continue...
temp1 += weight[i] * bones[index[i] * 2];
 
temp2 += weight[i] * bones[index[i] * 2 + 1];
 
}
 
//Matrix decompression
 
mat3 mat;
 
mat[0] = temp1.xyz;
 
mat[1] = vec3(-temp1.y, temp1.w, temp2.w);
 
mat[2] = cross (mat[0].xyz,mat[1].xyz);
 
 
gl_Position = gl_ModelViewProjectionMatrix * vec4(mat * gl_Vertex.xyz + temp2.xyz,1.0);
 
  
//Untested TBN Code
+
==Externe Links==
N = gl_NormalMatrix * (mat * gl_Normal);
 
T = gl_NormalMatrix * (mat * vec3(tangent));
 
B = cross (T,N);
 
  
        gl_TexCoord[0] = gl_MultiTexCoord0;
+
http://lumina.sourceforge.net/?id=27 Beispiel in Lumina. Der mit Lumina mitgelieferte Blenderexporter ist in der Lage die benötigten Daten zu erzeugen.
        gl_TexCoord[1] = gl_MultiTexCoord1;
 
color = weight.xywz;  //for testing
 
}
 
</glsl>
 
Da code für die TBN Matrix kann bei bedarf teilweise gelöscht werden. Nur mit Normal werden etwa 38 Instruktions benötig und komplet ohne werden nur noch 32 instruktions benötigt.
 

Aktuelle Version vom 2. März 2014, 23:56 Uhr

Info DGL.png Dieses Tutorial ist unvollständig. Ein ausführlicheres Tutorial zum Thema Bone-Animation (mit Bildern und Vertexshader-Code) findest du hier:

Tutorial Charakteranimation

Vorwort

Dieser Artikel richtet sich an alle, die schon weit Fortgeschritten sind. Ein sicherer Umgang mit Shadern und Vertexbufferobjekten ist hier Vorrausetzung. Auch Kenntnisse über Quaternionen oder Matrizen sind vorausgesetzt.

Bei der Boneanimation per Vertexshader geht es darum, möglichst viel Arbeit auf die Grafikkarte auszulagern. Jedoch sollten variablen die über das ganze Model gleich bleiben, auf der CPU berechnet werden.

Wer schon immer mal einen hochdetailierten Dinosaurier oder Octobus sein Unwesen treiben lassen wollte, ist hier genau richtig.

Grundlagen

Was sind Bones

Bones entsprechen nicht nur Knochen. Mathematisch gesehen repräsenteriert jeder Bone eine Transformationsmatrix, der einen gewichteten Einfluss auf verschiedene Vertices nehmen kann. Da Bones sehr häufig in einem Animationsskelet zusammengefast sind, werden sie durch Gelenke (Joints) verbunden, die Drehpunkten der Matrizen entsprechen. Sehr häufig werden Bones als Stab oder ähnliches dargestellt, sie können jedoch auch komplexer Formen haben (z.B. das Becken einen Menschen). Bones die durch ein festes unbewegliches Gelenk verbunden sind sollten umbedingt vermieden werden, da sie nur Resurcen kosten ohne einen Nutzen zu bringen. Grundsätzlich sollte alles was sich unabhängig voneinander bewegen können muss durch einen eigenen Bone dargestellt werden. Neben den nachgebildeten Kochen, gehören auch andere bewegliche Modelteile dazu wie Mimik und Augen.

Woher kommen die Animationsdaten?

Die Animationen können entweder aufgezeichnete Daten sein, durch Inverse Kinematik erzeugt werde oder aus einer Physikengine stammen. Bei einfachen Modellen ist es auch möglich diese Daten per Scripsprache zu erzeugen. In diesem Artikel werden die Bones eher als Schnittstelle dienen und Animationsdaten aus Blender übernommen.

Limits

Aktuelle Grafikkarten erlauben 256 bis 1024 Uniformvariablend des Typs Float4. Für einen Bone werden zwei bis vier Float4 vektoren benötigt. Damit lassen sich etwa 80 bis 500 Bones gleichzeitig verwenden. Wird dies schon beim Entwurf des Modells beachtet, stellen diese Werte kaum ein Limit dar. Bei einem symetrischem Modell lässt sich dieser Wert durch Spiegelung fast verdoppeln. Wenn das nicht ausreicht macht es Sinn bei Objekte mit vielen Bones Teile wie Hände oder Gesichter getrennt zu Rendern. Vorausgesetzt werden sollte eine Grafikkarte, die wenigstends das Shadermodel 2.0 unterstützt.


Modeldaten

Das Vertexbufferobjekt muss zu den sonst verwendeten Daten wie Vertices, Normalen, Texturkoordinaten und gegebenfalls Tangent und Bitangent noch zusätzliche Daten über die Abhängigkeit zu den Bones gespeichert werden.

Im einfachstem Fall kann jedem Vertex nur ein Bone zugewiesen werden. Hier genügt ein einzelner Integer, der als Index dient.

Wenn zwischen meheren Bones interpoliert werden soll, ist als erstes ein Wert nötig, der angibt wie viele Bones einen Einfluss auf den Vertex haben. Dann muss für jeden Bone die Gewichtung als Float und der Index als Int gespeichert werden. In den meisten Fällen sollte der Einfluss von vier Bones ausreichen. Um die größe der Geometriedaten (Attribute) klein zu halten macht es Sinn, den Index mit der Gewichtung zu addieren. Da die Summe aller gewichungen 1.0 ist, ist das trennen im shader kein problem. Lediglich der Sonderfall der Gewichtung von 1.0, muss auf zwei Gewichtungen zu 0.5 aufgeteilt werden.

In den Meisten Fällen beinflussen nur zwei Bones einen Vertex. Ein Knie sieht etwas seltsam aus wenn am Gelenk linear interpoliert wird. Hier kann ein Quaternion basierendes Animationsystem zu mehr Qualität helfen.

Vertexshader

Hier werden die beiden Varianten von Vertexshadern beschrieben.

Matrix basierende Boneanimation

Zum Gewichten werden pro Matrix 4 Vektor MADDs (Multiplikation und Addition) benötigt.

attribute vec4 Bones; 
attribute vec3 Tangent; 
uniform mat4 Pose[32]; 
varying vec3 T,B,N; 
 
void main(void){ 
	mat4 mat = 0.0; 
 
	for ( int i = 0; i < 4; i++){ 
		mat += Pose[int(Bones[i])] * fract(Bones[i]); 
		} 
 
	gl_Position = gl_ModelViewProjectionMatrix * (mat *gl_Vertex); 

	mat3 m3 = mat3(mat[0].xyz, mat[0].xyz, mat[0].xyz); // "mat3(mat)"
 	
	N = gl_NormalMatrix * (m3 * gl_Normal); 
	//T = gl_NormalMatrix * (m3 * Tangent); 
	//B = cross (T,N); 
	gl_TexCoord[0] = gl_MultiTexCoord0; 
	}


Als Optimierung kann es sinnvoll sein die Modelviewmatrix mit den Bonematrizen zu multiplizieren, da so der Vertexshader entlastet werden kann:

	gl_Position = gl_ProjectionMatrix * (mat *gl_Vertex); 

	N = m3 * gl_Normal; 
	//T = m3 * Tangent;

Quaternionen im Vertexshader

Lange gab es die diskusion ob Quaternion in shadern überhaupt Sinn machen, bei der Boneanimation im Vertexshader, hat sich herausgestellt, das die Anzahl der benötigten Instruktionen ähnlich ist wie bei der Matrix basierenden Variante. Der entscheidene Vorteil ist das sie für organische strukturen qualitativ besser ist und das nur zwei Uniform Float4 Vektoren pro Joint/Bone verbraucht werden. Etwas problematisch ist, dass die Vertexpositionen nicht mehr absolut zum Modelnullpunkt, sondern relativ zu dem Joint angegeben werden muss um das eine spherische Interpolation durchgeführt werden soll. Die W-komponente wird dabei durch die Indexnummer des Joints ersetzt.

attribute vec4 Bones; 
attribute vec3 Tangent; 
 
uniform vec3 Joints[32]; 
uniform vec4 Quaternions[32]; 
varying vec3 T,B,N; 
 
vec3 qrot( vec4 q, vec3 v ){ 
	return v + 2.0*cross(q.xyz, cross(q.xyz ,v) + q.w*v); 
	} 
 
void main(void){ 
	vec4 quaternion = vec4(0.0, 0.0, 0.0, 0.0); 
 
	for ( int i = 0; i < 4; i++){ 
		quaternion += Quaternions[Bones[i]] * fract(Bones[i]); 
		}

	normalize(quaternion); 
 
	vec4 vert = vec4(qrot(quaternion,gl_Vertex.xyz) + Joints[gl_Vertex.w] ,1.0) 
	gl_Position = gl_ModelViewProjectionMatrix * vert; 
 
	N = gl_NormalMatrix * qrot(quaternion, gl_Normal); 
	//T = gl_NormalMatrix * qrot(quaternion, Tangent); 
	//B = cross (T,N); 
 
	gl_TexCoord[0] = gl_MultiTexCoord0; 
	}

Die qrot Funktion rotiert einen Vektor mit hilfe eines Quaternions.


Auch hier kann es sinnvoll sein, die Joints in den Modelviewspace zu transformieren und die Quaternionen mit der Normalmatrix (gegebenfals in ein Quaternion umwandeln) zu rotieren:

	gl_Position = gl_ProjectionMatrix * vert; 
 
	N = qrot(quaternion, gl_Normal); 
	//T = qrot(quaternion, Tangent);

CPU side Stuff

Wer gedacht hat das war es schon, wird nun leider entäuscht werden, es ist zwar nur ein geringes Problem um die uniform arrays mit fertigen Posen zu füllen, die man z.B. mit einem Blender exporter erzeugen könnte, jedoch macht es mehr Sinn eine Armature zu bauen über die man das Modell manipulieren kann. Zur inversen Kinematik ist es dann nur noch ein kleiner Schritt. Die Armature kann Quaternion als auch Matrix basierend aufgebaut werden. Für alle die Quaternionen noch etwas fremdes sind, hilft es sehr wenn man diese als eine andere Form von Rotationsmatrizen betrachted, auch wenn sie etwas andere Eigenschaften haben.

Grundsätzlich sind folgende Eigenschaften für jeden Bone unverzichtbar:

Initial Joint Position, einem Positionsvektor mit 3 Kompoinenten. Aktuelle Joint Position, ebenfalls einem Positionsvektor. Die Rotation, sie kann durch ein Quaternion oder eine Rotationsmatrix dargestellt werden. Eine Liste aller Children.

Unnötig sind dagegen: Ein zweites Boneende, wie viele Editoren es verwenden.


Wird ein Bone rotiert, müssen alle Children mit rotiert werden. Das beiduetet, das sich deren Rotationen und Jointpositionen ändern. Alle Joints müssen dabei um den eigenen Joint mit der entsprechnden Rotation rotiert werden.


To continue...

Externe Links

http://lumina.sourceforge.net/?id=27 Beispiel in Lumina. Der mit Lumina mitgelieferte Blenderexporter ist in der Lage die benötigten Daten zu erzeugen.