みんな大好きBlenderスクリプトで恒星系(のように見える)配置を生み出そうという記事です。Blender2.80完全対応!
基本的にはイテレーションを回して物体に大量に配置する系のスクリプトですが、今回はマテリアルの設定などもしています。
1. 球体を大量に配置
とりあえず簡単なコードから。プリミティブの球体を自動的に並べてみます。
import bpy
import numpy as np
#現状をリセット
for mat in bpy.data.materials:
bpy.data.materials.remove(mat)
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
for obj in bpy.context.scene.objects:
bpy.data.objects.remove(obj)
#球体を生み出す数
number = 10
for i in range(number):
x = np.random.rand()*number*10-50
y = np.random.rand()*number*10-50
bpy.ops.mesh.primitive_uv_sphere_add(radius=np.random.rand()+1,location=(x,y,0))
2. なんちゃって惑星軌道を追加
それぞれの球体に、原点を中心としてその球体を通るようなリングを設定したら、なんとなく恒星系のようになりそうです。元々x
とy
で座標を指定していましたが、今後の計算の簡便のため、r
とtheta
の極座標で座標をします。
import bpy
import numpy as np
#現状をリセット
for mat in bpy.data.materials:
bpy.data.materials.remove(mat)
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
for obj in bpy.context.scene.objects:
bpy.data.objects.remove(obj)
#球体を生み出す数
number = 10
bpy.data.materials.new(name = 'orbit')
mat = bpy.data.materials['orbit']
mat.use_nodes = False
mat.diffuse_color = (1,1,0,1)
for i in range(number):
#極座標で計算
r = np.random.rand()*number*10+10
theta = np.random.rand()*np.pi*2
x = r*np.cos(theta)
y = r*np.sin(theta)
#こっちは惑星
bpy.ops.mesh.primitive_uv_sphere_add(radius=np.random.rand()+1,location=(x,y,0))
#こっちは軌道
bpy.ops.mesh.primitive_torus_add(major_radius=256,minor_radius=.1)
bpy.context.object.scale = (r/256,r/256,1)
ここではbpy.ops.mesh.primitive_torus_add
のAPIを用いています。リファレンスはここ。ドーナツの輪の半径をmajor_radius
、ドーナツの断面の半径をminor_radius
で指定しています。ここでmajor_radius
を0としているのは、なぜかこのAPIでは257以上の値を指定できないというよくわからない仕様があるせいで(最初わからなくてハマった)、半径256のドーナツをbpy.context.object.scale
によって拡大縮小することでr
に合わせるという風にしています。
3. マテリアルの指定
今回はマテリアルも設定していきます。Blender2.80になってから、マテリアルを設定する場合はほぼシェーダ必須となってしまいましたが、そこまでスクリプトの勉強が追い付いていないので今回はシェーダ使わないモードで解説させていただきます。スクリプトでマテリアルを設定する場合、bpy.data.materials.new
でマテリアルのデータを作成した後、対象オブジェクトにbpy.ops.object.material_slot_add()
でマテリアルスロットを追加し、そのスロットを対象にbpy.context.object.active_material
でそのマテリアルを設定する、という流れになります。
マテリアルのリファレンスはここですが、ノードを使う場合これは序の口であって、更に細かいノードのAPIをいじる必要があります。とりあえず今回はノードを使わない設定でいくので(mat.use_nodes = False
)、この中のdiffuse_color
、metallic
、roughness
だけ乱数で指定していきます。
GUIで言うとこの表示になります。なおこの表示はレンダーエンジンによっても変わり、上の図はCycles
のものになります。
import bpy
import numpy as np
#現状をリセット
for mat in bpy.data.materials:
bpy.data.materials.remove(mat)
for obj in bpy.data.objects:
bpy.data.objects.remove(obj)
for obj in bpy.context.scene.objects:
bpy.data.objects.remove(obj)
#球体を生み出す数
number = 10
#軌道のマテリアルだけ先に設定
bpy.data.materials.new(name = 'orbit')
mat = bpy.data.materials['orbit']
mat.use_nodes = False
mat.diffuse_color = (1,1,0,1)
for i in range(number):
r = np.random.rand()*number*10+10
theta = np.random.rand()*np.pi*2
x = r*np.cos(theta)
y = r*np.sin(theta)
#こっちは惑星
bpy.ops.mesh.primitive_uv_sphere_add(radius=np.random.rand()+1,location=(x,y,0))
#惑星の個別のマテリアルを設定
mat_name = 'material' + str(i)
bpy.data.materials.new(name = mat_name)
mat = bpy.data.materials[mat_name]
mat.use_nodes = False
mat.diffuse_color = np.random.rand(4)
mat.diffuse_color[3] = 1
mat.metallic = np.random.rand()
mat.roughness = np.random.rand()
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
#こっちは軌道
bpy.ops.mesh.primitive_torus_add(major_radius=100,minor_radius=.1)
bpy.context.object.scale = (r/100,r/100,1)
#前に設定した軌道マテリアルを適用
mat = bpy.data.materials['orbit']
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
#4 太陽(っぽいもの)の作成&レンダリング
せっかくなので、中心に太陽(っぽいもの)を設定します。
bpy.ops.mesh.primitive_uv_sphere_add(radius=4)
bpy.data.materials.new(name = 'sun')
mat = bpy.data.materials['sun']
mat.use_nodes = False
mat.diffuse_color = (1,.5,0,1)
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
惑星の半径や位置は乱数で指定しているので、スクリプトを回す度に違う恒星系らしきものが生成されます。Blenderのソリッド表示ではいい感じですが、この際レンダリングもしていきましょう。このままでは真っ暗なので中心にライトを設定してもよいですが、このニセ太陽のせいで光が遮られてしまいます。ここは手動でマテリアルを設定して、このニセ太陽自身が輝いているようにします。
シェーダノード。我々はこれに嫌でも慣れていかなければならない。
以下がカメラや背景等を設定してレンダリングしたもの。背景の星空はCGBeginner様よりお借りしました。