Blenderのスクリプトで、万博で話題になったアレのような何かを生み出していきます。乱数を基にプロシージャルに生み出しているので、無数のパターンのアレを生み出せます。Blenderのバージョンは2.8です。
第1段階 球の生成
import bpy
import numpy as np
# ここで赤マテリアルを設定
bpy.data.materials.new(name = 'red')
mat_red = bpy.data.materials['red']
mat_red.use_nodes = False
mat_red.diffuse_color = (1,0,0,1)
# 球の生成
bpy.ops.mesh.primitive_uv_sphere_add(radius=1,location=(0,0,0))
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat_red
プリミティブの生成と簡単なマテリアルの指定で赤い球を生み出していきます。bpy.ops.mesh.primitive_uv_sphere_add
でUV球を生み出すことができます。
第2段階 球の増殖
この赤球を無数に生み出したいところですが、単なるfor文でやるよりも、何らかの連続性があった方がいのちが輝いてる感が出てくるので、ここは再帰関数を使って実装していくことにします。再帰関数とは自分自身をコピーしながら生み出してくようなプログラムのことです(超適当)。
import bpy
import numpy as np
# ここで赤マテリアルを設定
bpy.data.materials.new(name = 'red')
mat_red = bpy.data.materials['red']
mat_red.use_nodes = False
mat_red.diffuse_color = (1,0,0,1)
def add_sphere(r,x,y,z,count):
if count > 20: #これがないと無限ループ
return
bpy.ops.mesh.primitive_uv_sphere_add(radius=r,location=(x,y,z))
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat_red
add_sphere(r,x+1,y,z,count+1) #自分自身のコピーを呼び出す
return
add_sphere(1,0,0,0,1)
add_sphere
という関数で球を生成していますが、この中で更にadd_sphere
という関数を呼び出しています。しかしまったく同じではなく、自分自身よりxに1ずれたコピーを生み出します。そのコピーはさらに1ずらした……という無限ループが発生し、連続性を持ったプロセスをシンプルに記述できます。これは単に横移動しただけですが、例えばadd_sphere(1.1*r,y,x+1,z,count+1)
のようなコマンドにすれば、以下のようになります。
生命の神秘を感じますね。
第3段階 ランダム性を加える
しかしこれだと規則的すぎていまいち輝きを感じません。元ネタを見ると、いくつもの大きさが異なる赤球がつらなってリング状になっているようです。やはり大きさに多少バリエーションがあった方が生命感が出るようです。お互いの発生位置も、親から子にぎりぎり接しながら伸びていくような形にします。
import bpy
import numpy as np #numpyを追加インポート
# ここで赤マテリアルを設定
bpy.data.materials.new(name = 'red')
mat_red = bpy.data.materials['red']
mat_red.use_nodes = False
mat_red.diffuse_color = (1,0,0,1)
def add_sphere(r,x,y,z,count):
if count > 20: #これがないと無限ループ
return
bpy.ops.mesh.primitive_uv_sphere_add(radius=r,location=(x,y,z))
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat_red
#ランダムに経度と緯度を指定
theta = np.random.rand()*np.pi*2
phi = (np.random.rand()-0.5)*np.pi
#新しい座標を指定
nr = r*(0.5 + np.random.rand())#子の球は親の0.5倍~1.5倍
nx = x+(r+nr)*np.cos(theta)*np.cos(phi)
ny = y+(r+nr)*np.sin(theta)*np.cos(phi)
nz = z+(r+nr)*np.sin(phi)
add_sphere(nr,nx,ny,nz,count+1)#自分自身のコピーを呼び出す
return
add_sphere(1,0,0,0,1)
第4段階 目玉を加える
キュートな目玉を加えていきます。テクスチャを設定できれば楽ですが、ここはプリミティブだけで描画することにします。
だいぶ輝いてきました。あとは少しシェーディングを整えてレンダリングすれば、冒頭のようないのち輝く画像を生成することができます。
コードは、ちょっと冗長な部分が多くなるので、一部を関数化して簡略化しています。
import bpy
import numpy as np #numpyを追加インポート
# マテリアルの設定を3つ書くのは長くなるので関数化
def create_material(color_name,r,g,b):
bpy.data.materials.new(name = color_name)
mat = bpy.data.materials[color_name]
mat.use_nodes = False
mat.diffuse_color = (r,g,b,1)
return mat
mat_red = create_material('red',1,0,0)
mat_white = create_material('white',1,1,1)
mat_eye = create_material('eye',0,1.5,1)
# 座標の指定を3つ書くのは長くなるので一部関数化
def next_params(x,y,z,theta,phi,dist):
nx = x + dist*np.cos(theta)*np.cos(phi)
ny = y + dist*np.sin(theta)*np.cos(phi)
nz = z + dist*np.sin(phi)
return nx,ny,nz
# UV球の生成を3つ書くのは長くなるので一部関数化
def create_sphere(r,x,y,z,mat):
bpy.ops.mesh.primitive_uv_sphere_add(radius=r,location=(x,y,z))
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
return
# ついでに角度の生成も関数化
def create_angular():
theta = np.random.rand()*np.pi*2
phi = (np.random.rand()-0.5)*np.pi
return theta,phi
def add_sphere(r,x,y,z,count):
if count > 20: #これがないと無限ループ
return
create_sphere(r,x,y,z,mat_red)
theta,phi = create_angular()
nr = r*(0.5 + np.random.rand())
nx,ny,nz = next_params(x,y,z,theta,phi,r+nr)
add_sphere(nr,nx,ny,nz,count+1)#自分自身のコピーを呼び出す
#白目を生成(これはただの関数)
theta,phi = create_angular()
nx,ny,nz = next_params(x,y,z,theta,phi,r/3)
create_sphere(0.8*r,nx,ny,nz,mat_white)
#瞳を生成(これはただの関数)
nx,ny,nz = next_params(x,y,z,theta,phi,r/2)
create_sphere(0.65*r,nx,ny,nz,mat_eye)
return
add_sphere(1,0,0,0,1)
第5段階 パラメータ調整
途中で二股に分かれるようにしたり、子の球のサイズを小さくするようにすると、また違った生命の美しさを見ることができます。
興味のある方は、色々とパラメータを変えて遊んでみるのも良いかもしれません。ただし、count > 20
のあたりを変更すると、プログラムの組み方によってはオブジェクト量が指数関数的に増えてBlenderがフリーズすることがあるのでご注意ください。