Schlagwort-Archive: 3D

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

Advertisements

3D Oberflächen und Licht


Processing 2.0

Betrachtet man ein reales Objekt, z.B. einen einfärbigen Würfel, so stellt man fest, dass alle Seiten unterschiedliche Farbschattierungen aufweisen. Das liegt am Spiel zwischen Licht und Schatten. Dieses Spiel gilt es so gut, wie möglich zu immitieren, will man realitätsnahe 3D Darstellungen generieren.

Eins vorweg: Processing ist dafür nur bedingt gut geeignet. Nichts desto trotz werden wir uns hier mit den Grundlagen von Lichtern und Oberflächen beschäftigen.

Um die Effekte der Kombination aus verschieden Lichtquellen und Oberflächen zu demonstrieren habe ich das Programm lightsTesterDemo geschrieben. Die lightsTester Klasse kann auch in anderen Projekten verwendet werden.

LightsTester funktioniert leider derzeit nicht online, muss also heruntergeladen werden.

Lichtquellen

Generell müssen Lichtquellen in jedem draw() Durchlauf, und nicht in setup() aufgerufen werden.

lights();

Die einfachste Möglichkeit in Processing mit Licht zu arbeiten bietet die Funktion lights(). Sie generiert ein Kombination aus verschieden Lichtquellen mit default Werten.

ambientLight(rot, grün, blau);

Dient als Hintergrundbeleuchtung und wird in der Regel in Kombination mit anderen Lichtquellen eingesetzt. Es beleuchtet Objekte von allen Seiten gleich stark.

directionalLight(rot, grün, blau, x, y, z);

Erzeut paralleles Licht aus einer bestimmten Richtung. Trifft es auf eine Oberfläche, so entsteht Steulicht, welches wiederum auf andere Objekte wirkt. Es hat eine bestimmte Farbe und eine Richtung.

pointLight(rot, grün, blau, x, y, z); Funktioniert derzeit nicht (Processing 2.0b3)!!!

Generiert eine punktförmige Lichtquelle. Farbe und Position müssen als Parameter angegeben werden.

spotLight(rot, grün, blau, x, y, z, dx, dy, dz, winkel, fokus);

Eine Lichtquelle, die wie ein Scheinwerfer funktioniert. Neben der Farbe und der Position müssen hier noch die Abstrahlrichtung (winkel) und die Konzentration der Lichtstärke im Zentrum (fokus) angegeben werden. In lights können die Werte für winkel und fokus über den alpha Kanal des entsprechenden Color Pickers gesteuert werden.

lightSpecular(rot, grün, blau);

Legt die Farbe der Glanzlichter fest. Hat nur Einfluss auf die jenigen Elemente, die im Code danach erstellt werden. D.h. die Glanzlichter müssen vor den anderen Lichtern gesetzt werden.

Oberflächen

Die Effekte von Lichtquellen hängen von der Oberflächenbeschaffenheit der beleuchteten Objekte ab. Deshalb gibt es in Processing eine Reihe von Oberflächeneigenschaften, die in Kombination mit den verwendeten Lichtquellen benutzt werden können.

ambient(rot, grün, blau);

Legt den Reflexionsgrad der Oberfläche in Kombination mit ambientLight() fest. Z.B. ambient(255,128,0) reflektiert rot zur Gänze, grün zur Hälfte und blau gar nicht.

emissive(rot, grün, blau);

Objekte können selbst Licht emittieren. D.h. sie leuchten selbst in der hier definierten Farbe. Alledings nur dann, wenn irgendeine Lichtquelle vorhanden ist.

specular(rot, grün, blau);

Damit definiert man die Farbe der Glanzlichter.

shininess(float);

Legt die Glanzintensität einer Oberfläche fest.

lightFalloff(constant, linear, quadratic);

Dieser Parameter legt den Lichtabfall über die Entfernung zur Lichtquelle mit Werten zwischen 0 und 1 fest. Er wirkt allerdings nur auf point spot und ambient Lights.

