Skriptování v Blenderu
Linuxdays 2016

Lukáš Bařinka

Obsah

  1. Co je to blender?
  2. Co je to python?
  3. Jak spolu blender a python souvisí?
  4. Jak je možné takové souvislosti využít?
  5. Praktická ukázka
  6. Lze to i bez programování?

Blender

Blender is the free and open source 3D creation suite. It supports the entirety of the 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, even video editing and game creation.

Python

Python is a programming language that lets you work quickly and integrate systems more effectively

Jak spolu blender a python souvisí

Python rozhraní (API) pro přístup k

  • Context Access (bpy.context)
  • Data Access (bpy.data)
  • Operators (bpy.ops)
  • Types, Utilities, Path, Application, Property

Jak spolu blender a python souvisí

  • Konzole (in/out/err)
  • Informační okno/terminál (out/err)
  • Tool tips
  • Editor

K čemu je to dobré?

  • Rozšíření (add-ons)
    Menu: File > User preferences > Add-ons
  • Automatizace činností
  • Tvorba scény

Ukázka použití konzole

  1. Vybrat objekt (kostku)
  2. bpy.context.object.location
  3. bpy.context.object.location = [0, 1, 2]
  4. bpy.context.object.location.x += 1
  5. Přidat a vybrat další objekty
  6. bpy.context.selected_objects

Context / Data / Ops

Vybrat objekt (kostku)


bpy.context.object.location.x += 1
bpy.data.scenes['Scene'].objects['Cube'].location.x += 1
bpy.ops.transform.translate(value=(1, 0, 0))
							

Editor


import bpy
import random
import math

bpy.ops.mesh.primitive_monkey_add()
bpy.ops.object.modifier_add(type='SUBSURF')
bpy.context.object.modifiers["Subsurf"].levels = 2
bpy.ops.object.shade_smooth()

alpha=random.uniform(0,2*math.pi)
bpy.ops.transform.translate(value=(0,alpha,math.sin(alpha)))
							

Run script (Alt+P)

Cyklus


import bpy
import math

alpha = 0
radius = 5

while alpha < 2 * math.pi:
   bpy.ops.mesh.primitive_monkey_add()
   bpy.ops.transform.translate(value=
      (radius*math.sin(alpha), radius*math.cos(alpha), 0)
   )
   bpy.ops.transform.rotate(value=alpha)
   alpha += math.pi / 4
							

Animace textu

  1. Vyčistit stávající scénu (objekty, křivky, materiály, fonty)
  2. Nastavit scénu, kameru, render
  3. Vytvořit objekty ve scéně (texty, čáry) a materiály
  4. Nastavit klíčové snímky a přechody mezi nimi
  5. Postupně měnit texty podle dat v souboru a renderovat animace

Kompletní skript

Intro


import bpy
import csv
import os

# nastavit cerne pozadi
bpy.context.scene.world.horizon_color = (0, 0, 0)

							

Vyčištění stávající scény


# smazat kostku a texty
for ob in bpy.context.scene.objects:
    #print('OB: %s %s\n' % (ob.type, ob.name))
    if ( ob.type == 'MESH' or ob.type == 'FONT' ):
        ob.select = True
    else: 
        ob.select = False
bpy.ops.object.delete()

# smazat texty
for t in bpy.data.curves:
    bpy.data.curves.remove(t)
							

Smazat staré materiály


# smazat stavajici a vytvorit nove materialy
for mat in bpy.data.materials:
    mat.user_clear()
    bpy.data.materials.remove(mat)
							

Vytvořit nové materiály


mat_white = bpy.data.materials.new('White')
mat_white.diffuse_color = (1,1,1)
mat_white.diffuse_intensity = 1
mat_white.use_shadeless = True
mat_gray = bpy.data.materials.new('Gray')
mat_gray.diffuse_color=  (0.5,0.5,0.5)
mat_gray.diffuse_intensity = 1
mat_gray.use_shadeless = True
mat_silver = bpy.data.materials.new('Silver')
mat_silver.diffuse_color=  (1.5,1.5,1.5)
mat_silver.diffuse_intensity = 1
mat_silver.use_shadeless = True
							

Smazat staré a vytvořit nové fonty


# smazat a nastavit fonty
for f in bpy.data.fonts:
    f.user_clear()
    bpy.data.fonts.remove(f)
font_normal = bpy.data.fonts.load('/usr/share/fonts-roboto-fontface/fonts/Roboto-Thin.ttf')
font_black = bpy.data.fonts.load('/usr/share/fonts-roboto-fontface/fonts/Roboto-Black.ttf')
							

Nastavení scény


# nastaveni sceny
scene = bpy.data.scenes['Scene']
scene.frame_start = 1
fps = 24
scene.frame_end = 6*fps

# nastaveni renderu
scene.render.resolution_x = 1920
scene.render.resolution_y = 1080
scene.render.resolution_percentage = 100
scene.render.image_settings.file_format = 'PNG'
							

Nastavení kamery


