Low-Poly-Motiv: Wolke

Lernziel

Was kann modelliert werden? Das wohl häufigste Motiv sind Landschaften mit den folgenden Objekten, die wir zum Teil auch erzeugen wollen:

  • Wolken
  • Steine
  • Bäume
  • Berge
  • Gras
  • Wasser

Handlungsanweisungen/Aufgaben

  1. Richte Dir Blender für das Skripting als auch für das Rendern der Zwischenergebnisse ein.
  2. Kopiere/Erstelle Dir das Grundgerüst für ein Skript (siehe auch die Muster im Anhang)
  3. Erstelle über die verfügbaren Menüs einen Baum der sich aus aus einem Zylinder und einer Kugel zusammensetzt.

Wolken und Steine

Fangen wir mit Steinen und Wolken an, die später auf der noch zu erstellenden Szene platziert werden können. Sie unterscheiden sich lediglich in Größe und Farbe. Später können wir sie eventuell noch als Baumkrone wiederverwenden.

Fügen wir im ersten Schritt eine ICO-Sphere hinzu. Sie sollte den Vorzug vor der UV-Sphere erhalten, weil sie aus Dreiecken geformt ist und sich damit besser für unregelmäßige Strukturen eignet. Hier erst einmal das Grundgerüst:

Das Gerüst

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!bpy
"""
Name: 'Cloud-Generator'
Blender: 2.7x
Group: 'Low poly'
Tooltip: 'Part of a Low-Poly-Skripts collection'
"""
import bpy


class Cloud():
    """Create a cloud as low poly"""

    def __init__(self):
        pass

    def setColor(self):
        pass

    def new(self):
        """ constuction of a new cloud """
        pass

    def remove(self):
        """ Delete a cloud or all"""
        pass

if __name__ == "__main__":

    # switch to object mode, if nessasary
    if bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
    cloud = Cloud()


Die erste Wolke

Was jetzt noch fehlt, sind die konkreten Anweisungen, um solch ein Bild erzeugen zu können:

../../../../_images/cloud.png

    def new(self):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(-3, 10, 8))

Die Anweisung für das Erzeugen eines einfachen Objektes findet man an den unterschiedlichsten Stellen. Zur Erinnerung nochmal eine kleine Liste:

  • wenn man es über das Add-Menü mit der Maus einfügt, steht es schon im Tool-Tip
  • klickt man auf den Tool-Tip erhält man die Online-Doku
  • hat man die Neuanlage ausgeführt, steht der komplette Befehl im Info-Fenster
  • über die Hilfe in der Liste der Operatoren suchen (Operator Sheat Sheet)
  • im Konsolen-Fenster kann man sich durch die Objekt-Hierarchie wuseln…
  • in diversen Suchmaschinen den richtigen Begriff eingeben
  • Freunde und Bekannte fragen
  • ein Buch kaufen

Wenn man den Befehl aus dem Info-Fenster kopiert (re. Mausklick markiert die Zeile, copy & paste), braucht man nicht alle Parameter, es reicht der Parameter location.

Ein Name für das Objekt

Bei einer Vielzahl von Objekten ist die Auswahl über den Namen recht nützlich. Also vergeben wir einen passenden Namen. Vorher kürzen wir den etwas langen Modulpfad noch ab und sparen damit Tipparbeit für die nachfolgenden Zeilen.


    def new(self):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(-3, 10, 8))
        obj = bpy.context.object
        obj.name = "cloud"

Alle auf einen Platz

Wenn wir jetzt Wolken hinzufügen, werden sie immer an der gleichen Position erzeugt. Zum ersten mal generieren wir deshalb zufällige Werte, die für die Positionierung verwendet werden.

  1. Das Modul random importieren – es liefert die Methoden für Zufallszahlen. Dieses Modul werdern wir jetzt oft für diesen Trick verwenden.

    import random
    
  2. Die Zahlen für die Postionierung der Objekte verwenden. Nicht unbedingt erforderlich, aber später besser lesbar sind die in Ganzzahlen umgewandelten Werte. Die Methode erhält auch zwei vorbelegte Parameter (min und max, siehe Zeilen 5-7).

    1
    2
    3
    4
    5
    6
    7
    8
    9
        def new(self, min=0, max=1):
            """ constuction of a new cloud """
            bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
            obj = bpy.context.object
            obj.name = "cloud"
            obj.location.x = int(random.uniform(min +10 * -1, max + 10))
            obj.location.y = int(random.uniform(min, max)) * 10
            obj.location.z = int(random.uniform(min, max +3 )) * 10
            
    

Eine variable Größe

Noch haben alle Wolken die gleiche Größe. Das soll sich mit den nächsten Anweisungen ändern (Zeilen 10-12).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

    def new(self, min=0, max=1):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "cloud"
        obj.location.x = int(random.uniform(min +10 * -1, max + 10))
        obj.location.y = int(random.uniform(min, max)) * 10
        obj.location.z = int(random.uniform(min, max +3 )) * 10
        
        obj.scale[0] = int(random.uniform(min + 1, max + 3)) 
        obj.scale[1] = int(random.uniform(min + 1, max + 3))
        obj.scale[2] = int(random.uniform(min + 1, max + 4))

Aufräumen

Wer bis hierher viele Wolken platziert hat, wird die zunehmende Zahl von Objekten bemerkt haben. Im Gerüst ist schon eine Methode zum entfernen vorbereitet.