Beispiel: lights

Mit dem Licht und Oberflächen Tool kannst du verschiedenste Licht und Oberflächeneffekte testen. Es funktioniert derzeit eider nur im Java Modus und muss deshalb als .zip Datei heruntergeladen werden.


// Bewege die Maus, um die Position der Lichtquelle zu ändern

//GUI Library ControlP5
import controlP5.*;
ControlP5 controlP5;
ColorPicker cplamb, cpldir, cplpoi, cpambient, cpfill, cplspt, cplspc, cpemissive, cpspecular;
CheckBox checkbox;

public int R, G, B=128;
public int shininess, falloff=0;
int mode = 0;
int [] heights = new int [25];
boolean lightsOn, ambientOn, directionalOn, pointOn, spotOn, specOn =false;

void setup() {
size(800, 800, P3D);
smooth(1);
createControlP5();

for (int i=0;i< heights.length;i++) {
heights[i]=(int) random(100, 255);
}
}

void draw() {
background(0);

// Kontrolle über die Lichter
controlP5.draw();

ambient(cpambient.getColorValue());
emissive(cpemissive.getColorValue());
specular(cpspecular.getColorValue());
shininess(shininess);
lightFalloff(0, 0, falloff);

if (specOn) {
lightSpecular(red(cplspc.getColorValue()), green(cplspc.getColorValue()), blue(cplspc.getColorValue()));
}
if (lightsOn) lights();
if (ambientOn) {
ambientLight(red(cplamb.getColorValue()), green(cplamb.getColorValue()), blue(cplamb.getColorValue()),
map(mouseX, 0, width, 1, -1), -1, map(mouseY, 0, height, 1, -1));
}
if (directionalOn) directionalLight(red(cpldir.getColorValue()), green(cpldir.getColorValue()), blue(cpldir.getColorValue()),
map(mouseX, 0, width, 1, -1), 1, map(mouseY, height, 0, -1, 1));

if (pointOn) pointLight(red(cplpoi.getColorValue()), green(cplpoi.getColorValue()), blue(cplpoi.getColorValue()),
0, 100, 0);

if (spotOn) {
spotLight(red(cplspt.getColorValue()), green(cplspt.getColorValue()), blue(cplspt.getColorValue()),
map(mouseX, 0, width, 0, width), -1000, map(mouseY, 0, height, 0, height*2),
0, 1, -1, map(alpha(cplspt.getColorValue()),0,255,0,PI), map(alpha(cplspt.getColorValue()),0,255,255,0));
}

fill(cpfill.getColorValue());
drawBlocks();
}

void drawBlocks() {

// Betrachtungsperspektive ändern
translate(100, height/2, -500);
rotateX(PI/3);

//Zeichne Blocks
for (int i=0;i<5;i++) {
for (int j=0;j<5;j++) {
pushMatrix();
translate(120*i, 120*j, heights[i*5+j]/2);
//box(50, 50, heights[i*5+j]*1.2);
sphere(heights[i*5+j]/2);
popMatrix();
}
}
}

