Blenderのコマンドラインレンダリング

スポンサーリンク

Blenderは、実はコマンドラインでも動かすことができます。レンダリング命令もできるので、簡易ネットワークレンダリングやバッチレンダリングにぴったりです。

スポンサーリンク

コマンドの例

コマンドはこのような感じになります。Windows, Linuxどちらも共通です。

blender --background -noaudio test.blend --threads 0 --render-output //anim --render-anim

※BlenderへのPATHが通っている前提の記載です。(PATHが通っていない場合は、上記の「blender」とある部分を、C:\Program Files~~などから始まる絶対パスで記述すればOKです)

オプションの解説

よく使うもののみを紹介します。すべてのオプションは、公式マニュアルにあるのでそちらを御覧ください。基本的に、指定しなかったオプションについては、保存されているblendファイルや、Preferenceの設定がそのまま使われます。

Command Line Arguments - Blender 4.2 Manual

-b もしくは --background
バックグラウンド実行(GUIを表示しない)。コマンドラインレンダリングの際は通常これにする。

-noaudio
オーディオシステムを使わない(GUIを起動していないLinuxなどはこれを追記する)。

-t もしくは --threads <スレッド数>
レンダリングに使うCPUスレッド数。0を指定すると、すべての論理スレッドを使う。(8コア16スレッドCPUを使っているとしたら、16になる。)これは無くてもいいが、blendファイルで少なく指定している場合にオーバーライドするために使う。

※マルチGPU構成等の場合、使用GPUを指定したかったりするかもしれませんが、これについては、コマンドライン引数では指定できません。そのかわり、Pythonスクリプトを使うことで実現可能です(後述)。

-o もしくは --render-output <アウトプットのパス+ファイル名>
出力先。指定しない場合は、blendファイルの情報が使用される。
// がblendファイルの場所基準の相対パス。
・アニメーションの場合、末尾に0000.pngや0000.exrがつく。
したがって、たとえば //render/out とすると、(blendファイルの場所)/render/out0000.exr(blendファイルの場所)/render/out0001.exr 、といった形でファイルが保存されていく。
・#記号を使うことで、その部分を連番扱いにすることも可能。
//render/animation_##_testとすると、animation_01_test.png のように名前がつけられる。#の数がそのままゼロ埋めの桁数になる。

-a もしくは --render-anim
アニメーションレンダリング。blendファイルで定義されたフレームのレンダリングを順番にすべて行う。

-f もしくは --render-frame <フレーム番号>
特定のフレームのみレンダリング。とびとびの値や、連続フレームなどが柔軟に指定できる。
・複数のフレームを指定するときは、コンマで区切る。
例 : --render-frame 1,4,10,20
・あるフレームからあるフレームの間を指定するときは、「..」を使う。
例 : --render-frame 5..10
・上2つを組み合わせた表記も可能。
例 : --render-frame 1,5,10..20,30..40,55,60
・スペースなどを含めないこと(引数の切れ目として認識されてしまう)。

オプション指定の注意点

以上のオプションを書く順番は、ある程度決まっています(これ重要)。ルールは、

blender [引数を取らないオプション] [blendファイル] [引数をとるオプション] [フレーム指定オプション]

となります。

[引数を取らないオプション]
-b(--background)や、-noaudioなど。blenderの起動方法に関わる部分。

[blendファイル]
blendファイルの場所を指定する。

[引数をとるオプション]
上の例では-t(--threads)や-o(--render-output)など。

[フレーム指定オプション]
-a(--render-anim)もしくは-f(--render-frame)を入れる。このオプションは必ず最後にしないといけない。途中に書くと、命令がそこまでだと認識され、後に書いた部分はすべて無視される。

自動レンダリングWindowsバッチファイル

Windowsのバッチレンダリングに使える.batファイルを作成してみました。変数を使って、柔軟に変更できるようになっています。
set文は、変数の指定を意味しています。同じ変数の指定文が複数ありますが、一番下にあるものが優先されるので、行の順番を変えることで設定をいじれるようになっています。(このような書き方って、セオリー的にどうなんでしょうかね。あんまり綺麗では無い気がしますが、私はこうやってます。)
また、logファイルに、レンダリングの情報が逐一出力されるようになっています。log=nulにすると、ログを作りません。
%blender%から始まる行を複製して並べれば、複数ファイルを順番にレンダリングするようなものを作れます。

set blender="C:\Software\Blender.81\blender.exe"
set log=nul
set log="Z:\Web\CGBeginner\log.txt"

set blendfile="Z:\Web\CGBeginner\render.blend"
set threads=0

