DGL
https://delphigl.com/forum/

Noiseerzeugung im Shader: Textur oder Berechnung
https://delphigl.com/forum/viewtopic.php?f=20&t=11364
Seite 1 von 1

Autor:  Vinz [ Fr Apr 10, 2015 15:53 ]
Betreff des Beitrags:  Noiseerzeugung im Shader: Textur oder Berechnung

Hallo Leute,

Ich brauche eine gute Noisefunktion im Shader, also eine, die keine Muster aufweist etc.
Und zwar, um Partikeln immer wieder neue Startpositionen zuzuteilen und zwar unabhängig von ihrer momentanen Position,
und so, dass sich keine Anhäufungen bilden.

Ich habe mir mal ein paar Algorithmen angesehen, perlin und simplex sehen gut aus, es sind aber schon einige Rechenschritte.
Jetzt könnte man ja eine Noisetextur schreiben, und müsste dann für jeden Wert nur einmal fetchen.
Ist das ratsam? Da die Textur vorberechnet ist, befürchte ich, dass sich dann repetitive Muster ergeben.
Kann man das durch geeignete Koordinaten beheben, und bekommt die gleiche Qualität hin?

Das gleiche Problem sehe ich eigentlich auch bei der Berechnung im Shader.
Wie bekommt man Koordinaten, so dass das Ergebnis der Funktion nicht mit den Koordinaten korreliert ist, zumindest nicht sichtbar.

Wenn ich im Hauptprogramm per Integeroverflow mein Rauschen berechne, ist es ja was anderes, weil sich der seed ja mit jeder Berechnung ändert.
Geht das im Shader auch? Mit TransformFeedBack müsste es doch gehen oder?
Würdet ihr diese Berechnungsmethode empfehlen, oder sind Perlin und co. schöner?
Die overflowmethode wär jedenfalls wesentlich günstiger, wenn man eh schon TFFB macht.

Grüße,
VinZ

