0t1 steckt noch in den Kinderschuhen.

Abstrakte 3D Rasterung in Processing

Bilder abstrakt dreidimensional und interaktiv darstellen

Ziel des Tutorials

Mithilfe dieses Tutorials erklären wir dir Schritt für Schritt, wie du Bilder im dreidimensionalen Raum rastern und abstrakt darstellen kannst. Durch das Drücken verschiedener Tasten und das Ziehen mittels der Maus kann das Bild gestalterisch verändert und individualisiert werden. Zusätzlich zeigen wir dir, wie du ein Foto von dir selbst mithilfe deiner Webkamera in das Programm einbindest.

Was ist Processing?

Processing ist eine JavaScript-Bibliothek, spezialisiert auf Grafik, Simulation und Animation. Um es nutzen zu können, musst du das Programm erst auf deinen Rechner runterladen. Download

Processing eignet sich für dieses Tutorial gut, da es umfangreiche Einstellungsmöglichkeiten für den dreidimensionalen Raum bietet. 

Nachdem du das Programm öffnest, wird dir ein leerer Sketch angezeigt. Keine Sorge, in den nächsten Schritten erklären wir dir genau, wie du für dieses Tutorial das Programm füllen sollst. 

Falls du dich davor lieber selbst mit dem Programm auseinandersetzen möchtest, wirf ein Blick auf die examples und reference.

Bild laden

Die setup()-Funktion wird nur einmal bei Start des Programms ausgeführt. Sie wird verwendet um Grundbedingungen wie die Bildschirmgröße, oder das Laden von Bildern oder Schriften festzulegen. Mittels dem Rendermodus P3d wird dieses sketch in ein 3-dimensionales umgewandelt, um sowohl die x-, y-, als auch z-Achse nutzbar zu machen.

Anfangs wird ein PImage Objekt, welches ein Bild darstellt erzeugt. PImage ist ein Datentyp innerhalb der Processing Software welcher für das Speichern von Bilddateien wie png, gif oder jpg zuständig ist.

Bevor ein Bild verwendet werden kann muss es mittels der loadImage-Funktion geladen werden. Das zu verwendete Bild muss hierbei im Projektordner gespeichert sein. Die resize-Methode skaliert da bild auf die vorgegebene Bildgröße, bzw. ändert sie die Höhe und Breite des Bildes dementsprechend.

PImage img;

void setup() {
  size(800, 800, P3D); //Bildschirmgröße, Rendermodus
  img = loadImage("1.jpg");
  img.resize(800,0); // 0 an 2. Stelle um das Seitenverhältnis beizubehalten
}

Rasterung

Die nächsten Schritte erfolgen inenrhalb der void draw()-Funktion, welche kontinuierlich ausgeführt wird.

Für die Rasterung erstellen wir zunächst die Variable "tiles", welches die Anzahl der Rasterkästchen beschreibt. Mit "tileSize" wird die Größe der Rasterkästchen berechnet, indem die Anzahl durch die Breite geteilt wird. Width bezieht sich hier immer auf die Bildschirmgröße.

background() legt die Hintergrundfarbe fest.

