ミルククラウンをBlenderのpythonスクリプトで作成
こんにちは、株式会社CFlatです。
blenderの流体シミュレーションを試してみたかったので、pythonスクリプトでミルククラウンを作ってみることにしました。
適当にオブジェクトを配置して、特にパラメーターを凝らなくても下図にあるミルククラウン風の結果が出たのでちょっと驚きました。
それではここからpythonファイルの解説をしていきます。最後に動画も載せておきます。
ミルクのマテリアル設定
ミルクのマテリアルはここからダウンロードしました。
ダウンロードしたmilk.blendをpythonファイルと同じディレクトリにおいてappendすればmilk.blend内のマテリアルが利用できます。
流体シミュレーションの設定
このpythonファイルを実行するときは重いので注意して下さい。私の環境で5分ほど掛かります。
もっと軽くしたければドメインのResolution値を小さくしたり、フレーム数を小さくして下さい。
流体のパラメーターはpresetにあったoilの値を使用しました。
流体シミュレーションの設定はここに詳しく書かれています。
流体シミュレーションの終了通知
bpy.ops.fluid.bake("INVOKE_DEFAULT")で流体シミュレーションが開始されますが別スレッドで実行されるため、
そのままだと流体シミュレーションが終わる前に動画作成を行ってしまいます。
流体シミュレーションの終了通知が分からなかったので、今回はキャッシュファイルの存在を調べることで代用しました。
スクリプトを実行する際はpythonファイルと同じディレクトリにcacheフォルダを作成してから実行する必要があります。
誰かもっと良い方法を知っていれば教えて下さい。
pythonスクリプト
下記が実際に使用したpythonスクリプトです。
ドメインを作成して、水たまりと水滴をFluidに設定するだけで簡単に流体シミュレーションが実行できます。
pythonファイルの使用方法は以前の記事を参考にして下さい。
# -*- coding: utf-8 -*- import os import bpy import math import time #デフォルトで存在しているメッシュやマテリアルを全て削除 for item in bpy.context.scene.objects: if item.type == 'MESH': bpy.context.scene.objects.unlink(item) for item in bpy.data.objects: if item.type == 'MESH': bpy.data.objects.remove(item) for item in bpy.data.meshes: bpy.data.meshes.remove(item) for item in bpy.data.materials: bpy.data.materials.remove(item) #Cubeを追加し流体シミュレーションのドメインに指定 bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0)) cube = bpy.data.objects["Cube"] bpy.context.scene.objects.active = cube bpy.ops.object.modifier_add(type='FLUID_SIMULATION') cube.modifiers["Fluidsim"].settings.type = 'DOMAIN' cube.modifiers["Fluidsim"].settings.end_time = 1 cube.modifiers["Fluidsim"].settings.viscosity_base = 5.0 cube.modifiers["Fluidsim"].settings.viscosity_exponent = 5 cube.modifiers["Fluidsim"].settings.filepath = "cache" #ミルクのマテリアルを設定 bpy.ops.wm.link_append(directory="./milk.blend/Material/", link=False, filename="Milk.001") obj = bpy.context.scene.objects.active obj.data.materials.append(bpy.data.materials['Milk.001']) cube.select = False #ドメインの底に水を張る bpy.ops.mesh.primitive_cube_add(location=(0, 0, -0.9)) bottom = bpy.data.objects["Cube.001"] bpy.context.scene.objects.active = bottom bottom.scale.z = 0.1 #bottomをFluidに設定 bpy.ops.object.modifier_add(type='FLUID_SIMULATION') bottom.modifiers["Fluidsim"].settings.type = 'FLUID' #透明マテリアルを作成してbottomに設定 transparent_mat = bpy.data.materials.new("transparent") obj = bpy.context.scene.objects.active obj.data.materials.append(bpy.data.materials['transparent']) transparent_mat.type = 'VOLUME' transparent_mat.volume.density_scale = 0 transparent_mat.use_raytrace = False bottom.select = False #水滴を作成 bpy.ops.mesh.primitive_uv_sphere_add(location=(0, 0, 0.5)) sphere = bpy.data.objects["Sphere"] bpy.context.scene.objects.active = sphere sphere.scale = ( 0.2, 0.2, 0.2 ) #sphereをFluidに設定 bpy.ops.object.modifier_add(type='FLUID_SIMULATION') sphere.modifiers["Fluidsim"].settings.type = 'FLUID' #水滴に透明マテリアルを設定 obj = bpy.context.scene.objects.active obj.data.materials.append(bpy.data.materials['transparent']) sphere.select = False #カメラ bpy.data.objects["Camera"].location = (2, 2, 1) bpy.data.objects["Camera"].rotation_euler = ( math.pi/3, 0, math.pi*3/4 ) bpy.data.cameras["Camera"].lens = 20 #照明 bpy.data.objects["Lamp"].location = (-2, 2, 10) bpy.data.objects["Lamp"].rotation_euler = ( math.pi/6, 0, math.pi*7/6 ) bpy.data.lamps["Lamp"].type = 'SUN' #ドメインをアクティブにして流体シミュレーション bpy.context.scene.frame_start = 0 bpy.context.scene.frame_end = 240 bpy.context.scene.objects.active = cube bpy.ops.fluid.bake("INVOKE_DEFAULT") #流体シミュレーションが終わるまで待機 while True: print('now simulationing...') if os.path.exists("cache/fluidsurface_final_0240.bobj.gz"): break else: time.sleep(3) # 動画作成 bpy.context.scene.render.resolution_x = 400 bpy.context.scene.render.resolution_y = 300 bpy.context.scene.render.resolution_percentage = 100 bpy.context.scene.render.image_settings.file_format = 'AVI_JPEG' bpy.data.scenes["Scene"].render.filepath = "test.avi" bpy.ops.render.render(animation=True) # 保存 savePath = os.path.abspath(os.path.dirname(__file__)) bpy.path.relpath(savePath) bpy.ops.wm.save_as_mainfile(filepath="test.blend", relative_remap=True)