Autor:  Lord Horazont [ Fr Apr 10, 2015 16:58 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Du kannst Simplex erstmal probieren und schauen, ob die Performance ausreicht. Wenn das nicht der Fall ist, kannst du eine Noisetexture verwenden. Die würde ich alle paar Frames (oder gar jeden Frame) auf der CPU neu generieren und hochladen (das Generieren kann man ja machen während die GPU gerade beschäftigt ist). Als Texturkoordinate kannst du dann z.B. den Partikelindex oder sowas verwenden.

viele Grüße,
Horazont

Autor:  Vinz [ Sa Apr 11, 2015 00:28 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Wäre ne Lösung, bei mehreren Millionen Partikeln wird die CPU das aber wohl nur so ein mal pro Sekunde schaffen, schätz ich.
Was mich auf ein weiteres Problem bringt:
Wenn es richtig viele Partikel sind, brauch ich ja auch ne riesige Textur, und dann wird das fetchen wieder langsamer.

Autor:  Vinz [ Sa Apr 11, 2015 01:06 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Mir is grad aufgefallen, dass es es bei Perlin und Simplex überhaupt nicht um die Erzeugung der Zufallszahlen geht, die hier vorausgesetzt werden.
Gerade um die gehts mir aber.
Was ich also suche ist eine gute Zufallszahlenfunktion in glsl, hat zufällig jemand eine auf Lager?
Also eine Funktion, die Texturkoordinaten und Zeitkoordinate frisst und möglichst unkorellierte (scheinbar) Werte ausspuckt.

Autor:  Shaijan [ Sa Apr 11, 2015 09:49 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Hey,

ich würde dir den Ansatz mit einer vorberechneten noise Textur empfehlen. Wenn du dem Shader jeden Frame z.B. die aktuelle Systemzeit mitschickst als uniform variable, kannst du die mit dem Partikelindex verrechnen (einfach addieren) und bekommst so scheinbar zufälligere Werte. Dieser Ansatz hat vor allem den Vorteil, dass er schnell umsetzbar ist und du wirst schnell sehen können ob dir die Ergebnisse ausreichen. Wenn nicht, kannst du entweder mehrere vorberechnete Texturen bereit halten aus denen du jeden Frame zufällig eine auswählst oder du kannst sie, wie Lord Horazont sagte, neu generieren.

Autor:  Lord Horazont [ Sa Apr 11, 2015 11:09 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Ich habe gerade mal getestet:
Code:
  1. #include <chrono>
  2. #include <cstdlib>
  3. #include <iostream>
  4. #include <vector>
  5.  
  6. typedef std::chrono::steady_clock used_clock;
  7.  
  8. int main()
  9. {
  10.     std::vector<float> data(1000000);
  11.     unsigned int seed = time(nullptr);
  12.     used_clock::time_point t0 = used_clock::now();
  13.     for (unsigned int i = 0; i < 10; i++) {
  14.         for (unsigned int j = 0; j < 1000000; j++) {
  15.             data[j] = float(rand_r(&seed)) / RAND_MAX;
  16.         }
  17.     }
  18.     used_clock::time_point t1 = used_clock::now();
  19.  
  20.     std::cout << std::chrono::duration_cast<std::chrono::duration<float, std::ratio<1> > >(t1 - t0).count() / 10. << std::endl;
  21.  
  22.     return 0;
  23. }
  24.  

Das gibt bei mir 0.012 aus (ohne jegliche Optimierungen!), also 12ms pro 1M Punkte. Wenn man das in einem Thread parallel zum Renderthread macht und einen Doublebuffer benutzt, dann bekommt man, zwar recht knapp, aber dennoch pünktlich, zu jedem Frame 1M frische Zufallsdaten. Faktisch wirds auch reichen, pro Frame nur ¼ der Noisetextur neu zu schreiben und halt wie Shaijan schon vorschlug mit einem Offset zu arbeiten. Ich würde hier aber einen rand_r() Wert für das Offset nehmen anstatt der Systemzeit, da die halt kontinuierlich wächst. Da kann man sich leicht Muster reinholen, v.a. wenn man nur einen Teil der Textur neuschreibt.

viele Grüße,
Horazont

Autor:  OpenglerF [ Sa Apr 11, 2015 11:25 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Es ist eine wichtige Unterscheidung zwischen Zufallszahl und Noise zu treffen. Eine Zufallszahl ist eine Zahl die einfach zufällig ist. Man erhält Werte die so verteilt sind:
Bild
Bild

Für viele Anwendungen ist das aber nicht gut genug. Man will nicht bloß zufällige Strukturen sondern auch einen kontinuierlichen Verlauf.
Das sieht dann zum Beispiel so aus:
Bild
Bild

Die beiden Dinge unterscheiden sich ziemlich. Für zweiteres gibt es Verfahren wie Perlin oder Simplex Noise. Oder viel einfachere Dinge, wie einfach zufällige Werte in einem Raster interpolieren und mit verschiedenen Frequenzen und Amplituden aufaddieren. (Grundkonzept)
Um Zweiteres umzusetzen braucht man aber in der Tat erstmal einfach Zufallszahlen.

Zufallsdaten als Textur würde ich eher nicht verwenden, weil es sich wiederholt. Wenn du keinen Schreibzugriff besitzt, können Hash Funktionen eine gute Basis für Zufallszahlen sein. Du hast einen nicht zufälligen Ausgangswert. Zum Beispiel gl_VertexID oder die Pixelkoordinate X und Y verrechnet. Wenn du keinen Determinus benötigst und OpenGL 4.2 zur Verfügung hast, bietet sich auch ein Atomic Counter Buffer an. Jedenfalls hast du kontinuierliche Eingangsdaten. Jetzt brauchst du nur noch eine Funktion die für kontinuierliche Eingangswerte in zufällige Ausgangswerte umwandelt.
Da gibt es viele Vorschläge dazu im Netz, ich habe in letzter Zeit das verwendet:
Code:
  1.  
  2. uint Hash(uint x)
  3. {
  4.     x ^= 0x9AAB7F34;
  5.     x += ( x << 10u );
  6.     x ^= ( x >>  6u );
  7.     x += ( x <<  3u );
  8.     x ^= ( x >> 11u );
  9.     x += ( x << 15u );
  10.     return x;
  11. }
  12. float HashFloat(uint x)
  13. {
  14.     return float(Hash(x)) * (1.0 / 4294967295.0);
  15. }


Nochmal zur Geschwindigkeit: Eine Funktion im Shader sollte einer Textur auch überlegen sein. Speicherzugriffe sind zunehmend auch auf der GPU der Flaschenhals. Man kann auch prognostizieren, dass der Trend zu noch langsameren Speicher geht.

@Lord Horazont
rand_r ist nicht portabel und hat für verschiedene Anwendungen weitere Probleme.
Man sollte die Zufallszahlen in C++ inzwischen am besten mit <random> generieren.
https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

Autor:  Vinz [ Sa Apr 11, 2015 12:23 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

@OpenglerF
Danke für die Klarstellung!
Zitat:
Wenn du keinen Schreibzugriff besitzt, können Hash Funktionen eine gute Basis für Zufallszahlen sein.

Aber wie ist es wenn man Schreibzugriff hat, z.B. mit TFFB, gibts da bessere Ansätze?

@Horazont
Ich bin von komplexeren Berechnungen für die Zufallszahlen ausgegangen, aber gut zu wissen, dass man mit rand() locker eine Million Werte pro Frame bekommt, man muss sie aber auch noch in den VRAM laden.

Autor:  Vinz [ Sa Apr 11, 2015 12:28 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Zitat:
Eine Funktion im Shader sollte einer Textur auch überlegen sein.

Was aber ein Vorteil an der Textur sein kann, ist, dass man die Zufallszahlen mit komplexeren Berechnugen erzeugen kann, und somit ne bessere Quali bekommt, z.B. mit http://www.cplusplus.com/reference/random/mt19937/.

Autor:  Vinz [ Sa Apr 11, 2015 12:36 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Shaijan hat geschrieben:
...Wenn du dem Shader jeden Frame z.B. die aktuelle Systemzeit mitschickst als uniform variable, kannst du die mit dem Partikelindex verrechnen (einfach addieren) und bekommst so scheinbar zufälligere Werte. Dieser Ansatz hat vor allem den Vorteil, dass er schnell umsetzbar ist...


Damit hab ich auch schon rumexperimentiert, das Problem ist aber, dass wenn man die Zeit einfach draufaddiert, man die Textur quasi unter den Texturkoordinaten durchzieht, dadurch wird es sehr zeitlich-räumlich korreliert, quasi wellenmäßig.

Autor:  OpenglerF [ Sa Apr 11, 2015 13:43 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Zitat:
Aber wie ist es wenn man Schreibzugriff hat, z.B. mit TFFB, gibts da bessere Ansätze?

Was ist den TFFB? Davon habe ich noch die gehört. Ich denke, der Ansatz ist ziemlich gut. Er kann gleichverteilte Zufallszahlen mit hoher Periode erzeugen. Alle klassischen Pseudozufallsgeneratoren arbeiten leider sequentiell und sind somit kaum für Shader geeignet. In einem Compute Shader mit einer Storage Buffer Object, könntest du jeden Thread einen eigenen Zustand für den Zufallsgenerator zuweisen und so parallel arbeiten. Wenn es geht, würde ich aber davon absehen. Ich weiß nicht, welches Ziel du genau verfolgst, Hashfunktionen haben allerdings für mich bisher für alle Dinge ausgereicht.

Die Systemzeit würde ich nicht verwenden. Ich würde lieber gut generierte Zufallszahlen von der CPU verwenden. RDTSC ist auf dem x86 ein guter Startpunkt für einen Seed. Ich glaube in Linux gibt es auch irgendeine Quelle von echten Zufallszahlen. Auf neuen Prozessoren gibt es noch RdRand. Jede Menge Möglichkeiten echten Zufall zu bekommen und Werte, die nicht zeitlich korrelieren.

Du kannst auch den Seed der Hashfunktion von der CPU aus für jeden Frame verändern. Wenn der Wert aber um den gleichen Faktor inkrementell steigt, ist der Trick icht "Seed0 + Seed1 + Seed2" zu machen, weil es sonst wieder diagonale Wellen gibt. Wenn Seed0 eins kleiner wird und Seed1 eins größer, kommt der gleiche Wert raus. Besser wird es, wenn man die Bits gegeneinander verschiebt:
"Seed0 + ((Seed1 >> 10) + (Seed1 << 22)) + ((Seed2 >> 21) + (Seed2 << 11))"

Autor:  Vinz [ Sa Apr 11, 2015 14:36 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Zitat:
Was ist den TFFB? Davon habe ich noch die gehört.

Transform Feedback, damit kann man aus dem Vertexshader heraus VBOs schreiben, und somit z.B. die Vertexdaten zeitlich entwickeln lassen, somit könnte man ja auch einen seed für jeden Vertex fortlaufen ändern.

Wie funktioniert das mit der Hashfunktion im Shader, so dass man für den selben Input immer andere Werte bekommt bei gleicher Rechenvorschrift (kann mir das Softwaretechnisch grad nicht vorstellen, hängt es mit der Hardware zussammen), und so dass sie tatsächlich gleichverteilt sind? Sind sie das wirklich? Und wie groß ist die Periode?

Autor:  OpenglerF [ Sa Apr 11, 2015 15:01 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Zitat:
Transform Feedback, damit kann man aus dem Vertexshader heraus VBOs schreiben, und somit z.B. die Vertexdaten zeitlich entwickeln lassen, somit könnte man ja auch einen seed für jeden Vertex fortlaufen ändern.

Achso. Ich habe nie mit Transform Feedback gearbeitet. Compute Shader haben es irgendwie abgelöst. :wink:
In der Tat könnte man damit jeden Input Vertex einen eigenen Seed geben und den denn dann nach einem klassischen Verfahren verarbeiten. Nachteil ist, dass es zusätzlichen Speicheraufwand bringt. Außerdem musst du erstmal von irgendwo sehr viel Zufall bekommen, um alle die Zustände jeden einzelnen Vertexes zu initialisieren.

Zitat:
dass man für den selben Input immer andere Werte

Das stimmt eben nicht ;-) . Man hat anderen Input. Zum Beispiel eine andere VertexID. Die ID ist für jeden Vertex anders. Sie ist nur nicht zufällig. Durch die Hash-Funktion bildet man diese langweilige gleichmäßig steigende Zahl auf eine Ausgabe ab, die sich scheinbar zufällig ändert, wenn man den Input auch nur um 1 erhöht.

Zitat:
und so dass sie tatsächlich gleichverteilt sind?

Diese Frage kann ich leider schwer theoretisch belegen. Die Quelle der Funktion(siehe mein Link) behauptet, sie wäre gleichverteilt und das getestet zu haben. Da ich keinen Grund sehe, warum das nicht der Fall sein sollte, glaube ich das einfach mal.

Zitat:
Und wie groß ist die Periode?

Bei einer vernümpftigen Hash Funktion so groß wie die Eingabe. Also so im Bereich von 2^32, wobei sich das ja erhöhen lässt, indem man vorher eine zufällige Zahl hinzuaddiert die man "ab und zu" von der CPU aus ändert.

Autor:  Lord Horazont [ Sa Apr 11, 2015 16:12 ]
Betreff des Beitrags:  Re: Noiseerzeugung im Shader: Textur oder Berechnung

Vinz hat geschrieben:
@Horazont
Ich bin von komplexeren Berechnungen für die Zufallszahlen ausgegangen, aber gut zu wissen, dass man mit rand() locker eine Million Werte pro Frame bekommt, man muss sie aber auch noch in den VRAM laden.

Das sollte kein Problem darstellen, denke ich. Ich hab neulich ein 2k×2k (4 Mio Punkte) Terrain gerendert und dabei die Heightmap (als 32bit floats) in jedem Frame über den Bus geschoben. Das lief auf meiner mini-nvidia (GT 520) noch flüssig ;). V.a. wenn du nur ein Viertel der Textur pro Frame überträgst solltest du da noch viel Spielraum haben.

viele Grüße,
Horazont

Seite 1 von 1 Alle Zeiten sind UTC + 1 Stunde
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/