2012-10-25 4 views
5

Also arbeite ich mit einem 2D-Skelett-Animationssystem.Optimieren Vertices für Skeleton Animation in OpenGL ES

Es gibt X Anzahl der Knochen, jeder Knochen hat mindestens 1 Teil (ein Quad, zwei Dreiecke). Im Durchschnitt habe ich vielleicht 20 Knochen und 30 Teile. Die meisten Knochen hängen von einem Elternteil ab, die Knochen werden jeden Rahmen bewegen. Es gibt insgesamt 1000 Frames pro Animation, und ich verwende etwa 50 Animationen. Insgesamt sind etwa 50.000 Bilder gleichzeitig im Speicher geladen. Die Teile unterscheiden sich zwischen den Instanzen des Skeletts.

Der erste Ansatz ich war, die Position/Drehung jeden Knochens zu berechnen, und einen Vertex-Array aufzubauen, die für jeden Teil davon bestanden aus:

[x1,y1,u1,v1],[x2,y2,u2,v2],[x3,y3,u3,v3],[x4,y4,u4,v4] 

und gibt diese durch glDrawElements jeweils Rahmen.

Das sieht gut aus, deckt alle Szenarien ab, die ich brauche, verbraucht nicht viel Speicher, aber funktioniert wie ein Hund. Auf einem iPod 4 könnten vielleicht 15 fps mit 10 dieser Skelette gerendert werden.

Ich stellte fest, dass der größte Teil der Leistung aufgefressen wurde, indem in jedem Frame so viele Vertex-Daten kopiert wurden. Ich entschied mich, zu einem anderen Extrem zu gehen und die Animationen "vorzurechnen", indem ich am Anfang für jedes Zeichen, das die xyuv-Koordinaten für jeden Rahmen, für jeden Teil in einem einzigen Zeichen enthielt, einen Vertex-Puffer aufbaute. Dann berechne ich den Index des Rahmens, der für eine bestimmte Zeit verwendet werden soll, und berechne einen Delta-Wert, der an den Shader weitergegeben wird, der zum Interpolieren zwischen den XY-Positionen des aktuellen und des nächsten Rahmens verwendet wird.

Die Eckpunkte so ausgesehen, pro Rahmen

[--------------------- Frame 1 ---------------------],[------- Frame 2 ------] 
[x1,y1,u1,v1,boneIndex],[x2, ...],[x3, ...],[x4, ...],[x1, ...][x2, ...][....] 

Die Vertex-Shader wie folgt aussehen:

attribute vec4 a_position; 
attribute vec4 a_nextPosition; 
attribute vec2 a_texCoords; 
attribute float a_boneIndex; 

uniform mat4 u_projectionViewMatrix; 
uniform float u_boneAlpha[255]; 

varying vec2 v_texCoords; 

void main() { 
    float alpha = u_boneAlpha[int(a_boneIndex)]; 
    vec4 position = mix(a_position, a_nextPosition, alpha); 
    gl_Position = u_projectionViewMatrix * position; 
    v_texCoords = a_texCoords; 
} 

Nun Leistung ist groß, mit 10 davon auf dem Bildschirm, es sitzt bequem auf 50 Bilder pro Sekunde. Aber jetzt verwendet es eine Tonne Speicher. Ich habe das optimiert, indem ich etwas Präzision auf XYUV verloren habe, die jetzt Ushorts sind.

Es gibt auch das Problem, dass die Knochenabhängigkeiten verloren gehen. Wenn zwei Bones vorhanden sind, ein Elternteil und ein Kind, und das Kind einen Schlüsselframe bei 0s und 2s hat, hat das Elternteil einen Schlüsselframe bei 0s, 0,5s, 1,5s, 2s, dann wird das Kind nicht zwischen 0,5s und 1.5s wie es sollte.

Ich habe eine Lösung gefunden, um dieses Knochenproblem zu beheben - indem ich das Kind zwinge, Keyframes an den gleichen Stellen wie die Eltern zu haben. Aber das verwendet noch mehr Speicher und tötet im Grunde den Punkt der Knochenhierarchie.

Hier bin ich jetzt. Ich versuche, ein Gleichgewicht zwischen Leistung und Speichernutzung zu finden. Ich weiß, dass hier eine Menge redundanter Informationen vorhanden sind (die UV-Koordinaten sind für alle Rahmen eines bestimmten Teils identisch, sie werden also etwa 30 mal wiederholt). Und ein neuer Puffer muss für jeden Satz von Teilen erstellt werden (die einzigartige XYUV-Koordinaten haben - Positionen ändern sich, weil verschiedene Teile unterschiedliche Größen haben)

Im Moment werde ich versuchen, ein Vertex-Array pro Charakter einzurichten, Das hat den XYUV für alle Teile und berechnet die Matrizen für jedes Teil und positioniert sie im Shader neu. Ich weiß, das wird funktionieren, aber ich bin besorgt, dass die Leistung nicht besser sein wird, als einfach die XYUVs für jeden Frame hochzuladen, den ich am Anfang gemacht habe.

Gibt es einen besseren Weg, dies zu tun, ohne die Leistung zu verlieren, die ich gewonnen habe?

Gibt es irgendwelche wilden Ideen, die ich versuchen könnte?

+0

Nette Frage sir – Weacked

+0

Machen alle Knochen für jeden Rahmen "sich selbständig" oder werden viele Knochen einfach verschoben, weil sich die Eltern bewegt haben? – Dirk

+0

Alle außer einem der Bones werden mit dem übergeordneten Objekt verschoben. Es gibt einige Knochen, die fast nichts unabhängig vom Elternteil machen, und dann gibt es einige, die sich viel öfter bewegen, als der Elternteil bewegt wird. –

Antwort

1

Der bessere Weg, dies zu tun, ist Ihre 30 Teile im laufenden Betrieb zu transformieren, nicht Tausende von Kopien Ihrer Teile in verschiedenen Positionen zu machen. Ihr Vertex-Puffer enthält eine Kopie Ihrer Vertex-Daten und spart so viel Speicherplatz. Dann kann jeder Rahmen durch eine Reihe von Transformationen dargestellt werden, die für jeden Knochen, den Sie mit einem Aufruf an glDrawElements() zeichnen, einheitlich an Ihren Vertex-Shader übergeben werden. Die Transformation jedes abhängigen Knochens ist relativ zum Elternknochen aufgebaut. Je nachdem, wo auf dem Kontinuum zwischen handgefertigter und prozedural erzeugter Animation Ihre Animationen stehen sollen, können Ihre Transformationen mehr oder weniger Speicherplatz und CPU-Rechenzeit beanspruchen.

Jason L. McKesson kostenloses Buch, Learning Modern 3D Graphics Programming, gibt eine gute Erklärung darüber, wie dies am Ende dieses Kapitels 6. Das Beispielprogramm in Kapiteln erreichen zeigt, wie eine Matrix-Stack verwenden, um ein hierarchisches Modell zu implementieren. I have an OpenGL ES 2.0 on iOS port of this program available.

+0

Leider funktionierte meine ursprüngliche Version genau so. Und es hat nirgends fast so schnell wie die aktuelle Implementierung durchgeführt. Ich wusste, dass es eine ärgerlich große Menge an Speicher verbrauchen würde und unsere Ladezeiten erhöhen würde, aber es ist mit vielen Charakteren auf dem Bildschirm mindestens 50% schneller. Aber, danke für die Vorschläge, abgestimmt. –