void draw(){

  float tiles = 300; // Anzahl Rasterkästchen bzw. Detailgrad
  float tileSize = width/tiles; //Größe des einzelnen Rasterkästchens

  background(#FFFFFF);//Weißer Hintergrund

Geschachtelte For-Schleife

Die erste for-Schleife durchläuft die Kästchen auf der x-Achse, während die zweite for-Schleife die Kästchen auf der y-Achse repräsentiert. Innerhalb der Schleife wird nun das Raster durchlaufen.

Um die Helligkeit der jeweiligen Pixel an der jeweiligen Rasterstelle zu erfragen benötigen wir zunächst einmal die Farbe, welches du mit der color-Variable, hier color c erfragen kannst. Mit img.get ist es möglich die Farbe eines Bildpixels zu erhalten. Um den Pixel an genau der Stelle der Rasterkästchen zu erfragen wird x und mit der tileSize multipliziert. Da tileSize jedoch eine float-Variable ist und nicht wie x und y ein Integer, wird dieses vorher konvertiert.

Im nächsten Schritt möchten wir herausfinden wie hell die Farbe der Variable color c ist. brightness() gibt den Helligkeits wert der zugehörigen Farbe wieder.

Die push()- und pop()-Funktionen ermöglichen eine genauere Transformation durch das Erstellen eines Koordinatensystems.

Die translate(x,y,z)-Funktion gibt einen Betrag zum Verschieben von Objekten innerhalb des Anzeigefensters an. Um den z-Parameter ist das Verwenden von P3D, wie vorher festgelegt, nötig. Mit der translate-Funktion wird das Bild hier innerhalb des Koordinatensystems zentriert, indem die x-Position*tileSize mit der Breite/2, da sich hier die Mitte befindet, bzw. Höhe des Fensters subtrahiert wird.

Mit sphere(radius) werden Sphären erstellt. Die Größe der Sphäre ändert sich basierend auf den Helligkeitswerten der Pixel. Da die Sphären etwas zu groß erscheinen multiplizieren wir den Wert einfach mit 0,8 um die Größe etwas zu verringern.

Bei gedrückter Maustaste soll sich die Verteilung der Sphären auf der z-Achse ändern, hierfür legen wir die Variable z fest. Die map(value, start1, stop1, start2, stop2 )-Funktion ordnet eine Zahl von einem Bereich zu einem anderen neu zu. value beschreibt den erwarteten Wert der neu berechnet werden soll, während start und stop jeweils die untere, bzw. die obere Grenze des Wertebereichs festlegen. Die ersten Werte legen den Startwertebereich fest und die zweiten Werte den Zielbereich. Die z-Positionierung ändert sich also basierend auf den Helligkeitswerten der jewiligen Pixel im Bereich von -100 und 100. Je dunkler der Wert, desto geringer die Position auf der z-Achse.

Ist die Maustaste nicht gedrückt, ist die z-Positionierung fest.

for (int x = 0; x < tiles; x++){
    for (int y = 0; y < tiles; y++) {
      color c = img.get(int(x*tileSize),int(y*tileSize)); //Farbe der Pixel
      float b = map(brightness(c),0,255,1,0); //Helligkeitswert
      
      if (mousePressed){ //wenn Maus geklickt, verteilen sich die "Partikel" basierend auf der Maus-Position
      float z = map(b,0,1,mouseX/2,mouseY/2);
     
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z); //Positionieren des Bildes im Zentrum
      sphere(tileSize*b*0.8);
      pop();
      }
      else { //wenn Maus NICHT gedrückt wird, werden die "Partikel" gleichmäßig verteilt
        float z = map(b,0,1,-100,100);
     
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.8);
      pop();
      }


    }
  }

Die Sphären sollen keine Kontur haben und den Detail der Sphären kannst du auch anpassen. sphereDetail() steuert das Detail, das zum Rendern einer Kugel verwendet wird, indem die Anzahl der Scheitelpunkte des Kugelnetzes angepasst wird.

Mit rotateY(winkel) wird eine Form an der y-Achse gedreht.

noStroke();
  sphereDetail(0);

rotateY(radians(frameCount));

Steuerung

Die Steuerung lässt sich beliebig erweitern oder ändern und nicht nur die Verteilung der Partikel anhand der Mausposition:

Zum Beispiel lassen sich die Farben der Sphären ändern. Bei Drücken der Leertaste färben sich die Sphären schwarz und sie werden sichtbar.

