Kinect Einstieg


Die Kinect ist eine 3D Kamera. Sie nimmt in erster Linie nicht das Aussehen von Objekten auf, sondern deren Position im Raum.

Wie schafft die Kinect das?

Die Hardware besteht aus 2 Kameras. Einer normale RGB Kamera und einer Infrarot Kamera. Die RGB Kamera liefert ein Bild vergleichbar einer günstigen Webcam in einer Auflösung von 640 mal 480 Pixel. Die IR Kamera liefert ein IR Bild in der selben Auflösung. Außerdem ist die Kinect mit einem IR Projektor ausgestattet. Dieser projeziert Infrarot Pixel, die dann mit den entsprechenden Pixel der IR Kamera verrechnet werden. Daraus kann dann die Entfernung der Pixel vom Sensor errechnet werden.

121212_222007_369

IR Bild aus der Kinect

Die Kinect kann nun verschiedene Outputs produzieren. Dafür verwenden wir folgendes Beispielprogramm:

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
*
* MOUSE
*
* KEYS
* s                   : save png
*/

import SimpleOpenNI.*;
import java.util.Calendar;

SimpleOpenNI  kinect;

void setup()
{
size(640, 480);

kinect = new SimpleOpenNI(this);

// depthMap aktivieren
kinect.enableDepth();

// RGB Kamera aktivieren
//kinect.enableRGB();

// IR Kamera aktivieren
//kinect.enableIR();

// Scenemap aktivieren
//kinect.enableScene();
}

void draw()
{
// Update Kinect
kinect.update();

// Eine der vier Images können aktiviert werden, wenn auch in
// setup() die entsprechende Funktion aktiviert wurde
// Achtung: RGB und IR können nicht parallel genutzt werden!!!
image(kinect.depthImage(), 0, 0);
//image(kinect.rgbImage(),0,0);
//image(kinect.irImage(), 0, 0);
//image(kinect.sceneImage(),0,0);
}

void keyReleased() {
if (key == 's' || key == 'S') saveFrame(timestamp()+"_##.png");
}

// timestamp
String timestamp() {
Calendar now = Calendar.getInstance();
return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}

Das Programm erzeugt zuerst einmal ein Objekt des SimpleOpenNI Sensors namens kinect. Mit dessen Hilfe kann man nun die Funktionen der Kinect kontrollieren. Damit wir dann Output generieren können, müssen wir die verschiedenen Funktionen der Kinect aktivieren. Das passiert in der Funktion setup(). In draw() müssen dann die Daten aus dem Sensor abergerufen werden. Nun können die vom Sensor bereitgestellten Bilder ausgegeben werden.

Wie sind diese Bilder zu interpretieren?

1. depthImage()

121212_221759_2334

Dieses Bild wird von der Kinect aus den Tiefeninformationen generiert. Nahe Objekte werden heller dargestellt, als weiter entfernte. Sind Objekte weniger weit als etwa 50 cm funktioniert die Entfernungsmessung nicht. Diese Objekte werden schwarz dargestellt. Der Messbereich reicht bis 8m. Die Entfernung werden von der Kinect sehr genau gemessen. Wir werden später sehen, wie wir diese ermitteln können. Was an  dem Bild noch auffällt ist, dass es kleine scharze Bereiche enthält. Diese werden von Schatten verursacht, die näher gelegene Objekte auf weiter entfernte werfen. In diesen Bereichen kann natürlich keine Tiefeninfo abgerufen werden.

Wenn wir das Programm um folgende Zeile erweitern, können wir die Tiefeninformation an der Mausposition abrufen. Dafür verwenden wir die OpenNI Funktion depthMap(). Sie gibt ein int- Array mit der Entfernung des Punktes zum Sensor zurück.

  println(kinect.depthMap()[640*mouseY+mouseX]);

2. rgbImage()

121212_221837_865

Dieses Bild stammt von der RGB Kamera und ist qualitativ nicht besonders ansprechend. Allerdings ist sie doch recht interessant. Wir werden uns später ansehen, wie man die Farbinfos auf die Tiefenprojektion übertragen und somit ein farbiges 3 dimensionales Bild erstellen können.

3. irImage()

121212_222007_369

Output der IR Kamera. Hier kann man sehr gut die projezierten IR Punkte sehen.

4. sceneImage()

121212_224833_15438

Hier sind wir schon bei den höheren Funktionen der SimpleOpenNI. In diesem Modus versucht die Library Gegenstände im Bild zu erkennen. Wird ein Gegenstand erkannt, färbt ihn das System automatisch ein.

Advertisements