void createControlP5() {

// Steuerelemente erstellen
controlP5 = new ControlP5(this);

//Checkboxes erstellen
checkbox = controlP5.addCheckBox("lcheckBox", 10, 10);
checkbox.setColorForeground(color(120));
checkbox.setColorActive(color(255));
checkbox.setColorLabel(color(255));
checkbox.setItemsPerRow(8);
checkbox.setSpacingColumn(119);
checkbox.addItem("lights()", 0);
checkbox.addItem("ambientLight()", 0);
checkbox.addItem("directionalLight()", 0);
checkbox.addItem("pointLight()", 0);
checkbox.addItem("spotLight()", 0);
checkbox.addItem("lightSpecular()", 0);

//Colorpicker erstellen
ControlGroup fillColor = controlP5.addGroup("fill Color", 10, 40);
fillColor.activateEvent(false);
fillColor.close();
cpfill = controlP5.addColorPicker("fillpicker", 0, 0, 128, 0);
cpfill.setGroup(fillColor);

ControlGroup ambLightCol = controlP5.addGroup("ambientLight Color", 140, 40);
ambLightCol.activateEvent(false);
ambLightCol.close();
cplamb = controlP5.addColorPicker("amblpicker", 0, 0, 128, 0);
cplamb.setGroup(ambLightCol);
cplamb.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup dirLightCol = controlP5.addGroup("directionalLight Color", 270, 40);
dirLightCol.activateEvent(false);
dirLightCol.close();
cpldir = controlP5.addColorPicker("dirlpicker", 0, 0, 128, 0);
cpldir.setGroup(dirLightCol);
cpldir.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup poiLightCol = controlP5.addGroup("pointLight Color", 400, 40);
poiLightCol.activateEvent(false);
poiLightCol.close();
cplpoi = controlP5.addColorPicker("poilpicker", 0, 0, 128, 0);
cplpoi.setGroup(poiLightCol);
cplpoi.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup sptLightCol = controlP5.addGroup("spotLight Color", 530, 40);
sptLightCol.activateEvent(false);
sptLightCol.close();
cplspt = controlP5.addColorPicker("spotlpicker", 0, 0, 128, 0);
cplspt.setGroup(sptLightCol);
cplspt.setArrayValue(new float[] {
100, 100, 100, 100
}
);

ControlGroup lightSpecCol = controlP5.addGroup("lightSpec Color", 660, 40);
lightSpecCol.activateEvent(false);
lightSpecCol.close();
cplspc = controlP5.addColorPicker("speclpicker", 0, 0, 128, 0);
cplspc.setGroup(lightSpecCol);
cplspc.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup surfaceCol = controlP5.addGroup("ambient Color", 10, 120);
surfaceCol.activateEvent(false);
surfaceCol.close();
cpambient = controlP5.addColorPicker("ambpicker", 0, 0, 128, 0);
cpambient.setGroup(surfaceCol);
cpambient.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup emissiveCol = controlP5.addGroup("emissive Color", 140, 120);
emissiveCol.activateEvent(false);
emissiveCol.close();
cpemissive = controlP5.addColorPicker("emipicker", 0, 0, 128, 0);
cpemissive.setGroup(emissiveCol);
cpemissive.setArrayValue(new float[] {
100, 100, 100, 255
}
);

ControlGroup specularCol = controlP5.addGroup("specular Color", 270, 120);
specularCol.activateEvent(false);
specularCol.close();
cpspecular = controlP5.addColorPicker("specpicker", 0, 0, 128, 0);
cpspecular.setGroup(specularCol);
cpspecular.setArrayValue(new float[] {
100, 100, 100, 255
}
);

//Slider erstellen
controlP5.begin(10, 150);
controlP5.addSlider("shininess", 0, 255, 0, 400, 110, 128, 10);
controlP5.addSlider("falloff", 0, 1, 0, 595, 110, 128, 10);
controlP5.end();
}

// Aktion Radio Button
void controlEvent(ControlEvent theEvent) {
if (theEvent.isGroup()) {
//print("got an event from "+theEvent.group().name()+"\t");
for (int i=0;i<theEvent.group().arrayValue().length;i++) {
int n = (int)theEvent.group().arrayValue()[i];
print(n);
if (i==0) {
if (n==1)  lightsOn=true;
else lightsOn=false;
}
if (i==1) {
if (n==1)  ambientOn=true;
else ambientOn=false;
}
if (i==2) {
if (n==1)  directionalOn=true;
else directionalOn=false;
}
if (i==3) {
if (n==1)  pointOn=true;
else pointOn=false;
}
if (i==4) {
if (n==1)  spotOn=true;
else spotOn=false;
}
if (i==5) {
if (n==1)  specOn=true;
else specOn=false;
}
}
}
}