if (keyPressed && key ==' '){
    fill(#000000);
  }

So werden bei Drücken der r-Taste auf der Tastatur die Sphären rot, oder demenstprechend grün oder blau.

if (keyPressed && key =='r'){ //rot 
    fill(#FF0000);
  }
  if (keyPressed && key =='g'){ //grün
    fill(#008800);
  }
  if (keyPressed && key =='b'){ //blau
    fill(#021BF9);
  }

Auch das Bild kannst du ändern:

if (keyPressed && key =='1'){
    img = loadImage("1.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='2'){
    img = loadImage("2.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='3'){
    img = loadImage("3.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='4'){
    img = loadImage("4.jpg");
    img.resize(800,0);
  }
  
    if (keyPressed && key =='5'){
    img = loadImage("5.jpg");
    img.resize(800,0);
  }

Ein Foto von dir in das Programm einbinden

Wir setzten uns das Ziel für dieses Tutorial eine Art Live-Kamera-Funktion einzubinden, sodass der Nutzer sich Live in dem Programm projizieren lassen kann. Wir stießen hier auf Probleme und schafften es leider nicht, die perfekte Lösung zu finden. Jedoch umgingen wir das Problem, indem wir ein weiteres Programm schrieben, welches dir ermöglicht ein Foto von dir selbst zu schießen und schließlich im 3D-Programm angezeigt werden kann.

Bevor du damit anfängst den Code zu schreiben, musst du erst die nötige Library runterladen, welche in Processing enthalten ist. Dafür gehst du zuerst auf Sketch, Library importieren und Manage Libraries. Hier gibst du ins Suchfeld „Video Processing 4“ ein und lädst dir die Library runter.

Auch musst du ein Objekt vom Typen Capture deklarieren, um auf die Webcam auf deinem Computer zuzugreifen.

import processing.video.*;
  
Capture webcam;

Die Funktion setup() muss das Capture-Objekt mit den benötigten Einstellungen initialisiert werden, um mit der Erfassung der Kamera beginnen zu können. In der Funktion draw() wird dann das Bild der Webkamera auf den Bildschirm gezeichnet.

void setup()
{
  size(640, 480);
  smooth();
    
  webcam = new Capture(this, width, height, 30);
  webcam.start();
}

void draw()
{
  background(#FFFFFF);
  image(webcam, 0, 0);
}

Die Funktion captureEvent() wird jedes Mal aufgerufen, wenn ein neuer Frame von der Webcam verfügbar ist. Innerhalb dieser Funktion musst du die Methode webcam.read() aufrufen, um das Webcam-Bild stetig zu aktualisieren.

void captureEvent(Capture webcam)
{
  webcam.read();
}

Schlussendlich sorgt die Funktion void keyPressed() dafür, dass bei Drücken der c-Taste ein Bild geschossen wird, welches dann auf den Ordner gespeichert wird und vom Hauptprogramm aufgerufen werden kann.

Übrigens: Achte beim Foto schießen auf einen weißen Hintergrund, der 3D-Effekt funktioniert so am besten.

void keyPressed () { //Screenshot
  if (key =='c') {
    save("5.jpg");
  }
}

Hier musst du auch auf die richtige Ordnerstruktur achten, damit das Hauptprogramm auf das Bild zugreifen kann. (Processing-Datei "tutorial" befindet sich im Überordner gemeinsam mit den Bildern, Screenshot-Unterordner und Unterordner mit Processing-Datei "camera")

Nun kannst du wieder in das Hauptprogramm gehen (ggf. Neustart nötig) und die Taste 5 drücken, um das Bild von dir anzeigen zu lassen.

Screenshots speichern

Um unser abstrahiertes Bild zu speichern erstellen wir die Funktion keyPressed(), in welcher wir bei Drücken der s-Taste auf der Tastatur, das Bild speichern können. Save("Speicherort") erstellt einen Screenshot des Fenster und speichert ihn im vorgegebenen Speicherort.

void keyPressed () { //save screenshot
  if (key =='s') {
    save("screenshots/tutorial.jpg");
  }
  }

Quellcode

Download (Kompletter Ordner)

Tutorial

PImage img;

void setup() {
  size(800, 800, P3D); //Erstellt eine Zeichenfläche im 3D Raum
  img = loadImage("1.jpg");
  img.resize(800,0);
}

  void draw() {
  background(#FFFFFF);
  if (keyPressed && key ==' '){ //schwarz bzw. Programm starten
    fill(#000000);
  }
  if (keyPressed && key =='r'){ //rot 
    fill(#FF0000);
  }
  if (keyPressed && key =='g'){ //grün
    fill(#008800);
  }
  if (keyPressed && key =='b'){ //blau
    fill(#021BF9);
  }
  
  if (keyPressed && key =='1'){ //lädt Bild 1
    img = loadImage("1.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='2'){ //lädt Bild 2
    img = loadImage("2.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='3'){ //lädt Bild 3
    img = loadImage("3.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='4'){ //lädt Bild 4
    img = loadImage("4.jpg");
    img.resize(800,0);
  }
  if (keyPressed && key =='5'){ //lädt Bild 5 bzw. eigenes Bild
    img = loadImage("camera/5.jpg");
    img.resize(800,0);
  }

  noStroke();
  sphereDetail(0);
  float tiles = 300; //Menge an Partikeln
  float tileSize = width/tiles;
  
  push();
  translate(width/2,height/2);
  rotateY(radians(frameCount)); //Rotation des Objektes (Kann auch mausgesteuert mit mouseX bzw. mouseY)
  
  for (int x = 0; x < tiles; x++){
    for (int y = 0; y < tiles; y++) {
      color c = img.get(int(x*tileSize),int(y*tileSize));
      float b = map(brightness(c),0,255,1,0);
      
      if (mousePressed){ //"Parikel" werden längst der z Achse verteilt, wenn Maus gedrückt und gezogen wird
      float z = map(b,0,1,mouseX/2,mouseY/2);
     
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.8);
      pop();
      }
      
      else { //wenn Maus NICHT gedrückt wird, werden die "Partikel" gleichmäßig verteilt
        float z = map(b,0,1,-100,100);
     
      push();
      translate(x*tileSize - width/2, y*tileSize - height/2, z);
      sphere(tileSize*b*0.8); //Definition der Form
      pop();
      }
    }
  }
  pop();
  }
  
  void keyPressed () { //Screenshot
  if (key =='s') {
    save("screenshots/tutorial.jpg");
    }
  }

Kamera

import processing.video.*;
  
Capture webcam;

void setup()
{
  size(640, 480);
  smooth();
    
  webcam = new Capture(this, width, height, 30);
  webcam.start();
}

void draw()
{
  background(#FFFFFF);
  image(webcam, 0, 0);
}

void captureEvent(Capture webcam)
{
  webcam.read();
}

 void keyPressed () { //Screenshot
  if (key =='c') {
    save("5.jpg");
  }
}

Dieser Code lässt viel Spielraum für deine eigene Kreativität, also probier dich aus.

Die Inspiration für unser Projekt war dieses Tutorial von Tim Rodenbröker.

Der Code für die Kamerafunktion stammt von dieser Webseite.


© 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.