Shading


Processing 2.0

In Processing 2.0 basiert die Rendering Engine auf GLSL Shadern. D.h. bei jeder Darstellung auf dem Bildschirm wird von Processing automatisch ein geeigneter Shader gewählt. Der Vorteil dabei liegt darin, dass die Programme zu einem größeren Teil von der GPU ausgeführt werden. Außerdem entfällt der Datentransfer vom RAM in den Grafikspeicher. All das führt zu einer mehr oder weniger starken Beschleunigung der Programme.

Es kommen immer 2 Shader zum Einsatz:

  • Vertex Shader
    • Manipuliert die Geometrie der dargestellten Objekte.
    • Z.B. zum Verändern der Perspektive
    • Er kann keine Objekte hinzufügen, oder entfernen!
  • Fragment Shader
    • Wandelt die 2D Objekte in ein Pixel-Array um –> Rasterization
    • Berechnet Blending und Licht
    • Das Resultat ist die endgültige Pixelfarbe

     

Wie schon gesagt, wendet Processing einen geeigneten Shader für die jeweilige Aufgabe aus. Diese Aufgaben können folgendermaßen eingeteilt werden:

  1. gefärbte Polygone ohne Licht und Textur
  2. gefärbte Polygone mit Licht, aber ohneTextur
  3. gefärbte Polygone ohne Licht, aber mit Textur
  4. gefärbte Polygone mit Licht und Textur
  5. Linien
  6. Punkte

Jeder dieser verschiedenen Shader erwartet bestimmte In- und Outputs.

Beinhaltet ein draw() Durchlauf verschiedene dieser Elemente, kommen die entsprechenden Shader (immer bestehend aus einem Vertex– und einem FragmentShader) zum Einsatz.

Will man nun einen eigenen Shader anwenden, kann man entweder beide default Shader, oder nur den Fragment-Shader ersetzen. Dadurch werden die default Shader aber nur deaktiviert und können jederzeit wieder aktiviert werden.

Beispiel: Default Shader für 1.

shader

In diesem Beispiel schauen wir uns an, wie man die default Shader von Processing durch eigene ersetzen kann und wie diese Shader aufgebaut sind.

Zuerst einmal brauchen wir einen Vertex-Shader. Dieser gleicht dem default Shader in Processing.


uniform mat4 projmodelviewMatrix;

attribute vec4 inVertex;
attribute vec4 inColor;

varying vec4 vertColor;

void main() {
gl_Position = projmodelviewMatrix * inVertex;

vertColor = inColor;
}

Was macht nun dieser Vertex Shader? Er bekommt von Processing die projmodelviewMatrix übergeben und für jeden Punkt die Variablen inVertex und inColor. In void main()  wird dann das eigentliche C-Programm platziert. In unserem Fall werden nur inVertex mit der projmodelviewMatrix verrechnet und in gl_Position gespeichert, und der Wert inColor wird in vertColor gespeichert.

Die Variable vertColor wird dann an den Fragment Shader weitergereicht und in dessen Programmteil verarbeitet. Die Definitionen am Beginn des Shaders sind aus Kompatibilitätsgründen notwendig.

Hier jetzt der gesamte Fragment Shader.

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec4 vertColor;

void main() {
// Outputting pixel color (interpolated across triangle)
gl_FragColor = vertColor;
}

Diese beiden Shader (vertex.glsl und fragment.glsl) müssen nun im Unterordner /data im Sketchordner platziert werden. Der Einsatz dieser Shader wird in Processing mit der PShader Klasse kontrolliert. Mit loadShader(„fragment.glsl“, „vertex.glsl“) werden die Shader geladen. Es kann auch nur ein Fragment Shader als Parameter übergeben werden. Ist der Shader erst einmal geladen, wird er mit shader(basic) aktiviert und mit resetShader() deaktiviert.

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* MOUSE LEFT          : Shader ein/aus
*/

PShader basic;

boolean enabled = false;

void setup() {
size(640, 360, P3D);
basic = loadShader("fragment.glsl", "vertex.glsl");
}

void draw() {
background(0);
fill(255);
if (enabled == true) {
shader(basic);
println("shader");
}

translate(width/2, height/2, -200);
sphere(200);
//println(frameRate);
}

void mousePressed() {
enabled = !enabled;
if (!enabled == true) {
resetShader();
}
}

Um zu beweisen, dass die Shader auch aktiv sind, kann man die folgende Zeile in das Programm des Fragment Shaders statt der Originalzeile einfügen:

gl_FragColor = vertColor*vec4(0.1,0.6,0.8,1.0);

