Low-Poly-Motiv: Untergrund

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

  • Berge/Untergund

Handlungsanweisungen/Aufgaben

  1. Ändere die Parameter!

  2. Wie würde eine Vertiefung mit Wasser generiert werden?

  3. Wie kann ein Teil des Berges mit einer eisbedeckten Spitze versehen werden?

Ein Beispiel:

Wie kann per Skript generiert werden, was im Bild gelb bzw. orange wie der Uluṟu aussieht. Bald sind wir schlauer…

../../../../_images/landscape.png

Das Gerüst

Wieder ist das Gerüst einer Klasse der Ausgangspunkt.

 1#!bpy
 2"""
 3Name: 'Landscape-Generator'
 4Blender: 2.7x
 5Group: 'Low poly'
 6Tooltip: 'Part of a Low-Poly-Skripts collection'
 7"""
 8import bpy
 9
10
11class Landscape():
12    """Create a landscape as low poly"""
13
14    def __init__(self):
15        pass
16
17    def setColor(self):
18        pass
19
20    def new(self):
21        """ constuction of a new landscape """
22        pass
23
24    def remove(self):
25        """ Delete a landscape or all"""
26        pass
27
28if __name__ == "__main__":
29
30    # switch to object mode, if nessasary
31    if bpy.ops.object.mode_set.poll():
32        bpy.ops.object.mode_set(mode='OBJECT')
33    ground = Landscape()
34
35

Die Grundplatte

Sie wird aus einer plane erzeugt und anschließend auf 100x100 Einheiten vergrößert.

    def new_area(self):
        """ constuction of a new landscape """
        scn = bpy.context.scene
        bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
        obj = bpy.context.object
        obj.scale[0] = 100
        obj.scale[1] = 100
        obj.name = "ground"

Ein kleines Erdbeben

Wir teilen die Grundplatte mehrfach und damit es nicht zu rechteckig wird, verwenden wir den Parameter fractal. Es is nur ein kleines Erdbeben, denn wir heben einzelne Punkte nur minimal an.

        bpy.ops.object.mode_set(mode='EDIT')
        val = 1
        for i in range(1, 5):
            val = val + random.randint(1, 3)
            bpy.ops.mesh.subdivide(fractal=val)

Der Brocken

Ein kleiner Teil der Grundplatte soll sich deutlich vom normalen Niveau abheben, was in mehrerern Schritten erfolgt. Verwendung findet ein bmesh, die neuere Version zur Verwaltung von Koordinaten.

        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.object.editmode_toggle()
        me = obj.data
        i = random.randint(0, len(me.vertices))
        obj.data.vertices[i].select = True
        bpy.ops.object.mode_set(mode='EDIT')
        for i in range(5):
            bpy.ops.mesh.select_more()
 
        bm = bmesh.from_edit_mesh(obj.data)
        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 11
                
        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.9, .9, 0))
 
        for vertex in bm.verts:
            if vertex.select:
                vertex.co.z += 15
 
        bmesh.update_edit_mesh(obj.data)
        bpy.ops.transform.resize(value=(.8, .8, 0))

Das Skript als Ganzes

 1#!bpy
 2"""
 3Name: 'Landscape-Generator'
 4Blender: 2.7x
 5Group: 'Low poly'
 6Tooltip: 'Part of a Low-Poly-Skripts collection'
 7"""
 8import bpy
 9import random
10import bmesh
11
12class Landscape():
13    """Create a landscape as low poly"""
14
15    def __init__(self):
16        self.top = 0
17
18    def setColor(self, obj, material, color):
19        material.diffuse_color = color
20        material.specular_hardness = 200
21        obj.data.materials.append(material)
22
23    def new_area(self):
24        """ constuction of a new landscape """
25        scn = bpy.context.scene
26        bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
27        obj = bpy.context.object
28        obj.scale[0] = 100
29        obj.scale[1] = 100
30        obj.name = "ground"
31        bpy.ops.object.mode_set(mode='EDIT')
32        val = 1
33        for i in range(1, 5):
34            val = val + random.randint(1, 3)
35            bpy.ops.mesh.subdivide(fractal=val)
36        bpy.ops.object.mode_set(mode='OBJECT')
37        bpy.ops.object.mode_set(mode='EDIT')
38        bpy.ops.mesh.select_all(action='TOGGLE')
39        bpy.ops.object.editmode_toggle()
40        me = obj.data
41        i = random.randint(0, len(me.vertices))
42        obj.data.vertices[i].select = True
43        bpy.ops.object.mode_set(mode='EDIT')
44        for i in range(5):
45            bpy.ops.mesh.select_more()
46 
47        bm = bmesh.from_edit_mesh(obj.data)
48        for vertex in bm.verts:
49            if vertex.select:
50                vertex.co.z += 11
51                
52        bmesh.update_edit_mesh(obj.data)
53        bpy.ops.transform.resize(value=(.9, .9, 0))
54 
55        for vertex in bm.verts:
56            if vertex.select:
57                vertex.co.z += 15
58 
59        bmesh.update_edit_mesh(obj.data)
60        bpy.ops.transform.resize(value=(.8, .8, 0))
61               
62    def remove(self, name=None):
63        """ Delete a ground or all"""
64
65        if name:
66            bpy.ops.object.select_pattern(pattern=name)
67        else:
68            bpy.ops.object.select_pattern(pattern="ground*")
69
70        bpy.ops.object.delete()
71
72
73if __name__ == "__main__":
74
75    # switch to object mode, if nessasary
76    if bpy.ops.object.mode_set.poll():
77        bpy.ops.object.mode_set(mode='OBJECT')
78    ground = Landscape()
79    ground.remove()
80    ground.new_area()