PeasyCam


Um die Ansicht einer Szene oder eines Bildes zu verändern gibt es im Allgemeinen zwei Möglichkeiten.

  1. Man verschiebt das Objekt.
  2. Man verändert den Standort des Betrachters.

Gerade in 3D Umgebungen ist es oft kompliziert mehrere Objekte so zu verschieben und zu drehen wie man das beabsichtigt. Hier ist es meist einfacher den Standort des Betrachter, sprich die Kamera zu verschieben und zu drehen.

Und genau dafür ist die PeasyCam Library da: http://mrfeinberg.com/peasycam/

Man braucht der Kamera nur die Position und den Punkt, auf den sie zeigen soll mitzuteilen.

Installation: siehe Libraries

Allgemeines:

import peasy.*;
PeasyCam cam;

void setup() {

  cam = new PeasyCam(this, 0, 0, 0, 500);

}

Die PeasyCam kann nach dem Import der Library mit der Maus gesteuert werden.

Was bedeuten die fünf Parameter der PeasyCam?
PeasyCam(this … Bezug auf den aktuellen Sketch, x-Position des Zielpunkts, y-Position des Zielpunkts, z-Position des Zielpunkts, Radius der Kugel, auf der sich die Cam bewegt);

Mausparameter:

  • Links-Click und ziehen –>  drehen
  • Mausrad oder Rechts-Click und ziehen –> zoom
  • Mittel-Click und ziehen –> Schwenken
  • DoppelClick –> reset

Die Cam wird auf einer Kugel mit einem bestimmten Abstand platziert und dann um dem Punkt, auf den sie „zeigt“ gedreht.

Außerdem ist es auch möglich die Kamera mittels Programmanweisungen zu positionieren.

Beispiel: Kamera mit Maus bewegen

In diesem Beispiel zeichnen wir das Koordinatensystem aus 3D Basics und bewegen die Kamera mit der Maus.

Starte Applet

import processing.opengl.*;
import peasy.*;

PeasyCam cam;

void setup() {
size(640, 640, OPENGL);
translate(width/2, height/2, 0);
cam = new PeasyCam(this, 0, 0, 0, 1000);
}
void draw() {
background(0);
textSize(20);
stroke(255);
drawAxes();
}

void drawAxes() {
stroke(255, 0, 0);
line(-300, 0, 0, 300, 0, 0);
text("+x", 300, 0, 0);
text("-x", -330, 0, 0);
stroke(0, 255, 0);
line(0, -300, 0, 0, 300, 0);
text("+y", 0, 330, 0);
text("-y", 0, -300, 0);
stroke(0, 0, 255);
line(0, 0, -300, 0, 0, 300);
text("+z", 0, 0, 330);
text("-z", 0, 0, -300);
}

Beispiel: Ändern des Betrachtungspunktes mittels Code

Mit der PeasyCam Funktion lookAt(x,y,z) kann man den Punkt der Betrachtung ändern. Das machen wir in diesem Beispiel innerhalb des Events mousePressed().

lookAt(x,y,z);

Wie zeichnen zwei Punkte in unsere 3D Umgebung und zentrieren die Kamera dann bei MausClick links und rechts auf je einen dieser Punkte.

Es ist zudem möglich die Position und Rotation der Kamera mit getPosition() und getRotation() (als float Array) auszulesen und in der Konsole auszugeben.

Starte Applet

import processing.opengl.*;
import peasy.*;

PeasyCam cam;

void setup() {
size(640, 640, OPENGL);
translate(width/2, height/2, 0);
cam = new PeasyCam(this, 0, 0, 0, 1000);
}
void draw() {
background(0);
textSize(20);
stroke(255);
drawAxes();
drawPoint();
}