set outpath= --render-output //scene1/render
set outpath=

set frame=--render-frame 359..360
set frame=--render-anim

%blender% --background -noaudio %blendfile% --threads %threads% %outpath% %frame% >> %log% 2>&1

rem @echo off
echo End of Batch
pause

自動レンダリングシェルスクリプト(Bash)

Linuxで使うシェルスクリプトも作成しました。中身は.batと同等です。

blender="/home/user/bin/blender2.81/blender"
log="/dev/null"
log="/home/user/log"

blendfile="/home/user/render.blend"
threads=0

outpath="--render-output //scene1/render"
outpath=""

frame="--render-frame 1,3,5"
frame="--render-frame 1..10"
frame="--render-anim"

${blender} --background -noaudio ${blendfile} --threads ${threads} ${outpath} ${frame} >> ${log} 2>&1 &

Pythonスクリプトを用いた高度な指定(GPU指定レンダリング)

現在、私のPCはGPUを2台搭載した以下のような構成になっています。

  • CPU : AMD Ryzen 9 5950X
  • GPU 1: NVIDIA GeForce RTX 3080
  • GPU 2: NVIDIA GeForce RTX 3090

基本的に、最後に保存したUser Preferenceの設定がそのまま使われるのですが、バッチ制御でGPUを指定してレンダリングを行いたい!というときがあります。わたしの場合、おなじblendファイルのレンダリングを、GPUごとに別プロセスに分けてレンダリングさせたりしてます。(そっちのほうがシステムが安定している気がする)

上述のコマンドライン引数では、計算タイプ(CUDA, Optix, HIP, etc)の指定や、GPUの指定はできません。そのような場合は、Pythonスクリプトを指定してレンダリングを行うことで実現可能です。

たとえば、上で書いたbatスクリプトを、

set blender=C:\Software\Blender.1.1\blender.exe
set blendfile="D:\Data\render.blend"

start %blender% --background -noaudio --python render_gpu0.py -- %blendfile%
start %blender% --background -noaudio --python render_gpu1.py -- %blendfile%

のように書き換えます。Pythonファイルを指定しているわけですが、Pythonファイルは以下のようになります。

render_gpu0.py

import bpy
import sys

# レンダリングする.blendファイルをコマンドライン引数から取得
blend_file_path = sys.argv[-1]

# Optixを使用するように設定
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'OPTIX'

# 利用可能なすべてのデバイスを表示
devices = bpy.context.preferences.addons['cycles'].preferences.devices
if devices is not None:
    for i, device in enumerate(devices):
        print("Device {}: {}".format(i, device.name))
else:
    print("No devices found.")

# 利用するデバイスを全てOFFに初期設定
devices = bpy.context.preferences.addons['cycles'].preferences.devices
for device in devices:
    device.use = False

# GPUを指定
devices[3].use = True

# レンダリング設定
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'

# ファイルの読み込みとレンダリング
bpy.ops.wm.open_mainfile(filepath=blend_file_path)
bpy.ops.render.render(animation=True, write_still=False)

一旦すべてのデバイスをOFFにした上で、devices[3].use = Trueと書いてある部分で、GPUの指定をしています。この数字はどこから得たのか?ですが、

# 利用可能なすべてのデバイスを表示
devices = bpy.context.preferences.addons['cycles'].preferences.devices
if devices is not None:
    for i, device in enumerate(devices):
        print("Device {}: {}".format(i, device.name))
else:
    print("No devices found.")

をBlender上のコンソール等で実行することでわかります。私の場合、以下のような出力になります。

Device 0: NVIDIA GeForce RTX 3090
Device 1: NVIDIA GeForce RTX 3080
Device 2: AMD Ryzen 9 5950X 16-Core Processor
Device 3: NVIDIA GeForce RTX 3090
Device 4: NVIDIA GeForce RTX 3080

このうち、Device 0, 1はCUDA、3,4がOptixです。2のCPUについては、CUDAとOptix両方のようです?このことを確認したければ、以下のコード

# デバイスが有効かどうか
devices = bpy.context.preferences.addons['cycles'].preferences.devices
if devices is not None:
    for i, device in enumerate(devices):
        print("Device {}: {}".format(i, device.use))
else:
    print("No devices found.")

をコンソールで動かせば、上記がTrueかFalseか表示されますので、BlenderのGUIでPreferenceをいじりながら、何のチェックを入れたら何がTrueになるのかを見ていけば紐付けられます。ということで私の場合は、RTX3090でOptixレンダリングしたい場合は、devices[3].use = Trueとなるわけです。

