0t1 steckt noch in den Kinderschuhen.

Interaktives Partikel-System

Abstraktes interaktives Partikel-System mit p5.js

Ziel der Übung

Mithilfe dieses Tutorials kannst du ein animiertes Kunstwerk aus sich bewegenden Partikeln in p5.js generieren. Die Partikel werden dabei zufällig auf der Leinwand positioniert und bewegen sich dann auf der Grundlage von Perlin-Noise. Zusätzlich lernst du, wie du die Geschwindigkeit und Schärfe der Partikel durch Drücken von Tasten erhöhen und den Farbverlauf ändern kannst. 

Folgende Punkte beinhaltet das Tutorial:

  • Partikelerstellung
  • Farb- und Geschwindigkeitsanpassung
  • Interaktivität

Bevor wir beginnen, schauen wir uns eine Vorschau an, was wir am Ende des Tutorials erreichen werden: 

p5.js

Zunächst einmal, was ist P5.js? P5.js ist eine JavaScript-Bibliothek, die es uns ermöglicht, interaktive und animierte Grafiken im Webbrowser zu erstellen.

Um loszulegen, öffne den p5.js-Editor. Dort kannst du die voreingestellten Vorgaben löschen.

(Der gesamte Quellcode ist übrigens am Ende des Tutorials verlinkt.)

Schritt 1: Variablen initialisieren 

Zuerst initialisieren wir einige Variablen: 

let particles = []; 
const num = 1000; //Ändert die Anzahl der Partikel 
const noiseScale = 0.01 / 2; 
let sharpness = 1; 
let speed = 1; 
let colorGradient = [ 
  [254, 67, 101], 
  [252, 157, 154], 
  [249, 205, 173], 
  [200, 200, 169], 
  [131, 175, 155], 
]; 
 
let keyPressedTime = 0; 
let randomness = 0.2; 
let monochrome = false;
  • particles ist ein Array von Vektoren, die die aktuelle Position jedes Partikels speichern
  • num gibt an, wie viele Partikel erstellt werden sollen
  • noiseScale ist ein Faktor, der die Rauheit des Perlin-Noise bestimmt
  • sharpness bestimmt, wie stark der Perlin-Noise die Bewegungsrichtung der Partikel beeinflusst
  • speed gibt an, wie schnell sich die Partikel bewegen sollen
  • colorGradient ist ein Array von Farben, die den Farbverlauf der Partikel bestimmen
  • keyPressedTime ist der Zeitpunkt, zu dem die letzte Taste gedrückt wurde, um die Geschwindigkeit und Schärfe der Partikel zu erhöhen
  • randomness gibt an, wie oft die Bewegungsrichtung der Partikel zufällig geändert werden soll
  • monochrome gibt an, ob der Farbverlauf monochromatisch sein soll. 

Schritt 2: setup-Funktion

Die setup Funktion wird einmal ausgeführt, wenn das Programm gestartet wird. Sie initialisiert das Canvas createCanvas(), auf der wir unsere Partikel zeichnen können. In diesem Beispiel verwenden wir ein 800x400 Pixel großes Canvas.

Zudem generiert die setup Funktion num viele Partikel an zufälligen Koordinaten innerhalb des Canvas. Hierzu werden die Teilchenarrays mithilfe einer for-Schleife initialisiert. Die FunktioncreateVector() wird verwendet, um eine zufällige Startposition createVector(random(width), random(height)) für jedes Teilchen zu generieren. Dann werden diese Positionen durch particles.push(particlePos) den particles Array hinzugefügt.

function setup() {
   createCanvas(800, 400);
  for (let i = 0; i < num; i++) {
    let particlePos = createVector(random(width), random(height));
    particles.push(particlePos);
  }
}

Schritt 3: onScreen-Funktion

Die Funktion onScreen(v) prüft, ob ein übergebener Vektor auf dem Bildschirm dargestellt wird, und gibt true zurück, wenn er angezeigt wird, andernfalls gibt sie false zurück. Diese Funktion benötigst du, um die Partikel zu sehen, die im nächsten Schritt erstellt werden.

function onScreen(v) {
  return v.x >= 0 && v.x <= width && v.y >= 0 && v.y <= height;
}

Schritt 4: draw-Funktion

Die Funktion draw() wird kontinuierlich ausgeführt und ist für das Zeichnen auf der Leinwand verantwortlich. Bevor wir unsere Partikel jedoch zeichnen können, benötigen wir einen Hintergrund. Wir können das mit der background() Funktion tun. In diesem Beispiel verwenden wir einen schwarzen Hintergrund mit einer Transparenz von 10, um einen leicht verwischten Effekt für die sich bewegenden Partikel zu erzeugen.