# nastaveni kamery
camera = scene.objects['Camera'] 
camera.location = (0,0,1)
camera.rotation_euler = (0,0,0)
camera.data.type = 'ORTHO'
camera.data.ortho_scale = 10
							

Tvorba čáry


# vytvoreni cary
bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
line = bpy.data.scenes['Scene'].objects['Plane']
line.name = 'line'
line.data.materials.append(mat_silver)
line.scale = (0.01, 0.75, 0)
							

Vytvoření textu 1


# vytvoreni textu 1
bpy.ops.object.text_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
location = bpy.data.scenes['Scene'].objects['Text']
location.name = 't.location'
location.data.materials.append(mat_white)
location_x = 0.5
location_y = 0.5
# zmena textu
text_location = location.data
text_location.body = 'Linuxdays'
text_location.size = 0.5
text_location.font = font_black
							

Vytvoření textu 2


# vytvoreni textu 2
bpy.ops.object.text_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
country = bpy.data.scenes['Scene'].objects['Text']
country.name = 't.country'
country.data.materials.append(mat_gray)
country_x = -0.25
country_y = 0
# zmena textu
text_country = country.data
text_country.body = 'Česká Republika'
text_country.size = 0.5
text_country.font = font_normal
							

Vytvoření textu 3


# vytvoreni textu 3
bpy.ops.object.text_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
date = bpy.data.scenes['Scene'].objects['Text']
date.name = 't.date'
date.data.materials.append(mat_gray)
date_x = 0.25
date_y = -0.5
# zmena textu
text_date = date.data
text_date.body = 'Říjen 2016'
text_date.size = 0.5
text_date.font = font_normal
							

Střed textu


# stredni pozice textu
center_x = 0.75
center_y = -0.75
							

Začátek animace - materiál


# zacatek animace
scene.frame_current = 1
mat_white.diffuse_color = (0,0,0)
mat_white.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_gray.diffuse_color = (0,0,0)
mat_gray.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_silver.diffuse_color = (0,0,0)
mat_silver.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_end*0.3)
							

Začátek animace - pozice


location.location = (center_x+location_x,center_y+location_y,0)
location.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
country.location = (center_x+country_x,center_y+country_y,0)
country.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
date.location = (center_x+date_x,center_y+date_y,0)
date.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
line.location = (center_x-0.3, center_y+1, 0)
line.keyframe_insert(
   data_path='location', index=-1, frame=scene.frame_end*0.3)
							

Polovina animace - materiál


# polovina animace
scene.frame_current = scene.frame_end/2
mat_white.diffuse_color = (1,1,1)
mat_white.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_gray.diffuse_color = (0.5,0.5,0.5)
mat_gray.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_silver.diffuse_color = (1,1,1)
mat_silver.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
							

Konec animace - materiál


# konec animace
scene.frame_current = scene.frame_end
mat_white.diffuse_color = (0,0,0)
mat_white.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_gray.diffuse_color = (0,0,0)
mat_gray.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_current)
mat_silver.diffuse_color = (0,0,0)
mat_silver.keyframe_insert(
   data_path='diffuse_color', index=-1,
   frame=scene.frame_end*0.7)
							

Konec animace - pozice


location.location = (center_x-location_x,center_y+location_y,0)
location.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
country.location = (center_x-country_x,center_y+country_y,0)
country.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
date.location = (center_x-date_x,center_y+date_y,0)
date.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_current)
line.location = (center_x-0.3, center_y-0.5, 0)
line.keyframe_insert(data_path='location', index=-1,
   frame=scene.frame_end*0.7)
							

Interpolace materiálu


# prubeh animace materialu silver
mat_silver.animation_data.action.fcurves[0].\
   keyframe_points[0].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[1].keyframe_points[0].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[2].keyframe_points[0].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[0].keyframe_points[1].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[1].keyframe_points[1].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[2].keyframe_points[1].interpolation = 'EXPO'
mat_silver.animation_data.action.fcurves[0].\
   keyframe_points[1].easing = "EASE_OUT"
mat_silver.animation_data.action.fcurves[1].keyframe_points[1].easing = "EASE_OUT"
mat_silver.animation_data.action.fcurves[2].keyframe_points[1].easing = "EASE_OUT"
							

Cyklus přes texty v souboru


with open(BASE+'/list') as file:
    reader = csv.reader(file, delimiter='|')
    for row in reader:
        text_location.body = row[1]
        text_country.body = row[2]
        text_date.body = row[3]      
        # render
        dir = BASE+'/title_{name:s}'.format(name=row[0])
        if not os.path.exists(dir):
            os.makedirs(dir)
        for frame in range(1, scene.frame_end+1):
            scene.frame_current = frame
            scene.render.filepath = \
               '{dir:s}/{frame:05d}.png'.format(dir=dir,frame=frame)
            bpy.ops.render.render( write_still=True )
							

Jiná cesta ke skriptování

  • Bez nutnosti klasického programování (ale s možností)
  • Grafické skriptování (propojování uzlů)
  • Animation Nodes (Video tutoriály)

Děkuji za pozornost