//zeichnet die beiden Punkte
void drawPoint() {
pushStyle();
strokeWeight(3);
point(-400, 0, 0);
point(400, 0, 0);
popStyle();
}

void drawAxes() {
stroke(255, 0, 0);
line(-300, 0, 0, 300, 0, 0);
text("+x", 300, 0, 0);
text("-x", -330, 0, 0);
stroke(0, 255, 0);
line(0, -300, 0, 0, 300, 0);
text("+y", 0, 330, 0);
text("-y", 0, -300, 0);
stroke(0, 0, 255);
line(0, 0, -300, 0, 0, 300);
text("+z", 0, 0, 330);
text("-z", 0, 0, -300);
}

void mousePressed() {
//Ändert den Punkt der Betrachtung
if( mouseButton==LEFT) cam.lookAt(400, 0, 0);
else cam.lookAt(-400, 0, 0);

//gibt die Position und Drehung der Kamera in der Konsole aus
println("Position x: "+cam.getPosition()[0]+
" y: "+  cam.getPosition()[1]+
" z: "+  cam.getPosition()[2]);
println("Rotation x: "+cam.getRotations()[0]+
" y: "+  cam.getRotations()[1]+
" z: "+  cam.getRotations()[2]);
}

3 D Basics


Processing 2.0

In Processing ist es auch möglich in 3D zu arbeiten. Dies gibt uns die Möglichkeit alle bisher programmierten Bilder und Animationen um eine Dimension zu erweitern.

Im Gegensatz zu 2D, wo wir nur die Achsen x und y haben, kommt in 3D eine z-Achse dazu, die vom Ursprung (links oben) aus direkt aus dem Bildschirm heraus zeigt.

Damit wir die 3. Dimension nutzen können, müssen wir Processing sagen, dass wir nun in 3D arbeiten wollen und einen entsprechenden Renderer wählen.

Der Einfachheit halber wurden in Processing 2 die beiden vormals zu Verfügung stehenden Renderer P3D und OPENGL zusammengeführt und die OpenGL Library in den Processing Core aufgenommen. Das hat zur Folge, dass nun auch OpenGL direkt im P3D Modus programmiert werden kann. Darüber hinaus verbessert sich auch die Kompatibilität des Processing Codes über die Plattformen hinweg verbessert.

Wir benutzen also immer den P3D Renderer:

size(600, 600, P3D);

So, und jetzt können wir (fast) alle einfachen Zeichenfunktionen von Processing auch in 3D nutzen. Wir müssen dafür allerdings immer zusätzlich zu den x-, und y-Koordinaten auch eine z-Koordinate angeben.

Einen letzten wichtigen, und manchmal etwas verwirrendenPunkt stellt die Tatsache dar, dass der Betrachter in einer 3-dimensionalen Darstellung ja auch seine Position haben muss. Um das zu verdeutlichen spielen wir mit der Funktion translate() im folgenden Beispiel.

translate(width/2,height/2, 0);

Mit dieser Zeile verschieben wir das Koordinatensystem von links oben in die Mitte des Programmfensters. Die 0 als z-Koordinate bewirkt, dass wir eine Szene so betrachten, als ob wir in 2D arbeiten würden. Wenn wir jetzt aber eine negative Zahl als z-Koordinate wählen, werden die gezeichneten Objekte kleiner, da wir den Ursprung des Koordinatensystems von uns weg bewegt haben.

Bei positiven z-Werten bewegen wir uns also weiter vor das Objekt, bei negativen weiter dahinter(bis wir es gar nicht mehr sehen können!!!)!

Beispiel Drehbares Koordinatensystem: starte Applet

Achtung: Funktioniert im Android Mode!!!

void setup() {
size(640, 640, P3D);
}
void draw() {
background(0);
textSize(20);
stroke(255);
translate(width/2, height/2, -200);
println((height/2.0) / tan(PI*60.0 / 360.0));
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, height, -PI, PI));
drawAxes();
}