function draw() {
  background(0, 10);
  for (let i = 0; i < num; i++) {
    let p = particles[i];
    let alpha = map(i, 0, num, 0, 255);
    let color;

    if (monochrome) {
      let red = map(p.x, 0, width, 0, 255);
      color = [255, 0, red, alpha];
    } else {
      let index = map(p.x, 0, width, 0, colorGradient.length - 1);
      let t = index % 1;
      let c1 = colorGradient[floor(index)];
      let c2 = colorGradient[ceil(index)];
      color = [
        lerp(c1[0], c2[0], t),
        lerp(c1[1], c2[1], t),
        lerp(c1[2], c2[2], t),
        alpha,
      ];
    }

Danach erstellen wir eine for-Schleife, die für jedes Partikel in der particles-Liste ausgeführt wird. let p = particles[i] ruft das aktuelle Partikel aus der Liste ab und let alpha = map(i, 0, num, 0, 255) berechnet die Transparenz des Partikels basierend auf seiner Position in der Liste. if (monochrome) { ... } else { ... } wiederum entscheidet, ob das Partikel monochrom oder farbig gezeichnet werden soll. Wenn monochrome wahr ist, wird die Farbe des Partikels in Abhängigkeit von seiner x-Position berechnet. Andernfalls wird die Farbe des Partikels basierend auf einem Farbverlauf colorGradient berechnet.

Schritt 5: Partikel in der Schleife

Als Nächstes lernst du, was mit jedem Partikel in der Schleife passiert, während der Sketch läuft.

stroke(color);
    strokeWeight(6);
    ellipse(p.x, p.y, 6, 6);

    let n = noise(
      p.x * noiseScale,
      p.y * noiseScale,
      frameCount * noiseScale * noiseScale
    );
    let a;
    let randomMovement = random() < randomness;
    if (randomMovement) {
      a = random(TAU);
    } else {
      a = TAU * n * sharpness;
    }
    p.x += cos(a) * speed;
    p.y += sin(a) * speed;
    if (!onScreen(p)) {
      p.x = random(width);
      p.y = random(height);
    }
  }
  • stroke(color) setzt die Konturfarbe des Partikels
  • strokeWeight(6) setzt die Konturstärke des Partikels auf 6
  • ellipse(p.x, p.y, 6, 6) zeichnet das Partikel als Ellipse an der Position (p.x, p.y) mit einer Breite und Höhe von 6
  • let n = noise(p.x noiseScale, p.y noiseScale, frameCount noiseScale noiseScale) berechnet einen Perlin-Geräuschwert basierend auf der Position des Partikels und der aktuellen Anzahl der gezeichneten Frames
  • let randomMovement = random() < randomness entscheidet, ob das Partikel eine zufällige Bewegung oder eine geräuschbasierte Bewegung haben soll, basierend auf dem Wert von randomness
  • if (randomMovement) { ... } else { ... } wenn randomMovement wahr ist, wird eine zufällige Richtung für das Partikel gewählt. Andernfalls wird die Richtung basierend auf dem Perlin-Geräuschwert berechnet
  • p.x += cos(a) * speed aktualisiert die x-Position des Partikels basierend auf der gewählten Richtung und Geschwindigkeit
  • p.y += sin(a) * speed aktualisiert die y-Position des Partikels basierend auf der gewählten Richtung und Geschwindigkeit

Schritt 6: Interaktion einbauen

Der folgende Code überprüft, ob eine Taste auf der Tastatur gedrückt wird. Wenn dies der Fall ist und die Zeit seit dem letzten Tastendruck mindestens 200 Millisekunden beträgt if (millis() - keyPressedTime > 200), wird die Geschwindigkeit der Partikelanimation erhöht speed += 0.5 und die Schärfe der Partikelbewegung basierend auf der gedrückten Taste eingestellt sharpness = map(keyCode, 0, 255, 20, 30).

Es gibt auch Bedingung für das Aktivieren des monochromen Modus, basierend auf der gedrückten Taste. In diesem Fall ändern sich die Farben beim Drücken der Tasten 1, 2, 3, 4 und 5 auf deiner Tastatur.

Wenn keine Taste gedrückt wird, wird die Geschwindigkeit auf einen Standardwert von 2 und die Schärfe auf einen Standardwert von 0.1 gesetzt. 

if (keyIsPressed) {
    if (millis() - keyPressedTime > 200) { //je kleiner die Zahl desto schneller reagieren 
                                          //die Partikel auf Tastendruck
      speed += 0.5; //Ändert die Geschwindigkeit der Partikel
      sharpness = map(keyCode, 0, 255, 20, 30);

      if (key === "1") {
        monochrome = false;
      } else if (key === "2") {
        monochrome = false
      } else if (key === "3") {
        monochrome = false;
      }else if (key === "4") {
        monochrome = false;
      }else if (key === "5") {
        monochrome = false;
      }

      keyPressedTime = millis();
    }
  } else {
    speed = 0.5; //Ändert die Geschwindigkeit der Partikel
    sharpness = 0; //Ändert die Bewegungsrichtung der Partikel
  }
}