Speichert man dann und lässt das Programm laufen, ändert sich die Farbe des Objekts.

Rotations III


Eine weitere Variation der Rotations.

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

int rand=(int) random(0, 50);

void setup() {
size(1280, 1280);
background(0);
stroke(50, 100, 200, 5);
frameRate(50);
colorMode(HSB);
}

void draw() {
if (frameCount<=500) {
translate(width/2, height/2);
strokeWeight(frameCount/2);
float winkel=radians(frameCount*80);
rotate(winkel*rand);
stroke(map(radians(winkel)%(2*PI), 0, 2*PI, 0, 255), 255, 255, 5);
translate(cos(winkel)*100, sin(winkel)*100);
line(100, 0, 300, 0);
if (frameCount%50 == 0) saveFrame("rotations-"+rand+"-#####.png");
}
}


Rotations II


Processing 2.0

Beispiel: Das Auge

Das Programm von Rotations I wird nun um eine zufällig generierte Farbpalette erweitert. Diese Zufallsfarben weisen eine Gauss’sche Normalverteilung auf. Siehe Zufall

Außerdem ändert sich während der Laufzeit die Strichlänge, Farbe und Position.

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

Random generator;
int str=(int) random(7, 7);

int col[] = new int [str];
int baseCol= (int) random(0, 255);

void setup() {
size(2700, 2700);
background(0);
frameRate(120);
colorMode(HSB);
generator = new Random();
for (int i =0; i<col.length; i++) {
float gauss = (float) generator.nextGaussian();
col[i] = round( 40 * gauss + baseCol);
}
println(col);
}

void draw() {
if (frameCount<30000) {
translate(width/2, height/2);
//strokeWeight(frameCount/128);
strokeWeight(random(1, width/40));
rotate(radians(frameCount/2*(360/str)));
// println(str);
stroke(col[frameCount%str]-frameCount/200, 255, 255, 1);
line(width/4.5-frameCount/50, 0, random(width/8, width*4/10)-frameCount/50, 0);
if (frameCount%3000==0) saveFrame("rotate"+str+baseCol+"#####.png");
}
}

Rotations I


Processing 2.0

Ausgehend von einem Beispiel aus dem Artikel Transformationen hat mich interessiert, was mit einfachen Rotationen alles möglich ist.

Beispiel: Erst mal einfärbig und mit nur einer Rotation

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

void setup() {
size(900,900);
background(0);
stroke(50,100,200,5);
frameRate(25);
}

void draw() {
if(frameCount<500) {
translate(width/2,height/2);
strokeWeight(frameCount/4);
rotate(radians(frameCount*20));
line(50,0,350,0);
}
if(frameCount==500) saveFrame("rotate.png");
}

Beispiel: Etwas mehr Farbe

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

void setup() {
size(900,900);
background(0);
stroke(50,100,200,5);
frameRate(25);
colorMode(HSB);
}

void draw() {
if(frameCount<500) {
translate(width/2,height/2);
strokeWeight(frameCount/4);
println(radians(frameCount*20));
rotate(radians(frameCount*20));
stroke(map(radians(frameCount*20)%(2*PI),0,2*PI,0,255),255,255,5);
line(50,0,350,0);
}
if(frameCount==500) saveFrame("rotate.png");
}

Beispiel: Variation mit etwas schmäleren Strichen und mehr Wiederholungen

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

void setup() {
size(900, 900);
background(255);
stroke(50, 100, 200, 5);
frameRate(120);
colorMode(HSB);
}

void draw() {
if (frameCount<50000) {
translate(width/2, height/2);
strokeWeight(frameCount/64);
rotate(radians(frameCount*20));
stroke(map(radians(frameCount*20)%(2*PI), 0, 2*PI, 0, 255), 255, 255, 5);
line(200, 0, 220, 0);
if (frameCount%500 == 0) saveFrame("background-######.png");
}
}

The Hunt


Processing 2.0

Für dieses Projekt habe ich den Flocking Algorithmus von Daniel Shiffman um eine Dimension erweitert um damit eine 3 dimensionale Szene erzeugen zu können. Darin gibt es Schwarmfische und Haie.

  • Die Schwarmfische bewegen sich nach den Regeln aus dem Flocking Artikel und 2 zusätzlichen Regeln. Sie bewegen sich immer zum Ursprung des Koordinatensystems und weichen den Haien aus.
  • Die Haie bewegen sich nach einem reduzierten Satz von Regeln. Sie halten einen bestimmten Abstand zueinander und sie bewegen sich in Richtung der durchschnittlichen Position der Schwarmfische in ihrer Nähe.