void drawAxes() {
stroke(255, 0, 0);
line(-300, 0, 0, 300, 0, 0);
text("+x", 300, 0, 0);
text("-x", -330, 0, 0);
stroke(0, 255, 0);
line(0, -300, 0, 0, 300, 0);
text("+y", 0, 330, 0);
text("-y", 0, -300, 0);
stroke(0, 0, 255);
line(0, 0, -300, 0, 0, 300);
text("+z", 0, 0, 330);
text("-z", 0, 0, -300);
}

Wenn wir das Koordinatensystem drehen fällt natürlich sofort auf, dass sich die Größe der Beschriftung mit der Entfernung von der Elemete ändert. Näheres erscheint größer, weiter entferntes kleiner. Das entspricht auch der Realität und stellt eben den großen Unterschied zu einer 2D Darstellung dar.

3 D spezifische Elemente (Körper):

Kugel

sphere(int size);

Zeichnet ein (natürlich!) 3-Dimensionale Kugel mit der Größe size. Die Kugel wird dabei immer auf den Punkt 0,0,0 zentriert.

sphereDetail(int detail);

Diese Anweisung bestimmt die Auflösung der Textur. 3D Renderer zeichnen nämlich keine Kurven, sondern zerlegen sie in einzelne Punkte, welche dann mit geraden Linien verbunden werden. Der Wert detail bestimmt die Anzahl der Punkte des Umfangs. Z.B.: detail 40 würde bedeuten, dass 360/40, also alle 9 Grad ein Punkt gezeichnet wird.

fill() und stroke() können wie gewohnt eingesetzt werden.

Beispiel Einfache Kugel: starte Applet

Achtung: Beispiel funktioniert nicht im Android Mode!!!

Im JavaScript Mode wird die Kugel nicht transparent dargestellt, und im Java Mode kommt es zu Darstellungsproblemen bei den Achsen!

Quellcode siehe Applet!

Würfel:

Wie Sphere, nur mit der Anweisung box().

box(Seitenlänge);

Komplexe Formen:

Sollten komplexere Formen benötigt werden, kann man diese entweder über beginShape(), vertex(x,y,z) und endShape() in Processing erstellen, oder man importiert fertige 3D Objekte (*.obj) mit Hilfe der loadShape(*.obj) Funktion.

Beispiel 3-seitige Pyramide: starte Applet

//Zeichne 3-seitige Pyramide
  beginShape(TRIANGLE);
  vertex(-1, -1, 1);
  vertex( -1, 1, 1);
  vertex( 0, 0, -1);

  vertex(-1, 1, 1);
  vertex( 1, 0, 1);
  vertex( 0, 0, -1);

  vertex(-1, -1, 1);
  vertex( 1, 0, 1);
  vertex( 0, 0, -1);

  vertex(-1, -1, 1);
  vertex( 1, 0, 1);
  vertex( -1, 1, 1);

  endShape();

Quellcode siehe Applet!

Sieht noch nicht sehr beeindruckend aus. Was noch feht ist die richtige Beleuchtung. Siehe Artikel 3D Oberflächen und Licht.

Vektoren


 

Processing 2.0

Ein PVector ist ein Objekt, das 2- oder 3-dimensionale Vektoren beschreibt. Ein Vektor hat eine bestimmte Richtung und eine bestimmte Länge. Man kann auch sagen, er erstreckt sich vom Punkt A zum Punkt B. In der Programmierung verwenden wir Vektoren, um Bewegung zu generieren.

Der Vorteil bei der Verwendung von Vektoren liegt darin, dass man schon mit 2 Vektor- Variablen die aktuelle Geschwindigkeit und Richtung eines Objektes beschreiben kann und mit einem zweiten die Änderung derselben. Diese beiden müssen dann nur noch in jedem Frame addiert werden.

Beispiel: starte Applet

