blender 命令行渲染

Posted 长虹剑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了blender 命令行渲染相关的知识,希望对你有一定的参考价值。

文章目录

基础环境配置

安装及资料

apt-get install blender

Blender 2.91 参考手册

运行python脚本

Blender python 初步文档

有关渲染的样例

虚拟桌面 Xvfb

在服务器上想要运行blender需要建立虚拟桌面,挺简单的就

apt-get install Xvfb

使用的时候,首先开启虚拟桌面到后台,然后就可以运行 blender 了

export DISPLAY=:6
Xvfb -ac :6 -screen 0 640x480x24 & # 1920x1080x24
blender -b --render-output tmp/res -f 0

blender 内置 python 修改 packages

找了半天至今不知道 blender 到底是调用的哪里的 python ,不过我猜测它可能把 python 给直接编译进去了,所以后期我们应该是换不了这个python的。所以就直接 sys 中替换加载变量的路径就行。

具体就是先运行

blender -b -P ck_bpy.py

ck_bpy.py 就写一个 print(sys.path)
获得路径,然后自己的 python 也打印一下,把路径拿出来,插入到指定位置就行。

内置blender 安装库

看到这里也可以考虑把 anaconda 的环境装到这里

cd D:/program/BlenderFoundation/Blender2.93/2.93/python/bin
 ./python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
cd D:/program/BlenderFoundation/Blender2.93/2.93/python/Scripts
./pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
./pip install Pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
./pip install scipy -i https://pypi.tuna.tsinghua.edu.cn/simple

编译 blender

svn 太慢,wget 下载的方法如下

wget -P download -r -p -np https://svn.blender.org/svnroot/bf-blender/trunk/lib/linux_centos7_x86_64 -o res.log

初步尝试杂记

渲染

用默认的blend,调用脚本渲染

blender -b -P tmp/test.py -o tmp/res -f 0
import bpy
for e in bpy.data.objects:
    print(e.name)
bpy.ops.render.render()
bpy.data.images['Render Result'].save_render(filepath='tmp/res/example.png')

直接命令行指定渲染
https://blenderartists.org/t/animating-rotation-using-keyframes-in-python/590243

blender -b --render-output tmp/res -f 0

计算

ob.matrix_world = Matrix.Translation(ob.location) * Matrix.Rotation(radians(45), 4, 'Z')

自己的一些代码

(未整理)

基本渲染设置

def f2_render_bones_video():
    FPS = 15 # 60
    smpl2skeleton = Cls_smpl2skeleton( fsmpl2rig, fparents, frot_mp )
    pms = smpl2skeleton.example( fpm )
    params.pms = pms
    params.parents = smpl2skeleton.parents
    params.NJ = smpl2skeleton.NJ

    ob = bpy.data.objects["smpl_skeleton"]
    bpy.app.handlers.frame_change_pre.clear()
    bpy.app.handlers.frame_change_pre.append(frame_change_pre)
    bpy.context.view_layer.objects.active = ob #相当于鼠标左键选中
    bpy.ops.object.mode_set(mode='POSE') #切换为pose更改模式
    arr = []
    bones = []
    for i in range(24):
        bone=ob.pose.bones[f'Bonei']
        bone.rotation_mode = 'AXIS_ANGLE' #'XYZ'
        bones.append( bone )
        if i==0:
            arr.append( bone.location.copy() )
        arr.append( bone.rotation_axis_angle )  # tuple arr
    #print( [ e[:] for e in arr ] )
    params.T_pose = arr
    params.bones = bones
    set_render_engine()
    if isdbg:
        #set_scene( 30  )
        set_scene( 3  )
    else:
        set_scene( len(params.pms)  )
    set_render_for_video( fv, fps=FPS )

    bpy.ops.wm.save_mainfile( filepath = fblend )

def frame_change_pre(scene):
    rcnt = scene.frame_current
    NJ = params.NJ
    bones = params.bones
    bones[0].location = params.T_pose[ 0 ].copy()
    for i in range(params.NJ):
        #if i==0:
        #    print(111, params.T_pose[ i+1 ][:], bones[i].rotation_axis_angle[:])
        bones[i].rotation_axis_angle = params.T_pose[ i+1 ]

    pm = params.pms[rcnt]
    for i in range(NJ-1, -1, -1):
        a = 3+i*4
        #if i==0:
        #    print( pm[a:a+4], bones[i].rotation_axis_angle[:] )
        bones[i].rotation_axis_angle = tuple(pm[a:a+4].tolist())
    bones[0].location = pm[:3]