Schritt 7: Reaktionen bei Interaktion

Darauffolgend bauen wir die Reaktionen ein, die beim Drücken einer Taste ausgelöst werden sollen.

Wenn die Leertaste gedrückt wird if (key === " ") {, wird der noiseSeed (Rauschen) neu gesetzt, was zu einer völlig neuen visuellen Darstellung führt. Wenn eine der Zahlen 1-5 gedrückt wird, wird ein bestimmter Farbverlauf aus der Arrayliste ausgewählt und als Hintergrundfarbverlauf verwendet.

function keyPressed() {
  if (key === " ") {
    noiseSeed(millis());
  } else if (key === "1") { //Verlauf pastel
    monochrome = false;
    colorGradient = [
      [254, 67, 101],
      [252, 157, 154],
      [249, 205, 173],
      [200, 200, 169],
      [131, 175, 155],
    ];
  } else if (key === "2") { //Verlauf orange zu rot
    monochrome = false;
    colorGradient = [
      [255, 165, 0],
      [255, 128, 0],
      [255, 69, 0],
      [255, 0, 0],
      [191, 0, 0],
    ];
  } else if (key === "3") { //Verlauf blau zu violett
    monochrome = false;
    colorGradient = [
      [0, 0, 255],
      [63, 0, 255],
      [127, 0, 255],
      [191, 0, 255],
      [255, 0, 255],
    ];
  }else if (key === "4") { //Verlauf grün zu gelb
    monochrome = false
    colorGradient = [
      [0, 255, 0],
      [127, 255, 0],
      [191, 255, 0],
      [255, 255, 0],
      [255, 191, 0],
    ];
  
  }else if (key === "5"){ //Verlauf türkis zu blau
    monochrome = false;
    colorGradient = [
      [0, 200, 255],
      [0, 150, 255],
      [0, 100, 255],
      [0, 50, 255],
      [0, 0, 255]
    ];
}
 if (key === 'ä') { //Dient zum Speichern als Bild
    saveCanvas('meinBild', 'png');
  }
}

Jeder Farbwert ist selbst ein Array mit drei Elementen, die jeweils die Rot-, Grün- und Blaukomponenten der Farbe darstellen. Wenn du dir den Farbcode von(key === "3") anschaust siehst du, dass der Farbverlauf mit reinem Blau [0, 0, 255] beginnt, zu Rosa/Lila [63, 0, 255] übergeht, dann zu einem helleren Farbton von Rosa/Lila [127, 0, 255] wechselt, dann zu Magenta [191, 0, 255] und schließlich mit reinem Magenta [255, 0, 255] endet.

Schlussfolgerung

In diesem Tutorial hast du gelernt, wie du mit p5.js einen einfachen, aber beeindruckenden Partikeleffekt erstellen kannst. Du hast erfahren, wie du einen Array von Partikeln erstellst und sie auf dem Canvas animierst. Dabei hast du auch die Verwendung von Farbverläufen kennengelernt, um den Effekt noch ansprechender zu gestalten. Neben der Erstellung eines Partikeleffekts hast du auch gelernt, wie man Interaktionen hinzufügt, um den Sketch dynamischer zu gestalten.

Wir hoffen, dass du dieses Tutorial hilfreich fandest und nun in der Lage bist, eigene Partikeleffekte zu erstellen.

Quellcode

Hier findest du den gesamten Quellcode im p5.js Editor. Viel Spaß beim Experimentieren! 

Das Tutorial basiert auf diesem Video.


© 0t1

Cookies

0t1 mag keine Kekse (und kein Tracking). Wir verwenden lediglich notwendige Cookies für essentielle Funktionen.

Wir verwenden Schriftarten von Adobe Fonts. Dafür stellt dein Browser eine Verbindung zu den Servern von Adobe in den USA her. Wenn du unsere Seite nutzen möchtest, musst du dich damit einverstanden erklären.

Weitere Informationen in unserer Datenschutzerklärung.