/** 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.

//hier werden die Vektoren erstellt
PVector direction = new PVector(1,1);
PVector position = new PVector(20,20);
int radius = 15;

void setup() {
size(300,300);
}

void  draw() {
//für den Schweif
fill(255,20);
rect(0,0,width,height);
//damit sich überhaupt was bewegt
position.add (direction);
//die nächsten Zeile lassen die Ellipse zurückprallen
if (position.x < radius || position.x > (width-radius)) {
direction.x *= -1;
}
if (position.y < radius || position.y > (height-radius)) {
direction.y *= -1;
}
//zeichnet die Ellipse
fill(50);
ellipse(position.x,position.y,radius*2,radius*2);
}

Das Ganze kann dann noch gut mit Zufallszahlen kombiniert werden.

Methoden:

  • vector.set(x,y,z) … legt die x-, y- und z-Werte des Vektors fest
  • vector.get() … gibt die x-, y- und z-Werte des Vektors zurück
  • vector.mag() … gibt die Länge des Vektors zurück
  • vector1.add(vector2) … Addiert 2 Vektoren
  • vector1.sub(vector2) … Subtrahiert 2 Vektoren
  • vector1.mult(float) … Multipliziert  einen Vektor mit einer Zahl
  • vector1.div(float) … Dividiert  einen Vektor durch eine Zahl
  • vector.nomalize() … Ändert die Länge auf 1
  • vector1.anglebetween(vector2) … gibt den Winkel zwischen 2 Vektoren zurück
  • vector.heading2D() … gibt die Richtung des Vektors zurück

Aufgabe: Verändere das Programm so, dass der Kreis zufällig seine Richtung leicht variiert.

Vektoren im 3 dimensonalen Raum

Richtig interessant wird die Verwendung von Vektoren in 3D. Auch hier können alle oben genannten Methoden verwendet werden. D.h., um beispielsweise ein Objekt an die Position eines Vektors zu verschieben geht man wie folgt vor:

PVector loc=new PVector(x,y,z);

translate(loc.x, loc.y, loc.z);
Dann wird das Objekt auf die Position (0,0,0) gezeichnet. Wir haben also das Koordinatensystem verschoben. Um das Koordinatensystem wieder an zurückzusetzten verwendet man pushMatrix() und popMatrix().

Da Objekte im Raum, aber nicht nur eine Position, sondern auch eine bestimmte Orientierung aufweisen, stellt sich schnell die Frage, wie man die Orientierung im Raum definiert.

In einer Fläche (2 Dimensionen) ist das raltiv einfach. Man braucht dafür nur einen Winkel. Dieser steht normal zur Fläche (siehe Bsp.).

Beispiel: Rotate 2D starte Applet

/** 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.

PVector zero, actual;

void setup() {
size(300,300, P2D);
zero= new PVector(100,0);
rectMode(CENTER);
}

void draw() {
background(200);
stroke(0);

// Verschiebe das Koordinatensystem in den Mittelpunkt
translate(width/2, height/2);
line(0,0,zero.x,zero.y);

// Berechne die Koordinaten des Vektors actual aus der Mausposition
actual=new PVector(cos(map(mouseX,0,width,0,2*PI))*100,sin(map(mouseX,0,width,0,2*PI))*100);

stroke(200,0,50);
line(0,0,actual.x,actual.y);
text("Winkel:"+degrees(actual.heading2D()),height/2-130,width/2-30);

//Verschiebe das Koordinatensystem an die Position von actual
translate(actual.x,actual.y);

//Rotiere das Koordinatensystem
rotate(actual.heading2D());
rect(0,0,20,20);
}

Die Axis-Angle Methode

Im 3 dimensionalen Raum muss neben dem Winkel noch eine Achse angegeben werden, um welche gedreht wird. Wenn ich  sage, meine beiden Unterarme liegen  im Winkel von 90 zueinander, so würde sofort gefragt, in welcher Richtung. Ich könnte nämlich meine Arme in viele verschiedene Positionen drehen, und trotzdem könnten sie immer 90 zueinander liegen. Was wir brauchen ist die Definition einer Achse, um die gedreht wird.

Eine Methode, Axis-Angle Methode genannt, bietet die Möglichkeit beides, nämlich die Achse und den Rotationswinkel zu berechnen. Dabei kommen zwei mathematische Methoden zum Einsatz. Nämlich das sog. Punktprodukt od. Skalarprodukt (gibt den Rotationswinkel an) und das Kreuzprodukt, welches über einen neuen Vektor die Rotationsachse repräsentiert.

Beispiel: Axis-Angle Methode für Processing 2.0

Achtung: läuft mit Processing 2.0b6 nicht im Android Mode!!!

// von Thomas Koberger
/**
* MOUSE               : Rotation um die Achse zwischen Objekten
*
* KEYS
* Space               : neue Zufallswerte für Vektoren
*/
// 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.