Hier nun die Umsetzung. Wir prüfen, ob mit dem Aufruf ein Name übergeben wurde. Wenn ja, dann wählen wir das Objekt mit dem gegebenen Namen, wenn kein Name mitgegeben wurde, dann markieren wir alle Objekte, deren Name mit cloud beginnt. Danach wird gelöscht.

Die Wolkenform ändern

Noch sieht man den Wolken die Ursprungsform einer Kugel an. Das soll sich nun mit dem Einsatz eines Modifiers ändern. Dazu schalten wir kurz vom Objekt- in den Edit-Modus um, denn der Subdivide-Befehl funktioniert nur im Edit-Modus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    def new(self, min=0, max=1):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "cloud"
        obj.location.x = int(random.uniform(min +10 * -1, max + 10))
        obj.location.y = int(random.uniform(min, max)) * 10
        obj.location.z = int(random.uniform(min, max +3 )) * 10
        
        obj.scale[0] = int(random.uniform(min + 1, max + 3)) 
        obj.scale[1] = int(random.uniform(min + 1, max + 3))
        obj.scale[2] = int(random.uniform(min + 1, max + 4))

        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.object.editmode_toggle()
        
        bpy.ops.object.modifier_add(type='DISPLACE')
        STUCCI_TEX = bpy.data.textures.new("Stucci tex", type="STUCCI")
        obj.modifiers["Displace"].texture = STUCCI_TEX
        obj.modifiers["Displace"].strength = .2

Von Grau zu Blau

Jedes Objekt soll auch eine Farbe bekommen. Welche das ist, überlassen wir wieder dem Zufall (Zeilen 24-31). Wir versuchen die Farbwerte aber im blauen Bereich zu belassen. Es können aber auch ganz andere Farben sein, schau einfach mal öfter aus dem Fenster!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    def new(self, min=0, max=1):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "cloud"
        obj.location.x = int(random.uniform(min +10 * -1, max + 10))
        obj.location.y = int(random.uniform(min, max)) * 10
        obj.location.z = int(random.uniform(min, max +3 )) * 10
        
        obj.scale[0] = int(random.uniform(min + 1, max + 3)) 
        obj.scale[1] = int(random.uniform(min + 1, max + 3))
        obj.scale[2] = int(random.uniform(min + 1, max + 4))

        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.object.editmode_toggle()
        
        bpy.ops.object.modifier_add(type='DISPLACE')
        STUCCI_TEX = bpy.data.textures.new("Stucci tex", type="STUCCI")
        obj.modifiers["Displace"].texture = STUCCI_TEX
        obj.modifiers["Displace"].strength = .2

        MATERIAL_CLOUD = bpy.data.materials.new('cloud')
        self.setColor(obj,
                      MATERIAL_CLOUD,
                      (random.uniform(0, .3),
                       random.uniform(0, .3),
                       random.uniform(0.3, 1)))  

Fünf Wolken bitte…

Damit ist der Wolken-Generator schon fast fertig. Wir justieren ihn so, dass folgende Schritte ablaufen:

  • löschen aller vorhandenen Wolken
  • Neuanlage von fünf neuen Wolken
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
if __name__ == "__main__":

    # switch to object mode, if nessasary
    if bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
    cloud = Cloud()
    cloud.remove()
    for i in range(5):
        cloud = Cloud()
        cloud.new()

../../../../_images/five-clouds1.png

Das Skript komplett

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!bpy
"""
Name: 'Cloud-Generator'
Blender: 2.7x
Group: 'Low poly'
Tooltip: 'Part of a Low-Poly-Skripts collection'
"""
import bpy
import random

class Cloud():
    """Create a cloud as low poly"""

    def __init__(self):
        pass

    def setColor(self, obj, material, color):
        material.diffuse_color = color
        material.specular_hardness = 200
        obj.data.materials.append(material)

    def new(self, min=0, max=1):
        """ constuction of a new cloud """
        bpy.ops.mesh.primitive_ico_sphere_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.name = "cloud"
        obj.location.x = int(random.uniform(min +10 * -1, max + 10))
        obj.location.y = int(random.uniform(min, max)) * 10
        obj.location.z = int(random.uniform(min, max +3 )) * 10
        
        obj.scale[0] = int(random.uniform(min + 1, max + 3)) 
        obj.scale[1] = int(random.uniform(min + 1, max + 3))
        obj.scale[2] = int(random.uniform(min + 1, max + 4))

        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.subdivide()
        bpy.ops.mesh.subdivide()
        bpy.ops.object.editmode_toggle()
        
        bpy.ops.object.modifier_add(type='DISPLACE')
        STUCCI_TEX = bpy.data.textures.new("Stucci tex", type="STUCCI")
        obj.modifiers["Displace"].texture = STUCCI_TEX
        obj.modifiers["Displace"].strength = .2

        MATERIAL_CLOUD = bpy.data.materials.new('cloud')
        self.setColor(obj,
                      MATERIAL_CLOUD,
                      (random.uniform(0, .3),
                       random.uniform(0, .3),
                       random.uniform(0.3, 1)))  

    def remove(self, name=None):
        """ Delete a cloud or all"""

        if name:
            bpy.ops.object.select_pattern(pattern=name)
        else:
            bpy.ops.object.select_pattern(pattern="cloud*")
            
        bpy.ops.object.delete()     


if __name__ == "__main__":

    # switch to object mode, if nessasary
    if bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
    cloud = Cloud()
    cloud.remove()
    for i in range(5):
        cloud = Cloud()
        cloud.new()