def set_render_engine():
    for scene in bpy.data.scenes:
        scene.render.engine = 'BLENDER_EEVEE'

    scene = bpy.context.scene
    eevee = scene.eevee

    eevee.use_soft_shadows = True

    eevee.use_ssr = True
    eevee.use_ssr_refraction = True

    eevee.use_gtao = True
    eevee.gtao_distance = 1

    eevee.use_volumetric_shadows = True
    eevee.volumetric_tile_size = '2'

def set_scene( full_frame ):
    bpy.context.scene.frame_end = full_frame
    #bpy.context.scene.camera = bpy.data.objects['camera']

def set_render_for_video(fv, fps=30, w=640, h=360 ):
    r = bpy.context.scene.render
    r.filepath = fv
    #scene.render.image_settings.file_format = 'PNG' # set output format to .png
    r.image_settings.file_format = 'FFMPEG' # set output format to .png
    r.ffmpeg.format = 'MPEG4'
    r.fps = fps
    r.resolution_x = w
    r.resolution_y = h

    bpy.ops.render.render(animation=True)

    #r.image_settings.file_format = 'PNG'
    #r.color_mode = 'RGBA'
    #bpy.types.FFmpegSettings.codec = "PNG"
    #bpy.types.FFmpegSettings.pix_fmt = "RGBA"

def set_render_for_picture(fimg, w=640, h=360 ):
    r = bpy.context.scene.render
    r.filepath = fimg
    r.image_settings.file_format = 'PNG'
    r.resolution_x = w
    r.resolution_y = h
    bpy.ops.render.render(write_still=True)

骨骼驱动的一些心得 【!!! 重要】

骨骼的正确驱动包括几个因素:

  1. 骨骼设计
  2. 蒙皮权重
  3. 代码与骨骼的对应

比如基于SMPL样式的骨架有下面两种
1)完全符合 SMPL 系数的骨架

2)符合直觉的骨架

这两种骨架会导致使用smpl系数时写代码以及自动绑定的难易。

  1. 第一种骨骼:与SMPL系数几乎直接对应(可能需要坐标变换),但是如果使用blender的自动绑定权重会非常不合理,需要手动刷权重。
  2. 第二种骨骼:需要根据骨骼局部坐标轴,确定变换矩阵。(就需要写code时推导一下)

一些总结@9-2

  1. 在坐标系转换中,最终考虑的是点变换,但是我们一般直接看坐标系得到的变换其实是坐标系变换,与点变换互逆
  2. 由1 可知我们实际得到标准坐标系到骨骼局部坐标系的变换 M: A->B, 而点变换是M.T
  3. 执行坐标系变换夹杂旋转变换时时要考虑一个点,最终可以得到 M_.T x R x M_ x p , 前三个组成了最终的旋转变换,注意如果使用2,则M_=M.T
  4. blender 中显示的是骨骼的局部坐标系,如果想要变成统一的骨骼,那么骨骼都是竖直朝上的,这样写程序好些,但是自动绑定的时候蒙皮会有很大的问题。

之后发现一个事情
当blender中导入fbx 选择自动骨骼坐标系时,骨骼看起来会比较正常,但是传入的系数就必须改变
这里有几个后来分析得出的知识
1) bvh 默认就是 ogl 坐标系,和 smpl 的骨骼时一模一样的
2)blender 中 fbx 其实坐标系也是 ogl ,但是如果骨骼旋转,他会把这个旋转矩阵给记录下来,而这个matrix 就是坐标系变换
3)其他 maya motionbuilder 软件一打开就是正常的说明人间会自动根据子集和父级的关系给你连上线,但是保存的时候不会动你的 matrix
4)blender 中不像may,它不会自己改变骨骼帮你连,而且一旦改变了骨骼,这个matrix就存下来了,这样以后你给的系数就必须按照新的骨骼坐标系给。(所以需要有个坐标系变换 具体推导可以看一下这里的: 坐标系变换,对应旋转与平移形变
5)fbx 导入 blender 选择自动骨骼坐标系,操作完动画,然后转为 bvh, 然后用 bvh 的数据给到 原始的 fbx 依然可以得到正确的动画。 这说明那个matrix 的变换和动画的变换都合并了。

关键帧动画