Die 3D Modelle der Fische stammen von http://www.turbosquid.com/ . Ich habe dann die Szene noch entsprechend ausgeleuchtet (–> siehe Artikel 3D Oberflächen und Licht).

Download Source

Flocking


Processing 2.0

Mit Flocking Algorithmen lassen sich sehr imposante Graphiken und Animationen erstellen. Flocking Algorithmen versuchen das Verhalten von Tieren in Schwärmen nachzubilden.

Dafür erstellt man eine Klasse, die ein Individuum repräsentiert, das sich in seinen Bewegungen an den, in der Nähe befindlichen Individuen orientiert. Dabei folgt jedes Individuum einigen einfachen Regeln.

In einer einfachen Implementierung von Daniel Shiffman auf http://processing.org/learning/topics/flocking.html sind das folgende:

  1. Separation (Aufteilung); wähle eine Richtung, die einer Häufung von Individuen entgegenwirkt.
    2 Individuen dürfen sich nicht am selben Ort befinden. Deshalb müssen sie einander ausweichen, wenn sie einen bestimmten Abstand unterschreiten.
  2. Alignment (Angleichung): wähle eine Richtung, die der mittleren Richtung der benachbarten Individuen entspricht.
    Jedes Individuum richtet seine Bewegungsrichtung nach den Nachbarn aus. D.h. es wird eine durchschnittl. Bewegungsrichtung der, innerhalb eines bestimmten Abstands befindlichen Individuen errechnet. Die eigenen Bewegungsrichtung passt sich dieser an.
  3. Cohesion (Zusammenhalt): wähle eine Richtung, die der mittleren Position der benachbarten Individuen entspricht.
    Alle Individuen immerhalb eines bestimmten Bereichs bewegen sich Richtung dessen Zentrum.

Diese 3 Regeln führen zu einem Verhalten, wie im untenstehenden Sketch von Daniel Shiffman. Es können bei Bedarf natürlich noch weitere Regeln definiert werden.

Starte Applet

Flock flock;

void setup() {
  size(640, 360);
  flock = new Flock();
  // Add an initial set of boids into the system
  for (int i = 0; i < 150; i++) {
    flock.addBoid(new Boid(width/2,height/2));
  }
}

void draw() {
  background(50);
  flock.run();
}

// Add a new boid into the System
void mousePressed() {
  flock.addBoid(new Boid(mouseX,mouseY));
}

// The Boid class

class Boid {

  PVector location;
  PVector velocity;
  PVector acceleration;
  float r;
  float maxforce;    // Maximum steering force
  float maxspeed;    // Maximum speed

  Boid(float x, float y) {
    acceleration = new PVector(0,0);
    velocity = new PVector(random(-1,1),random(-1,1));
    location = new PVector(x,y);
    r = 2.0;
    maxspeed = 2;
    maxforce = 0.03;
  }

  void run(ArrayList<Boid> boids) {
    flock(boids);
    update();
    borders();
    render();
  }

  void applyForce(PVector force) {
    // We could add mass here if we want A = F / M
    acceleration.add(force);
  }

  // We accumulate a new acceleration each time based on three rules
  void flock(ArrayList<Boid> boids) {
    PVector sep = separate(boids);   // Separation
    PVector ali = align(boids);      // Alignment
    PVector coh = cohesion(boids);   // Cohesion
    // Arbitrarily weight these forces
    sep.mult(1.5);
    ali.mult(1.0);
    coh.mult(1.0);
    // Add the force vectors to acceleration
    applyForce(sep);
    applyForce(ali);
    applyForce(coh);
  }

  // Method to update location
  void update() {
    // Update velocity
    velocity.add(acceleration);
    // Limit speed
    velocity.limit(maxspeed);
    location.add(velocity);
    // Reset accelertion to 0 each cycle
    acceleration.mult(0);
  }

