Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

1

03.08.2012, 10:50

Skeletale Animation, Berechnung der Matrizen

So, also das Problem ist relativ schlicht. Was brauche ich um die Matrizen für jedes bone auf der CPU berechnen zu können?
Momentan sieht mein "System" so aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
struct bone{
    bone* parent;
    mat4 modelview;
    vec3 root, offset;
};

struct bonePose{
    bone* bone;
    vec3 offset;
    vec3 rotation;
}


Wobei ich so für jedes "Frame" die Rotation und das neue offset speichern muss. Auch wenn ich bei einem Arm z.B. nur den Oberarm drehen wollte. Ganz einfach weil sich ja auch der ganze Rest mitdreht und die Gelenke deshalb nicht an der Position bleiben an der sie sind. Ich weiß auch nicht wie ich zwischen 2 Frames interpolieren kann, da ich ja das offset sonst selbst ausrechnen müsste.
Also irgendwie sowas:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for(unsigned int i = 0; i < m_skeleton.size(); i++){
    if(m_skeleton[i].parent){
        m_skeleton[i].root = m_skeleton[i].parent->root+m_skeleton[i].parent->offset;
        m_skeleton[i].modelview = m_skeleton[i].parent->modelview;
    }
    else{
        m_skeleton[i].modelview.setIdentity();
    }

    m_skeleton[i].offset = m_frames[i].offset;

    //m_skeleton[i].modelview.setIdentity();
    m_skeleton[i].modelview.translate(m_skeleton[i].root);
    m_skeleton[i].modelview.rotate(m_frames[i].rotation.x, 1, 0, 0);
    m_skeleton[i].modelview.rotate(m_frames[i].rotation.y, 0, 1, 0);
    m_skeleton[i].modelview.rotate(m_frames[i].rotation.z, 0, 0, 1);
    m_skeleton[i].modelview.translate(m_skeleton[i].root*-1);
}

Und wie rechne ich mit den weights nachher im Shader? Im Moment sieht das so aus:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#version 150

uniform mat4 pm;
uniform mat4 bm[2];

in vec3 vertex;
in ivec3 boneIndex;
in vec3 boneWeight;

void main(){
    vec4 pos = vec4(vertex, 1.0);

    if(boneIndex.x > -1){
        pos = (bm[boneIndex.x]*pos)*boneWeight.x;
    }
    if(boneIndex.y > -1){
        pos = (bm[boneIndex.y]*pos)*boneWeight.y+pos;
    }
    if(boneIndex.z > -1){
        pos = (bm[boneIndex.z]*pos)*boneWeight.z+pos;
    }
    
    gl_Position = m*vec4(vertex, 1.0);
}

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »DeKugelschieber« (04.08.2012, 13:09)


DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

2

04.08.2012, 13:09

push (weil komplett bearbeitet)

3

04.08.2012, 14:50

Vielleicht hilft dir das hier etwas weiter:
http://zfx.info/viewtopic.php?f=5&t=2266

Da ist zumindest eine Skizze, aus welchen Bestandteilen die Matrix berechnet wird und welche Transformationen da eine Rolle spielen.

Letztendlich berechnest du rekursiv für jeden Knochen eine finale Matrix. Mein Shader sieht dann z.B. so aus:

HLSL-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#version 130

attribute vec3 PositionAtt;
varying vec4 varyFragmentPosition;
attribute vec3 NormalAtt;
attribute vec3 TangentAtt;
attribute vec3 BitangentAtt;
varying vec3 varyNormal;
varying vec3 varyTangent;
varying vec3 varyBitangent;
attribute uvec4 WeightIdAtt;
attribute vec4 WeightValueAtt;
uniform mat4 varyBoneTransforms[50];

void main()
{
 varyNormal=normalize(NormalAtt);
 varyTangent=normalize(TangentAtt);
 varyBitangent=normalize(BitangentAtt);
 vec4 Pos=vec4(PositionAtt, 1);
 Pos=(varyBoneTransforms[WeightIdAtt[0]]*vec4(PositionAtt, 1))*WeightValueAtt[0]
   + (varyBoneTransforms[WeightIdAtt[1]]*vec4(PositionAtt, 1))*WeightValueAtt[1]
   + (varyBoneTransforms[WeightIdAtt[2]]*vec4(PositionAtt, 1))*WeightValueAtt[2]
   + (varyBoneTransforms[WeightIdAtt[3]]*vec4(PositionAtt, 1))*WeightValueAtt[3];
 varyFragmentPosition=gl_ModelViewMatrix*Pos;
 gl_Position=gl_ProjectionMatrix*gl_ModelViewMatrix*Pos;
}


Interpolieren geht dann, indem du die Keys interpolierst und eine neue Matrix daraus ausrechnest. Für Rotationskeys möchtest du dann aber vielleicht Quaternionen benutzen, anstatt die Winkel linear zu interpolieren.
Allgemein muss die Interpretation natürlich nicht linear sein, je nach Anwendungsfall würde man vielleicht ein ease in/out benutzen (also mit Beschleunigung und Abbremsung). Andererseits macht man das vielleicht eher im Animationsprogramm und exportiert dann 60 Keys / Sekunde, zwischen denen man dann wieder linear interpoliert (der Unterschied ist dann nicht mehr zu sehen). Aber das nur um zu verdeutlichen, dass es nicht "die" richtige Interpolationsmethode gibt.

(Sollte das jetzt zu knapp gewesen sein, einfach nochmal nachfragen).
Lieber dumm fragen, als dumm bleiben!

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

