GMU:Einführung ins Programmieren mit Processing/final/Metaballs

From Medien Wiki

Metaballs

Idee

Mein eigentliches Ziel war es, etwas an sich „Lebendes“ zu schaffen. Dabei wollte ich eigentlich das klassische Räuber-Beute-Modell implementieren. Voraussetzung dafür ist eigentlich ein gutes Grundset an Variablen, woraus sich dann Räuber und deren Beute entwickeln sollte, und Zeit für diese Entwicklung. Somit war dies sehr aufwendig, so dass ich mich dafür entschied dies zu verallgemeinern und nur den Prozess des „Räuber frisst Beute“ realisieren wollte. Dabei stieß ich auf Metaballs.

Im Allgemeinen kann man sagen, dass Metaballs Kugeln( in 2D Kreise) sind, die, desto näher sie sich kommen, miteinander verschmelzen. Das ist ein sehr schöner Effekt, der sich dafür eignet um zum Beispiel den Eindruck zu Erwecken, dass Zellen miteinander verschmelzen und sich danach wieder teilen. Somit war die Grundidee gefunden, deren Implementierung nur noch auf mich wartete.

Metaballs

Die Berechnung in meinem Projekt für die Metaballs erfolgt pixelweise. Dabei besitzt jeder Metaball eine Position auf dem Fenster, für die Breite X von -1 bis 1 und für die Höhe Y ebenso. Nun wird für jeden Pixel die Quadratische Entfernung zu allen Metaballs berechnet. Um jetzt feststellen zu können ob an dem Pixel ein Metaball vorhanden ist oder nicht, wird der Kehrwert die der Quadratischen Entfernung zu diesem Pixel berechnet. Versieht man dies noch mit einer Potenzierung, wird Kantendarstellung der Metaballs intensiviert.

Als eine Art Pseudocode sieht das so aus:

Für alle Pixel X, Y {
  Für Alle Metaballs {
    Farbe = (|(PosX-X)| * |(PosY-Y)| * Größe)-1;
    // Die nächste Zeile ist optional und eingebaut in meinem Projekt, 
    //dafür muss einfach nur die Taste `d´ gedrückt werden.
    Farbe *= Farbe;
    FinalFarbe += Farbe;
  }
  Pixel X, Y = FinalFarbe
}

Mit dieser einfachen Formel kann man schon sehr gut Metaballs darstellen. Da dies aufgrund vielen Multiplikationen in jedem Pixel pro Frame sehr rechenintensiv ist, wäre die Berechnung auf GPU Ebene sinnvoll, was ich leider nicht implementiert habe. Im Internet habe ich auch noch ein paar Optimierungen gefunden um ein paar Berechnungen vorauszulagern, zum Beispiel das Berechnen der Entfernungen.


Struktur des Programms

Zur Struktur ist so viel zu sagen, als dass ich 3 Klassen erstellt habe:

  • Surface, welche die draw()-Funktion enthält und die Pixel auf Metaballs hin analysiert,
  • MetaballManager, der für jeden Metaball eine gewünschte Funktionen aufrufen kann,
  • Metaball, welcher seine eigene Bewegung berechnet.

Weiterhin habe ich die library „ControlP5“ benutzt um eine GUI erstellen zu können.

Weil wie gesagt die Berechnung für jeden Pixel erfolgt, habe ich einen einfachen Trick benutzt um die Anzahl der Berechnungen zu reduzieren bei gleicher Bildgröße. Dabei erfolgt die Berechnung auf einem kleineren Bild fester Größe, welches danach einfach als vergrößertes Bild in Processing dargestellt wird.


 

Jeder Metaball hat die Möglichkeit einen anderen zu fressen, wenn sie sich nah genug sind, sofern sie alle gleich groß sind. Doch hier gilt das Gesetz: Ist der andere größer, wird man gefressen. Danach schwimmt der Gefressene solange im Räuber bis er sich wieder nach draußen frei gekämpft hat. Das schafft er erst, sobald er eine gewisse Distanz vom Ursprung des Räubers überwunden hat. Kommt es dazu das ein Räuber jemanden gefressen hat, dann wächst er um einen bestimmten Faktor, kann somit rein theoretisch mehr fressen, wird im Gegenzug aber auch langsamer. Somit entsteht ein gutes Gleichgewicht im System.

Weiterhin bleiben alle Metaballs im Fenster, weil eine einfache Kollisionsabfrage mit dem Rand eingebaut ist.

PROGRAM

File:Processing einfuehrung alexander.zip