render_gpu1.pyには、devices[4].use = Trueとしておけば、それぞれのGPUで別プロセスでレンダリングが自動でできるというわけです。CPUも加えたければ一緒に書いておけばいいと思います。

以下は、RTX3090とCPUでレンダリングするためのPythonコードです。

render_gpu0_cpu.py

import bpy
import sys

# レンダリングする.blendファイルをコマンドライン引数から取得
blend_file_path = sys.argv[-1]

# Optixを使用するように設定
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'OPTIX'

# 利用可能なすべてのデバイスを表示
devices = bpy.context.preferences.addons['cycles'].preferences.devices
if devices is not None:
    for i, device in enumerate(devices):
        print("Device {}: {}".format(i, device.name))
else:
    print("No devices found.")

# 利用するデバイスを全てOFFに初期設定
devices = bpy.context.preferences.addons['cycles'].preferences.devices
for device in devices:
    device.use = False

# GPUとCPUを指定
devices[2].use = True
devices[3].use = True

# レンダリング設定
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'

# ファイルの読み込みとレンダリング
bpy.ops.wm.open_mainfile(filepath=blend_file_path)
bpy.ops.render.render(animation=True, write_still=False)

逆順からレンダリング

Blenderは通常、アニメーションレンダリングは、指定範囲のフレームを、前から順に行っていきます。でも、マシン複数台でレンダリングする際など、レンダリングの順番を制御したいときもあるでしょう。私も、メインマシンとサブマシンでレンダリングする際、両方とも前から順でもいいのですが、たまにマシンの環境違い等によって、微妙に結果が違うレンダリングになってしまうことが経験上ありました。メインマシンとサブマシンで両方とも前から順にレンダリングしていた場合、そういう時に変なフリッカーになってしまい、分離するにも容易じゃない状況になってしまいます。こういう時、メインマシンは前から順、サブマシンは逆順でレンダリングができれば便利では!?そう思ったので方法をメモします。

上で述べた、Pythonスクリプトをベースにして、render_gpu0_backwards.pyと名付けましょう。

import bpy
import sys

# レンダリングする.blendファイルをコマンドライン引数から取得
blend_file_path = sys.argv[-1]

# Optixを使用するように設定
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'OPTIX'

# 利用可能なすべてのデバイスを表示
devices = bpy.context.preferences.addons['cycles'].preferences.devices
if devices is not None:
    for i, device in enumerate(devices):
        print("Device {}: {}".format(i, device.name))
else:
    print("No devices found.")

# 利用するデバイスを全てOFFに初期設定
devices = bpy.context.preferences.addons['cycles'].preferences.devices
for device in devices:
    device.use = False

# GPUを指定
devices[0].use = True

# レンダリング設定
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'GPU'

# ファイルの読み込みとレンダリング
bpy.ops.wm.open_mainfile(filepath=blend_file_path)
#bpy.ops.render.render(animation=True, write_still=False)

# 逆順にレンダリング
scene = bpy.context.scene

# オリジナルのフレーム範囲を保存
original_start_frame = scene.frame_start
original_end_frame = scene.frame_end

# 逆順にループ
for frame in range(original_end_frame, original_start_frame - 1, -1):
    print(f"Rendering frame {frame}")
    
    # フレーム範囲を1フレームのレンジに設定
    scene.frame_start = frame
    scene.frame_end = frame
    
    # アニメーションのレンダリング
    bpy.ops.render.render(animation=True, write_still=False)
    
    print(f"Finished rendering frame {frame}")

# フレーム範囲を元に戻す
scene.frame_start = original_start_frame
scene.frame_end = original_end_frame

print("Rendering completed.")

上記スクリプトの「逆順にレンダリング」の部分以下がポイントです。やっていることは、もともと設定したレンダリングフレーム範囲を強制的に変更しながら、ループを回すという仕組みです。

scene.frame_startがアニメーション開始フレーム、scene.frame_endがアニメーション終了フレームを意味しており、その情報をoriginal_start_frameとoriginal_end_frameに保持したうえで、scene.frame_startとscene.frame_endを変化させながらループさせます。

もしシーンのフレーム範囲が1から250に設定されている場合、
original_end_frame は 250 です。これがループの開始点になります。
original_start_frame – 1 は 0 です (original_start_frame が 1 のため)。
-1 はステップサイズで、1ずつ減少させながらループします。
最初のループでは、frame は 250 になります。
次に、frame は 249 になります。
このように、frame は 1 まで1ずつ減少します。
frame が 1 になった後、次に frame が 0 になるため、ループが終了します。

コメント

タイトルとURLをコピーしました