// Damit die Szene rotiert und gezoomt werden kann
// Im Javascript auszukommentieren
import peasy.*;

// Im Javascript Mode auszukommentieren
PeasyCam cam;

PVector zero, left, right;

void setup() {
size(800, 800, P3D);

// Im Javascript auszukommentieren
cam = new PeasyCam(this, 0, 0, 0, 1000);

// Vektoren werden definiert
zero= new PVector(0, 0, 0);
left=new PVector(-width/4+random(-100, 100), random(-100, 100), random(-100, 100));
right=new PVector(width/4+random(-100, 100), random(-100, 100), random(-100, 100));
}

void draw() {
background(50);

// Im Javascript und Android Mode einkommentieren
//translate (width/2,height/2,0);
drawAxes(300);
stroke(255);
fill(200);

// Zeichne Würfel und Linien
line(left.x, left.y, left.z, right.x, right.y, right.z);
pushMatrix();
translate(left.x, left.y, left.z);
box(20);
popMatrix();
pushMatrix();
translate(right.x, right.y, right.z);
box(20);
popMatrix();
pushMatrix();
stroke(200, 0, 50);

// Verschiebe das Koordinatensystem an die Position right
translate(right.x, right.y, right.z);

// Kopie von left erstellen
PVector diff=new PVector(left.x, left.y, left.z);

// Differenzvektor berechnen
diff.sub(right);

// Koordinatensystem verschieben
translate(diff.x/2, diff.y/2, diff.z/2);
diff.normalize();

// Orientierung des mittleren Quaders festlegen
zero=new PVector(0, 0, 1);

// Achse und Winkel berechnen
float angle = acos(zero.dot(diff));
PVector axis = zero.cross(diff);

//Rotiere das Koordinatensystem
rotate(angle, axis.x, axis.y, axis.z);

// Rotiere um die Achse
rotateZ(map(mouseX, 0, width, -PI, PI));
drawAxes(50);
fill(50, 0, 200);
box(20);
popMatrix();
}

// Koordinatensystem zeichnen
void drawAxes(int scl) {
stroke(255, 0, 0);
line(-scl, 0, 0, scl, 0, 0);
text("+x", scl, 0, 0);
text("-x", -scl-30, 0, 0);
stroke(0, 255, 0);
line(0, -scl, 0, 0, scl, 0);
text("+y", 0, scl+30, 0);
text("-y", 0, -scl, 0);
stroke(0, 0, 255);
line(0, 0, -scl, 0, 0, scl);
text("+z", 0, 0, scl+30);
text("-z", 0, 0, -scl+30);
}

// Achse neu ausrichten
// für Javascript Mode auskommentieren
void keyPressed() {
if (key == ' ') {
left=new PVector(-width/4+random(-100, 100), random(-100, 100), random(-100, 100));
right=new PVector(width/4+random(-100, 100), random(-100, 100), random(-100, 100));
}
}

PVector – Einführung von Daniel Shiffman