  // A method that calculates and applies a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  PVector seek(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    // Normalize desired and scale to maximum speed
    desired.normalize();
    desired.mult(maxspeed);
    // Steering = Desired minus Velocity
    PVector steer = PVector.sub(desired,velocity);
    steer.limit(maxforce);  // Limit to maximum steering force
    return steer;
  }

  void render() {
    // Draw a triangle rotated in the direction of velocity
    float theta = velocity.heading2D() + radians(90);
    fill(200,100);
    stroke(255);
    pushMatrix();
    translate(location.x,location.y);
    rotate(theta);
    beginShape(TRIANGLES);
    vertex(0, -r*2);
    vertex(-r, r*2);
    vertex(r, r*2);
    endShape();
    popMatrix();
  }

  // Wraparound
  void borders() {
    if (location.x < -r) location.x = width+r;
    if (location.y < -r) location.y = height+r;
    if (location.x > width+r) location.x = -r;
    if (location.y > height+r) location.y = -r;
  }

  // Separation
  // Method checks for nearby boids and steers away
  PVector separate (ArrayList<Boid> boids) {
    float desiredseparation = 25.0f;
    PVector steer = new PVector(0,0,0);
    int count = 0;
    // For every boid in the system, check if it's too close
    for (Boid other : boids) {
      float d = PVector.dist(location,other.location);
      // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if ((d > 0) && (d < desiredseparation)) {
        // Calculate vector pointing away from neighbor
        PVector diff = PVector.sub(location,other.location);
        diff.normalize();
        diff.div(d);        // Weight by distance
        steer.add(diff);
        count++;            // Keep track of how many
      }
    }
    // Average -- divide by how many
    if (count > 0) {
      steer.div((float)count);
    }

    // As long as the vector is greater than 0
    if (steer.mag() > 0) {
      // Implement Reynolds: Steering = Desired - Velocity
      steer.normalize();
      steer.mult(maxspeed);
      steer.sub(velocity);
      steer.limit(maxforce);
    }
    return steer;
  }

  // Alignment
  // For every nearby boid in the system, calculate the average velocity
  PVector align (ArrayList<Boid> boids) {
    float neighbordist = 50;
    PVector sum = new PVector(0,0);
    int count = 0;
    for (Boid other : boids) {
      float d = PVector.dist(location,other.location);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(other.velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div((float)count);
      sum.normalize();
      sum.mult(maxspeed);
      PVector steer = PVector.sub(sum,velocity);
      steer.limit(maxforce);
      return steer;
    } else {
      return new PVector(0,0);
    }
  }

  // Cohesion
  // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
  PVector cohesion (ArrayList<Boid> boids) {
    float neighbordist = 50;
    PVector sum = new PVector(0,0);   // Start with empty vector to accumulate all locations
    int count = 0;
    for (Boid other : boids) {
      float d = PVector.dist(location,other.location);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(other.location); // Add location
        count++;
      }
    }
    if (count > 0) {
      sum.div(count);
      return seek(sum);  // Steer towards the location
    } else {
      return new PVector(0,0);
    }
  }
}

// The Flock (a list of Boid objects)

class Flock {
  ArrayList<Boid> boids; // An ArrayList for all the boids

  Flock() {
    boids = new ArrayList<Boid>(); // Initialize the ArrayList
  }

  void run() {
    for (Boid b : boids) {
      b.run(boids);  // Passing the entire list of boids to each boid individually
    }
  }

  void addBoid(Boid b) {
    boids.add(b);
  }
}

Erläuterungen zur Boid Klasse:

  • Die Vektoren location, velocity und acceleration kontrollieren Position, Geschwindigkeit und Beschleunigung.
  • In jedem draw() Durchlauf wird die acceleration (Beschleunigung) basierend auf der 3 Kräfte Separation, Alignment und Cohesion neu berechnet.
  • Separation: Es werden alle Boids durchlaufen und ein Distanzvektor errechnet. Weiters wird aus allen Distanzvektoren, die größer 0 und kleiner als ein desiredseparation genannter Wert sind, ein Durchsschnittdistanzsvektor namens steer errechnet. Von diesem wird dann noch der aktuelle Geschwindigkeitsvektor abgezogen.
  • Alignment: Auch hier werden wieder alle Boids durchlaufen. Dabei wird ein durchschnittlicher  Geschwindigkeitsvektor aller Boids errechnet, die sich innerhalb einer bestimmten Distanz (neighbordist) befinden. Auch von diesem wird dann wieder der Boid eigene Geschwindigkeitsvektor abgezogen.
  • Cohesion: Bei der Cohesion wird ein durchschnittlicher Positionsvektor aller, sich innerhalb einer best. Distanz befindenen Boids errechnet. In diese Richtung wird dann gesteuert.
  • In der flock-Methode der Boid Klasse werden dann alle Kräfte gewichtet und zur aktuellen Beschleunigung aufsummiert.
  • In update wird dann die aktuelle Beschleunigung zur aktuellen Geschwindigkeit hinzugerechnet. Dann wird noch die Geschwindigkeit auf die Maximalgeschwindigkeit begrenzt und die Beschleunigung auf 0 gesetzt.

einstieg in die programmierung mit processing

%d Bloggern gefällt das: