Licht und Schatten
Bei dreidimensionalen Applikationen gilt für jede Software genau wie im wirklichen Leben: »Ohne Licht sehen Sie nichts!« Das ist bei den spezialisierten Programmen wie Blender oder PoVRay genau so, wie auch in Processing.py. Daher möchte ich in folgendem Skript zeigen, welche Möglichkeiten der Beleuchtung es in Processing gibt und welche Auswirkung sie auf die Szene haben.
Dazu habe ich eine Kugel verschachtelt in einer Box erzeugt und sie in ein 3D-Fenster gesetzt. Sie ist im Grunde farblos, nur für eine Belichtung (lights()
) habe ich der Kugel eine hell- und der Box eine dunkelblaue Farbe verpaßt.
Bevor ich die Kugel und die Box zeichnen lasse, überprüfe ich, welche Beleuchtungsfunktion aktuell angewählt ist. Processing kennt sechs Beleuchtungfunktionen. Diese sind
noLights()
: Diese schaltet alle Beleuchtung aus und die dreidimensionalen Objekte wirken zweidimensional. Diese Funktion kann benutzt werden, um dreidimensionale Objekte mit zweidimensionalen Zeichnungen zu kombinieren.lights()
: Das ist die einfachste Beleuchtungsfunktion, die die Umgebung in ein neutrales, ambientes Licht taucht. Sie kann immer erst einmal für den Test der dreidiemnsionalen Objekte eingesetzt verwendet werden, bevor man sich an spektakulärere Beleuchtungsmodelle wagt.directionalLight(v1, v2, v3, nx, ny, nz)
: Diese Beleuchtungsfunktion besitzt sechs Parameter. Die ersten drei geben die Farbwerte an (es können je nach gewähltem Farbmode entweder RGB- oder HSB-Werte sein). Die letzten drei Werte geben jeweils die Richtung des Lichtes aus der x-, y, und/oder z-Richtung an. Direkte Richtungen sind0, -1, 0
nach oben,0, 1, 0
nach unten,1, 0, 0
nach rechts und-1, 0, 0
nach links. Analog sind die Werte für »Licht von vorne« und »Licht von hinten« einzustellen und durch Kombinationen der drei Parameter bekommt man auch Licht aus beliebigen Richtungen.ambientLight(v1, v2, v3)
taucht die Umgebung in ein ambientes Licht in der mitv1, v2, v3
spezifizierten Farbe (RGB oder HSB). Ambientes Licht wird meist mit anderen Lichtquellen kombiniert, um zum Beispiel die Schlagschatten der anderen, gerichteten Lichtquellen aufzuhellen.pointLight(v1, v2, v3, x, y, z)
setzt ein punktförmiges Licht in den Farbenv1, v2, v3
aus der Positionx, y, z
.spotLight(v1, v2, v3, x, y, z, nx, ny, nz, angle, concentration)
ergibt ein kegelförmiges Licht in den Varbenv1, v2, v3
von der Quellex, y, z
in die Richtungnx, ny, nz
mit dem Winkelangle
und der Intensitätconcentration
.
Alle Beleuchtungsfunktionen müssen innerhalb der draw()
-Funktion aufgerufen werden. Werden sie stattdessen in der setup
-Funktion aufgerufen, sind sie nur beim ersten Durchlauf wirksam. Daher habe ich das auch im Sketch so gehalten, wobei je nach gewähltem lightMode
die Beleuchtung gesetzt wird.
Licht aus – Spot an!
Die Beleuchtung kann man während der Sketch läuft mit der Tastatur auswählen. Die Tasten sind sprechend gewählt:
def keyPressed():
global lightMode, lightDirection
if key == "n":
lightMode = 0 # no lights
elif key == "l":
lightMode = 1 # lights
elif key == "d":
lightMode = 2 # directional light
elif key == "a":
lightMode = 3 # ambient light
elif key == "p":
lightMode = 4 # point light
elif key == "s":
lightMode = 5 # spot light
Warnung
Bevor man die Tasten drückt, sollte man darauf achten, daß das Graphikfenster von Processing.py im Vordergrund ist, also den Fokus besitzt. Denn sonst tippt man versehentlich gnadenlos Buchstaben in sein Skript und wundert sich, warum es anschließend nicht mehr läuft. Ich verstehe nicht ganz, warum im Python-Mode das Ausgabefenster beim Start des Programmes nicht automatisch den Fokus bekommt, wie das im Java-Mode von Processing der Fall ist?
Ist das direktionale Licht ausgewählt kann man zusätzlich noch mit den Pfeiltasten die Richtung des Lichtes bestimmen.
Quellcode
Wenn man bedenkt, daß in diesem Programm doch einiges passiert, ist der Quellcode immer noch sehr kurz. Das Nachvollziehen sollte auch keine besondere Mühe machen, schließlich wird Python ja oft und zu Recht als lauffähiger Pseudocode bezeichnet.
lightMode = 0
lightDirection = 0
def setup():
size(640, 480, P3D)
frame.setTitle("Licht und Schatten")
def draw():
global lightMode, lightDirection
background(0)
# Lichter setzen
if lightMode == 0:
noLights()
elif lightMode == 1:
lights()
elif lightMode == 2:
if lightDirection == 0:
directionalLight(255, 128, 0, 0, -1, 0) # up
elif lightDirection == 1:
directionalLight(0, 255, 0, 1, 0, 0) # right
elif lightDirection == 2:
directionalLight(255, 0, 255, 0, 1, 0) # down
elif lightDirection == 3:
directionalLight(0, 255, 255, -1, 0, 0) # left
elif lightMode == 3:
ambientLight(0, 255, 255)
elif lightMode == 4:
pointLight(255, 255, 0, 100, height*0.3, 100)
elif lightMode == 5:
spotLight(128, 255, 128, 800, 20, 300, -1, .25, 0, PI, 2)
else:
noLights()
# Kugel und Box zeichnen
with pushMatrix():
translate(width/2, height/2)
with pushMatrix():
rotateY(radians(frameCount))
fill(255)
if lightMode == 1:
fill(151, 255, 255)
noStroke()
sphere(160)
with pushMatrix():
rotateZ(radians(frameCount))
rotateX(radians(frameCount/2.0))
fill(255)
if lightMode == 1:
fill(0, 0, 139)
noStroke()
box(240)
def keyPressed():
global lightMode, lightDirection
if key == "n":
lightMode = 0 # no lights
elif key == "l":
lightMode = 1 # lights
elif key == "d":
lightMode = 2 # directional light
elif key == "a":
lightMode = 3 # ambient light
elif key == "p":
lightMode = 4 # point light
elif key == "s":
lightMode = 5 # spot light
if key == CODED:
if keyCode == UP:
lightDirection = 0
elif keyCode == RIGHT:
lightDirection = 1
elif keyCode == DOWN:
lightDirection = 2
elif keyCode == LEFT:
lightDirection = 3
Credits
Dieses Beispielprogramm folgt einer Idee aus dem Bucn »Processing 2: Creative Programming Cookbook« von Jan Vantomme. Ich habe sie geringfügig überarbeitet und vom Processing 2 Java-Mode in den Python-Mode von Processing 3 umgeschrieben.
Literatur
- Jan Vantomme: Processing 2: Creative Programming Cookbook, Birmingham (Packt Publishing), 2012