做流体模拟的时候,想要复现别人的成果,但是别人的代码都是每帧输出 ply 格式的文件,渲染部分需要自己完成
看了一下,似乎用 blender 是最简单的,于是记录一下过程中用到的代码
Blender 版本 4.0
假设所有 ply 文件都和 blend 文件位于同一目录
希望导入所有 ply 文件,都放在一个 collection 里面,并且只连接到这个 collection
import bpy
import os
in_dir = bpy.path.abspath("//")
filters = [] # files to ignore
files_number = 0
def only_link_to_one_collection(obj, collection):
for other_col in obj.users_collection:
other_col.objects.unlink(obj)
if obj.name not in collection.objects:
collection.objects.link(obj)
def import_ply(path, filters):
need_file_items = []
need_file_names = []
filterDict = {}
for item in filters:
filterDict[item] = True;
file_lst = os.listdir(path)
for item in file_lst:
fileName, fileExtension = os.path.splitext(item)
if fileExtension == ".ply" and (not item in filterDict):
need_file_items.append(item)
need_file_names.append(fileName)
fluid_mesh_collection = bpy.data.collections.new(name='FluidMesh')
bpy.context.scene.collection.children.link(fluid_mesh_collection)
files_number = len(need_file_items)
for i in range(files_number):
item = need_file_items[i]
itemName = need_file_names[i]
ufilename = path + "\\" + item
bpy.ops.wm.ply_import(filepath=ufilename)
cur_obj = bpy.data.objects[itemName]
if (cur_obj):
only_link_to_one_collection(cur_obj, fluid_mesh_collection)
cur_obj.hide_set(False)
cur_obj.hide_render = True
import_ply(in_dir, filters)
删除没有使用到的材质
import bpy
toRemove = [block for block in bpy.data.materials if block.users == 0]
for block in toRemove:
bpy.data.materials.remove(block)
添加 Glass BSDF 材质
import bpy
fluid_mat = bpy.data.materials.new("FluidMat")
fluid_mat.use_nodes = True
principled_node = fluid_mat.node_tree.nodes.get("Principled BSDF")
fluid_mat.node_tree.nodes.remove(principled_node)
glass_node = fluid_mat.node_tree.nodes.new("ShaderNodeBsdfGlass")
glass_node.location = (0, 0)
glass_node.inputs[0].default_value = (0.730, 0.927, 1.0, 1.0)
glass_node.inputs[1].default_value = 0.0
glass_node.inputs[2].default_value = 1.333
output_node = fluid_mat.node_tree.nodes.get("Material Output")
output_node.location = (200, 0)
fluid_mat.node_tree.links.new(glass_node.outputs[0], output_node.inputs[0])
for obj in bpy.data.collections['FluidMesh'].all_objects:
obj.active_material = fluid_mat
重定位 Mesh
import bpy
for obj in bpy.data.collections['FluidMesh'].all_objects:
obj.rotation_euler[0] = 1.5708 # 90d
obj.location = (-4, 4, 0)
清理旧的动画
import bpy
for obj in bpy.data.collections['FluidMesh'].all_objects:
obj.animation_data_clear()
Hide_Render 动画
import bpy
for obj in bpy.data.collections['FluidMesh'].all_objects:
mesh_name = obj.name
i = int(mesh_name)
obj.hide_viewport = True
obj.hide_render = True
obj.keyframe_insert("hide_viewport", frame=0)
obj.keyframe_insert("hide_render", frame=0)
obj.hide_viewport = False
obj.hide_render = False
obj.keyframe_insert("hide_viewport", frame=i+1)
obj.keyframe_insert("hide_render", frame=i+1)
obj.hide_viewport = True
obj.hide_render = True
obj.keyframe_insert("hide_viewport", frame=i+2)
obj.keyframe_insert("hide_render", frame=i+2)
地面、天光等创建暂时不写脚本里
import bpy
import math
camera = bpy.data.objects['Camera']
camera.location = (20, -20, 20)
camera.rotation_euler = (math.radians(60), 0, math.radians(45))
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'
bpy.context.scene.cycles.samples = 256
bpy.context.scene.render.resolution_x = 1080
bpy.context.scene.render.resolution_y = 720
bpy.context.scene.render.fps = 30
bpy.context.scene.render.filepath = bpy.path.abspath("//fluid_anim.mkv")
bpy.context.scene.render.image_settings.file_format = 'FFMPEG'
bpy.context.scene.frame_start = 1
bpy.context.scene.frame_end = len(bpy.data.collections['FluidMesh'].all_objects)+1
bpy.ops.render.render(animation=True)