Opengl Particles

Hey Leuts, Hey @Fancy :smiley:

Aalso, ich wollte „ein paar“ particles mit lwjgl rendern. Da liegt schon das Problem.
Jedes Particle hat ne eigene Matrix, die dann eben irgendwie cpu-seitig bearbeitet wird.
Nun muss ich ja alle matrizen jedes frame auf den shader schieben, und dann mit draw_arrays_instanced
zeichnen. Das Problem: selbst wenn ich ein UBO benutze - auf meinem laptop kann ich das mat4 models[] array nicht größer
als 200 felder groß machen.
Was mach ich nun? Doch jedes einzelne partikel mit drawArrays zeichnen und die matrix davor hochladen? Oder nicht?..
Instancing deshalb, da die partikel kreise sind und im vbo eben die vielen punkte des kreises liegen…
hab ich nen komplett falschen ansatz?.. wie soll man das sonst machen?.. ich dachte schon so an mindestens 1000 partikel die ich
da auf den bildschirm bringen wollte…

Irgendne idee?
Danke!

*** Edit ***

Ach und noch ne Frage:

Angenommen ich hab nur ein Shader Programm, was wohl eher seltener der Fall sein wird, aber naja,
dann bringen mir UBO’s an sich doch nichts oder? Oder bringen die doch irgendeinen Geschwindigkeitsvorteil?..

Nochmal: Du hast vor, die Positionen der Partikel als einzelne Uniforms zu speichern? (Ich hoffe, Fancy wird darauf ebenso irritiert reagieren, wie ich :smiley: )

hm, als einzelne mat4… insgesamt wollte ich ein uniform array… also ja…
wie denn sonst? -_- Wenn ich die logik / physik / etc, auf der cpu machen will?..

Nun, wie so oft muss ich für Details an Fancy verweisen, aber … ich denke, bei allem, was “große” Daten sind (und eben NICHT nur Parameter für den Shader) bringt man die Daten in einem normalen Buffer object unter - oder ggf. einer Textur (ja, auch ganze Matrizen werden in Texturen gespeichert). Wenn man die Daten auf der CPU verändern will, müssen sie ja sowieso den Weg alles irdischen (also über den Bus) gehen, d.h. man muss diesen Buffer eben mit den Daten füllen, die man auf der CPU verändert hat…

ja das ist klar. aber wohin will man ihn dann laden, sodass die daten noch unterschieden / getrennt werden können?
Ein array ist doch da am sinnvollsten?..

Moin,

ein Array wirst Du so auch nicht größer anlegen können als ein UBO. Abhängig vom Treiber vielleicht sogar nur deutlich kleiner und wahrscheinlich auch langsamer. Wie Marco schon schrieb, entweder Du speicherst die Daten in einer normalen Textur, einem UBO, einem Texture Buffer Object (TBO) oder einem Shader Storage Buffer Object (SSBO) (geht erst ab OpenGL 4.3).

Ich würde ein TBO empfehlen. Außerdem solltest Du Buffer Object Streaming anwenden.

Unabhängig davon, ist es eine bescheidene Idee das auf der CPU zu berechnen. Wenn man sich alle 16ms in den Fuß schießt, braucht man sich nicht zu wundern, wen man anschließend nur noch schwer flüssig laufen kann.

(Die OpenGL Seite ist gerade down, imho sollten die Links aber stimmen)

Viele Grüße
Fancy

Also SSBO hsat du ja schon häufig erwähnt, kommt aber nicht infrage wegen kompatibilität und ich hab
nicht einen rechner der das kann ^^ TBO guck ich mir mal an, aber ist das nicht irgendwie eine falsche verwendung?..
BOS? GUck ich mir mal an. „einem UBO,“ hä? Gerade das ist ja zu klein…

Hä, hä, hä? Wie soll man das denn sonst machen? Ich hab ein Particle System, das hält particles, die haben je eine matrix.
Wie soll ich das sonst machen? Welchen Draw call soll ich dann bitte verwenden? ._.

Wenn das auf der CPU berechnet wird, muss das Ergebnis für jedes Frame neu zur Grafikkarte übertragen werden, das macht es langsam. Deshalb wird so etwas eigentlich ausschließlich auf der Grafikkarte in einem Shader berechnet. Der Inhalt der Texturen/Buffer wird also nicht mehr von der CPU aktualisiert, sondern direkt von der GPU.

Ein willkürlicher Link: http://wiki.delphigl.com/index.php/GLSL_Partikel_2

(Mit OpenGL 3 sollte das so auch ohne die dort verwendeten Extensions funktionieren.)

Viele Grüße
Fancy

HIilllffee Fancy ._.

Ich verstehe nicht was der da macht.

Erstens: Geometry Shader… was wie warum kann ich ja googeln,
aber geht das nicht ohne?

Zweitens: Was genau… was macht der jetzt auf der c und was auf der g - pu?
Der sagt ja auch was von “jeden frame” iwas in den buffer schieben… er benutzt doch
texture buffer oder nicht? wie genau greift der jetzt (read / write) auf den buffer zu auf
der gpu??

Sorry, aber ich versteh diese anleitung leider überhaupt nicht ._.

*** Edit ***

Erhaltet ihr eigentlich ne Nachricht bei so einem edit?

Nachricht nicht, aber es taucht wie ein “Neuer Beitrag” in der Liste auf.

(Zum Thema: Wenn ich konkreter helfen könnte, würde ich das wohl tun. Aber erstens bin ich nicht so der Experte in diesen “Shader-nahen” Themen, und zweitens verstehe ich nichtmal, warum du für jeden Partikel eine komplette Matrix brauchst. Meistens haben die ja nur eine Position, aber ich kann mir vorstellen, dass man fancy Effekte hinbekommt, wenn man denen eine Matrix spendiert und sie als komplexe (und dann eben rotierte und skalierte) Objekte rendert, und nicht nur als Punkte…)

Ja das ist der hauptansatzpunkt, aber selbst wenn nicht, auch wenn es einfach nur Vec3’s wären…
hm… dann hätte ich halt 8 * mehr platz… aber ist ja trozdem sehr langsam, und geht nur auf
der gpu laut fancy und googel. verstehe nur nicht genau wie ._.
mir mangelt es einfach an erfahrung… ^^

Ich würde einen Shader schreiben, der als Werte pro Partikel erwartet: Position, Bewegungsvektor und die (Rest-) Lebenszeit. Im Shader lässt sich dann einfach berechnen (ungefähr):

position = position + bewegung * timedelta
bewegung = (bewegung + schwerkraft) * daempfung * timedelta;
lebenszeit = lebenszeit – timedelta;

Also einfach der Übergang von einem Zeitpunkt auf den nächsten Zeitpunkt. Das Spannende ist nun, wie man die Ergebnisse des Shaders im nächsten Frame wieder als Eingabewerte verwenden kann. In dem Tutorial verwendet der dazu ein TBO und Transform Feedback. Warum er ein Geometrie Shader verwendet, sehe ich so auf Anhieb allerdings auch nicht. Allerdings verwendet er OpenGL 2.0 + Extensions, es könnte sein das eine der verwendeten Methoden damit noch nicht im Vertex Shader möglich war.

Vielleicht komme ich morgen dazu ein möglichst einfaches KSKB zu schreiben.

Eine komplette Transformationsmatrix mitzuschleppen, macht imho nur sinn, wenn man statt einzelner Punkte wirkliche Objekte darstellen möchte. Bei Punkten wären nur die Position und die Größe interessant. Und die Größe könnte man einfach mit gl_PointSize = lebenszeit abkanzeln.

Viele Grüße
Fancy

Ich find’ sowas wie http://www.youtube.com/watch?v=dX8KeqYLY10 ja ziemlich lustig. Da läuft die Berechnung aber mit CUDA, und nicht in einem “normalen” Shader…

Ja, das ist recht beeindruckend! CUDA müsstest Du ihm dann allerdings zeigen. :wink:
Liegt der Quellcode davon im Nvidia CUDA SDK? Dann würde ich mir davon ggf. mal ein GLSL port auf die ToDo Liste schreiben…

Ich hab inzwischen auch noch mal über das Tutorial oben nachgedacht: Der verwendet den Geometrie Shader um denn Partikel am ende seiner Lebenszeit komplett verwerfen zu können, dadurch taucht der in der nächsten Iteration gar nicht mehr auf. Eigentlich ziemlich geschickt.

Viele Grüße
Fancy

Der Code ist im SDK, aber auch einzeln unter http://docs.nvidia.com/cuda/cuda-samples/#smoke-particles (ist halt eine recht komplexe Anwendung im Vergleich zu den anderen Samples… und… NVIDIA samples sind … zurückhaltend formuliert… nicht auf leichte Versteh- und Nachvollziehbarkeit ausgelegt :verzweifel: )

Fany, bitte veröffentliche das Kskb hier (noch) nicht!
Ich will endlich mal was ohne kskbs schaffen ._.
das prinzp hab ich jetzt also verstanden, möglichst wenige informationen -> gpu,
gpu -> rest erledigen ist also die devise. Dieser Transport von dem einen in den nächsten
frame. Das würd ich gern verstehen… Transform Feedback sagtest du… Ich guck mal wie
man das erreichen kann, diesen Transport…

Ich werde mich zurückhalten. :wink:

Ja, mit dem Transform Feedback solltest Du anfangen. Ein einfaches Beispiel wäre, wenn Du Dir ein KSKB schreibst, das 3 VAOs mit jeweils einem VBO verwendet. In dem ersten VAO kommen die Partikel, die in diesem Frame neu hinzukommen (von der CPU). Im zweiten VAO liegen die bewegten Partikel aus dem vorherigen Frame (von der GPU). Und in den VBO des dritten VAO werden die Ergebnisse des aktuellen Frames hineingeschrieben. Anschließend tauschst Du VAO1 mit VAO2.

Im Pseudocode etwa so:

begin transform feedback, write to vao2.vbo

bind emitter vao
glDrawArrays

bind vao1
glDrawArrays

end transform feedback
switch(vao1,vao2)

Neue Partikel werden also einmalig von der CPU per VAO/VBO auf die Grafikkarte gebracht und leben anschließend zwischen den beiden VAO1 und VAO2 auf der Grafikkarte. Im Detail kann das etwas kniffelig sein. Ich hab dazu ein KSKB, wenn Du nicht weiter kommst, einfach hier bescheid sagen.

@Marco13 : Danke für den Link, werd bei Gelegenheit mal sehen wie viel ich davon verstehe. :slight_smile:

Viele Grüße
Fancy

Okay danke für diese Erklärung. Ähm… da wird das Vbo aber groß, bei ein paar tausend partikeln?
Aber gut. Ich werds morgen mal versuchen. Und was genau definiertst du jetzt unter einem partikel in
diesem vbo (0) ? Alle eigenschaften die ich eben brauche? Würde ja sinn machen…

Ja. Vermutlich ist es am einfachsten, wenn Du mit GL_POINTS anfängst und nur die Position speicherst (in allen 3 VBOs). Auch ein einfacher Shader ist am Anfang hilfreich, etwa:

#version 140

layout(std140) uniform MVP {
    mat4 vp;
    float delta;
};

in vec4 vIn;
out vec4 vOut;

void main() {

    vOut = vIn + vec4(0, 0.05, 0, 0) * delta;
    gl_Position = vp * vIn;

}

Beliebig komplizierter machen kann man das dann immer noch.

Viele Grüße
Fancy

Moment mal, ist ja schön und gut - aber transform feedback, (was ja die einzige möglichkeit ist aus glsl in ein buffer zu schreiben, oder etwa nicht?)
ist laut opengl.org ab gl 4.4… Super ^^