Make some noise

by nex on 12/13/04, 1:57 PM in implementierung

Die Algorithmen zur Erzeugung von Perlin Noise sind implementiert und getestet. Jetzt geht es darum, sie auch sinnvoll einzusetzen, daher an dieser Stelle ein paar Anmerkung zu deren Verwendung.


CloudGenerator.getCloud() liefert ein ein-dimensionales Array, das konzeptionell aber drei-dimensional ist und sämtliche Frames einer Rauchwolken-Animation enthält. Die aktuelle Version der NoiseEffectSource demonstriert, was man damit tut. Ihr solltet das mit dem runtest-Task ausprobieren oder ein Video mit einer sehr geringen Auflösung verwenden, um zu vermeiden, dass euch der Speicher ausgeht. Die 3D-Voxel-Volumes brauchen nämlich ziemlich viel Speicher. In dieser Form ist der CloudGenerator eigentlich dazu gedacht, kleine Rauchwölkchen als Sprites für ein Partikelsystem zu generieren.

Weitere Vorgehensweise

Nachdem der Effekt schon ganz hübsch aussieht, wäre es auch denkbar, dass wir uns den Aufwand mit dem Partikelsystem sparen und einfach eine solche Animation direkt als Alpha-Kanal für die Rauch-Farbe verwenden. Man könnte dabei so tun, als ob die Animation ein nahtloser Loop wäre, und zwar auch in der x- und y-Richtung, sodass man das Bild scrollen kann, z.b. nach oben, um aufsteigenden Rauch zu simulieren. Wenn die Animation größer als das Video-Frame ist und ständig verschoben wird, entsteht nicht so schnell der Eindruck, dass sie sich ständig wiederholt. (Das mit dem nahtlosen Loop geht jetzt noch nicht, aber ich kann es bei Bedarf jederzeit mit wenig Aufwand nachimplementieren.) Dann müsste man also eine größere Rauch-Animation vorberechnen und als Video speichern (unkomprimiert würde sie viel zu viel Speicherplatz benötigen) und einen Overlay-Effekt verwenden, der dieses Video über das Original-Bild legt. Mag das jemand von euch machen? (C.f. .plan.)

Er beißt nicht, er will nur, dass du mit ihm spielst.

Ich habe den neuen Code aus Zeitgründen leider noch nicht vollständig dokumentiert. Bitte zögert nicht, mich sofort zu kontaktieren, wenn ihr auf mangelhafte JavaDoc stoßt.

Noch ein paar erklärende Worte zur Generierung des Perlin-Noise: Die Oktaven mit niedrigeren Frequenzen werden erzeugt, indem ein dementsprechend kleines VoxelVolume erzeugt und auf die Größe des zu generierenden VoxelVolume aufgeblasen wird. Beim Vergrößern werden die neuen Werte durch Interpolation berechnet. In VoxelVolume.getEnlargedVolume() stehen vier verschiedene Funktionen zur Berechnung der Interpolations-Funktion (drei davon auskommentiert).

Falls ihr euch mit dem Rendern des Effekts spielen wollt, um das Bild (nach ästhetischen Gesichtspunkten) zu verbessern, könnt ihr hier zunächst die verschiedenen Funktionen durchprobieren. Wenn man sich nur eine Oktave auf einmal anzeigen lässt, sieht das Bild trotz meiner Bemühungen, die gute Funktion zu finden, noch immer etwas 'eckig' aus. Das lässt sich verbessern, indem man noch einen Weichzeichner darüber laufen lässt. Eine entsprechende Code-Zeile in CloudGenerator.generateCloud() ruft hierzu die Methode blur() auf. Ich habe das vorerst auskommentiert, da es im Gesamtbild nicht so sehr ins Gewicht fällt; ist aber auch eine Möglichkeit zum Experimentieren.

Ein weiterer beliebig gewählter 'Fudge Factor' ist die Variable octaveRatio an dieser Stelle. Sie regelt, wie stark benachbarte Oktaven in Relation zueinander ins Gewicht fallen. Der aktuelle Wert von 1,2 bedeutet beispielsweise, dass die erste Oktave (hat ca. die halbe Frequenz der 0ten Oktave) 1,2 mal so stark ins Endergebnis einfließt wie die 0te Oktave, und nur 1/1,2 mal so stark wie die 2. Oktave. Auf diese Weise werden die ganz hohen Frequenzen etwas unterdrückt; ansonsten würde das Bild zu sehr 'rauschen', was eher nach Bildstörung als nach Rauch aussieht.

Also normal ist das nicht

Die letzte Verbesserung, die ich gestern noch vorgenommen habe, betrifft den Kontrast der generierten Wolke. Nachdem ich die einzelnen Oktaven wie oben beschrieben addiert hatte, stellte ich fest, dass die Normalisierung der Werte in den durch ein Byte abgedeckten Bereich nicht sehr gut funktioniert. Ein Histogramm (Diagramm der Dichte der Verteilung) sah z.b. so aus:

Die Werte decken im Großen und Ganzen nur ca. die Hälfte des Wertebereichs ab. Eine genaue Betrachtung des Histogramms fördert die Ursache zu Tage: Kleine Anhäufungen von Ausreißer-Werten an den extremen Enden des Histogramms verhindern, dass der 'Berg' in der Mitte besser über den Wertebereich verteilt wird.

Um dem abzuhelfen, habe ich gestern zusätzlich zu der normalise()-Methode noch contrastStretchNormalise() implementiert. Nach deren Aufruf sieht der Alpha-Kanal besser aus und es gibt nun tatsächlich Bereiche im Bild, an denen der Rauch voll oder auch gar nicht sichtbar ist (statt nur ein bisschen, ein bisschen mehr oder noch ein bisschen mehr).



 |