4

04.08.2012, 15:46

Mich interessiert eher wie du die Matrizen vorher auf der CPU errechnest. Ich hab das Problem das ich ja zu den Gelenken transformieren muss um dann zu rotieren, aber für das nächste bone dann den Gelenkpunkt an einer anderen Position habe.

Also was wird an dein

Quellcode

1
uniform mat4 varyBoneTransforms[50];


gesendet?

[Edit]

Sehe ich das in der Zeichnung richtig, dass bei der Node Transformation vom letzten zum root bone gerechnet wird?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »DeKugelschieber« (04.08.2012, 15:52)


5

04.08.2012, 16:06

Deine Vertexe sind am Anfang ja in Modellkoordinaten. Wie es halt exportiert wurde. Die Transformationskeys geben dann jeweils an, wie du von Modellkoordinaten in Knochenkoordinaten kommst und das halt immer relativ zum Elternknochen. Also dass die Hand 30 cm unter dem Arm ist oder so. Was du letztendlich aber brauchst, sind die Vertexe in den neuen, transformierten Modellkoordinaten.
Aus der Standardpose kannst du also die Offsetmatrizen aus der Zeichnung berechnen. Die bleiben dann pro Knochen konstant. Die Kopfvertexe sind dann also nicht mehr irgendwo 1,80 oberhalb des Ursprungs, sondern ziemlich nah um ihn herum, weil sie halt relativ zum Kopf sind. Bei der Rücktransfomration sind sie dann vielleicht nicht mehr 1,8 über dem alten Ursprung, sondern nur noch 1,4, weil sich der Charakter nach vorne gebeugt hat oder so.

Wie die Matrizen dann berechnet werden, siehst du ganz unten in der Zeichnung. (Scale*Rot*Trans) bilden jeweils die Node-Transformations. Wichtig ist eben, dass deine Transformationen eine durchgehende Kette bilden, die in Modellkoordinaten startet und auch dort wieder aufhört.
Eventuell musst du die Reihenfolge drehen, in der Zeichnung werden die Transformationen von links nach rechts ausgeführt, da man aber normalerweise Matrix*Vektor rechnet, würden sie dann von rechts nach links geschrieben.
Lieber dumm fragen, als dumm bleiben!

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

6

28.10.2012, 20:54

[gelöscht]

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »DeKugelschieber« (29.10.2012, 23:02)


DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

7

29.10.2012, 23:44

So nochmal einfach. Ich hab mir das hier durchgelesen.
Dort wird erklärt wie man die Matrizen aufsetzt. Ich habe das jetzt so verstanden:

Lokal: Offset*Skalierung*Rotationen*Verschiebung // ich nehme erstmal nur die Rotationen
Global: Lokal*Global_des_Elternbone

Code dazu:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void Bone::updateMatrices(){ // wird einmal für root bone aufgerufen
    calculateModelMatrix();

    for(unsigned int i = 0; i < m_children.size(); i++){
        m_children[i]->calculateModelMatrix();
    }
}

void Bone::calculateModelMatrix(){
    // local space
    m_local.setIdentity();

    if(m_start != 0 && m_end != 0){
        m_local.rotate(m_start->m_rotate.x, 1, 0, 0); // skalierung, verschiebung noch außer acht!
        m_local.rotate(m_start->m_rotate.y, 0, 1, 0);
        m_local.rotate(m_start->m_rotate.z, 0, 0, 1);
    }

    m_local.translate(m_offset);
    
    // world space
    m_world = m_local;

    if(m_parent){
        m_world *= m_parent->m_world;
    }
}


m_start->m_rotate ist ein Vektor mit der lokalen Rotation für die Pose. In meinem Beispiel einfach mal 10|0|0.

Ich verstehe jetzt erstmal nicht was dieses offset so da zu suchen hat. Dadurch werden doch die Vertices verschoben? Ich will aber um das Gelenk drehen.


(Link)


Wie man im linken Bild sieht... Im rechten habe ich das offset einfach auskommentiert. Aber dort wird nicht um den Gelenkpunkt (Ende unterer [root] bone 0|0|1) gedreht sondern um den Mittelpunkt des Koordinaten Systems vom vorherigem bone.

Also wie setzte ich den Gelenkpunkt (das offset) in den Mittelpunkt meines lokalen Koordinatensystems? Ich dachte eigentlich das ginge mit dem offset...
Wäre nett wenn mir das mal jemand erklären könnte. Ich habs zwar gesucht, aber so richtig verstanden/gefunden habe ich da noch nichts.

(ich komm mir langsam vor wie der größte Vollpfosten das ich das nicht verstehe)

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

8

30.10.2012, 20:05

I don´t get it.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Bone::calculateModelMatrix(){
    // local space
    m_local.setIdentity();
    
    if(m_start != 0 && m_end != 0){
        m_local.rotate(m_start->m_rotate.x, 1, 0, 0);
        m_local.rotate(m_start->m_rotate.y, 0, 1, 0);
        m_local.rotate(m_start->m_rotate.z, 0, 0, 1);
    }
                
    // world space
    m_world = m_local*m_offset;

    if(m_parent){
        m_world *= m_parent->m_world;
    }
}



(Link)


Der linke bone sollte eigentlich da sein wo der Pfeil hinzeigt. Liegt es schlicht an der Reihenfolge der Matrizenmultiplikation? Hab ich das Prinzip immer noch nicht verstanden?

Werbeanzeige