def f4_test():
    set_render_engine()
    # useful shortcut
    scene = bpy.context.scene

    # this shows you all objects in scene
    scene.objects.keys()

    # when you start default Blender project, first object in scene is a Cube
    kostka = scene.objects[0]

    # you can change location of object simply by setting the values
    kostka.location = (1,2,0)

    # same with rotation
    kostka.rotation_euler = (45,0,0)

    # this will make object cease from current scene
    #scene.objects.remove(kostka)
    kostka.select_set(True)
    bpy.ops.object.delete()

    # clear everything for now
    scene.camera = None  
    for obj in scene.objects:  
        #scene.objects.remove(obj)
        obj.select_set(True)
        bpy.ops.object.delete()

    # create sphere and make it smooth
    bpy.ops.mesh.primitive_uv_sphere_add(location = (2,1,2), radius=0.5)  
    bpy.ops.object.shade_smooth()  
    kule = bpy.context.object

    # create new cube
    bpy.ops.mesh.primitive_cube_add(location = (-2,1,2))  
    kostka = bpy.context.object

    # create plane 
    bpy.ops.mesh.primitive_plane_add(location=(0,0,0))  
    plane = bpy.context.object  
    plane.dimensions = (20,20,0)

    # for every object add material - here represented just as color
    for col, ob in zip([(1, 0, 0, 0), (0,1,0,0), (0,0,1,0)], [kule, kostka, plane]):  
        mat = bpy.data.materials.new("mat_" + str(ob.name))
        mat.diffuse_color = col
        ob.data.materials.append(mat)

    # now add some light
    light_data = bpy.data.lights.new(name="light_2.80", type='POINT')
    light_data.energy = 30

    light_object = bpy.data.objects.new(name="light_2.80", object_data=light_data)

    # link light object
    bpy.context.collection.objects.link(light_object)
    
    # make it active 
    bpy.context.view_layer.objects.active = light_object
    light_object.location = (-3, 0, 12)
    
    # update scene, if needed
    #dg = bpy.context.evaluated_depsgraph_get() 
    #dg.update()

    # and now set the camera
    cam_data = bpy.data.cameras.new(name="cam")  
    cam_ob = bpy.data.objects.new(name="Kamerka", object_data=cam_data)  
    bpy.context.collection.objects.link(cam_ob)  
    cam_ob.location = (-3, 0, 5)  
    cam_ob.rotation_euler = (3.14/6,0,-0.3)  
    cam = bpy.data.cameras[cam_data.name]  
    cam.lens = 10
    scene.camera = cam_ob

    ### animation
    positions = (0,0,2),(0,1,2),(3,2,1),(3,4,1),(1,2,1)

    # start with frame 0
    number_of_frame = 0  
    for pozice in positions:

        # now we will describe frame with number $number_of_frame
        scene.frame_set(number_of_frame)

        # set new location for sphere $kule and new rotation for cube $kostka
        kule.location = pozice
        kule.keyframe_insert(data_path="location", index=-1)

        kostka.rotation_euler = pozice
        kostka.keyframe_insert(data_path="rotation_euler", index=-1)
        kostka.location = pozice
        kostka.keyframe_insert(data_path="location", index=-1)
        

        # move next 10 frames forward - Blender will figure out what to do between this time
        number_of_frame += 10

    set_scene( number_of_frame )
    set_render_for_video( fvabs, fps=params.FPS, w=params.width, h=params.height )

    bpy.ops.wm.save_mainfile( filepath = fblend )
    pass

网上一些可以参考的代码

包含渲染的代码
https://github.com/DeepMotionEditing/deep-motion-editing
https://github.com/jonepatr/genea_visualizer

blender2.9 一些代码

复制物体

import bpy
xy=[[0,2],[0,4],[0,5],[0,7]]
xy_count=len(xy)
for i in range(xy_count):
    factor=3
    x=xy[i][0]*factor
    y=-xy[i][1]*factor
    z=0
    bpy.ops.object.select_all(action="DESELECT")
    bpy.data.objects["cube"].select_set(True)
    bpy.ops.object.duplicate_move(OBJECT_OT_duplicate="mode":"TRANSLATION",TRANSFORM_OT_translate="value":(x,y,z))

以上是关于blender 命令行渲染的主要内容,如果未能解决你的问题,请参考以下文章

blender 命令行渲染

Blender 导出器 (v70) 到 json three.js 蒙皮动画

更换蒙皮网格上的材质

关于Unity中蒙皮网格和布料的使用

Spine转:Spine术语和概念

Blender:音序器不支持边框渲染