Blender 4.0でPython APIを使用して文字を表示してレンダリングする方法

3DCG

はじめに

この記事では、Blender 4.0でPython APIを使用して文字を表示し、レンダリングする手順を説明します。このプロセスは、コマンドラインからBlenderを操作し、自動化タスクを実行する際に役立ちます。


こちらの記事もおすすめ

Wonder3Dを用いて画像から3Dモデル生成
はじめにこの記事は、画像から3Dモデルを生成するためのソフトウェアであるWonder3Dの使用方法について説明していきます。これを理解するために、いくつかのソフトウェア開発ツールやプログラミング知識が必要となりますが、できる限りわかりやすく...
Blender 4.0 のPython APIで物体を発光・移動スクリプトを作成してみた
はじめにこの記事では、Blender 4.0のPython APIを使用して、オブジェクトを発光させつつ動かすスクリプトを作成する方法について解説します。デモ動画 @maki.sunwood.ai.labs Blenderの Python...

必要なツール

  • Blender 4.0
  • 基本的なPythonの知識

クリプトの準備

まず、Blenderをバックグラウンドで実行し、テキストオブジェクト、カメラ、ライトを作成してレンダリングするスクリプトを準備します。以下のスクリプトをテキストエディタにコピーし、background_job.pyという名前で保存します。


# This script is an example of how you can run blender from the command line
# (in background mode with no interface) to automate tasks, in this example it
# creates a text object, camera and light, then renders and/or saves it.
# This example also shows how you can parse command line options to scripts.
#
# Example usage for this test.
#  blender --background --factory-startup --python $HOME/background_job.py -- \
#          --text="Hello World" \
#          --render="/tmp/hello" \
#          --save="/tmp/hello.blend"
#
# Notice:
# '--factory-startup' is used to avoid the user default settings from
#                     interfering with automated scene generation.
#
# '--' causes blender to ignore all following arguments so python can use them.
#
# See blender --help for details.

import bpy

def example_function(text, save_path, render_path):
    # Clear existing objects.
    bpy.ops.wm.read_factory_settings(use_empty=True)

    scene = bpy.context.scene

    # Set the render engine to Eevee
    bpy.context.scene.render.engine = 'BLENDER_EEVEE'
    bpy.context.scene.eevee.use_bloom = True

    # Text Object
    txt_data = bpy.data.curves.new(name="MyText", type='FONT')
    txt_ob = bpy.data.objects.new(name="MyText", object_data=txt_data)
    scene.collection.objects.link(txt_ob)
    txt_data.body = text
    txt_data.align_x = 'CENTER'

    # Text Material (Emission)
    mat = bpy.data.materials.new(name="EmissionMaterial")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    nodes.clear()  # clear all the nodes
    emission = nodes.new(type='ShaderNodeEmission')
    emission.inputs[0].default_value = (1, 1, 1, 1)  # white color
    emission.inputs[1].default_value = 5.0  # strength
    output = nodes.new(type='ShaderNodeOutputMaterial')
    links = mat.node_tree.links
    link = links.new(emission.outputs[0], output.inputs[0])
    txt_ob.data.materials.append(mat)

    # Camera
    cam_data = bpy.data.cameras.new("MyCam")
    cam_ob = bpy.data.objects.new(name="MyCam", object_data=cam_data)
    scene.collection.objects.link(cam_ob)
    scene.camera = cam_ob
    cam_ob.location = 0.0, 0.0, 10.0

    # Sun Light
    light_data = bpy.data.lights.new("MyLight", 'SUN')
    light_ob = bpy.data.objects.new(name="MyLight", object_data=light_data)
    scene.collection.objects.link(light_ob)
    light_ob.location = 2.0, 2.0, 5.0
    light_ob.data.energy = 5.0  # adjust the energy for desired brightness

    bpy.context.view_layer.update()

    if save_path:
        bpy.ops.wm.save_as_mainfile(filepath=save_path)

    if render_path:
        render = scene.render
        render.use_file_extension = True
        render.filepath = render_path
        bpy.ops.render.render(write_still=True)

def create_simple_scene(save_path, render_path):
    # Clear existing objects
    bpy.ops.wm.read_factory_settings(use_empty=True)

    scene = bpy.context.scene

    # Create a simple cube
    bpy.ops.mesh.primitive_cube_add(size=2)
    cube = bpy.context.object

    # Camera
    cam_data = bpy.data.cameras.new("Camera")
    cam_ob = bpy.data.objects.new("Camera", cam_data)
    scene.collection.objects.link(cam_ob)
    scene.camera = cam_ob
    cam_ob.location = (5, -5, 5)
    cam_ob.rotation_euler = (0.785398, 0, 0.785398)

    # Light
    light_data = bpy.data.lights.new(name="Light", type='POINT')
    light_ob = bpy.data.objects.new("Light", light_data)
    scene.collection.objects.link(light_ob)
    light_ob.location = (0, 0, 5)

    # Update the scene
    bpy.context.view_layer.update()

    # Save and Render
    if save_path:
        bpy.ops.wm.save_as_mainfile(filepath=save_path)
    if render_path:
        render = scene.render
        render.use_file_extension = True
        render.filepath = render_path
        bpy.ops.render.render(write_still=True)

def main():
    import sys       # to get command line args
    import argparse  # to parse options for us and print a nice help message

    # get the args passed to blender after "--", all of which are ignored by
    # blender so scripts may receive their own arguments
    argv = sys.argv

    if "--" not in argv:
        argv = []  # as if no args are passed
    else:
        argv = argv[argv.index("--") + 1:]  # get all args after "--"

    # When --help or no args are given, print this help
    usage_text = (
        "Run blender in background mode with this script:"
        "  blender --background --python " + __file__ + " -- [options]"
    )

    parser = argparse.ArgumentParser(description=usage_text)

    # Example utility, add some text and renders or saves it (with options)
    # Possible types are: string, int, long, choice, float and complex.
    parser.add_argument(
        "-t", "--text", dest="text", type=str, default="Hello World",
        help="This text will be used to render an image",
    )

    parser.add_argument(
        "-s", "--save", dest="save_path", metavar='FILE', default="/tmp/hello.blend",
        help="Save the generated file to the specified path",
    )
    parser.add_argument(
        "-r", "--render", dest="render_path", metavar='FILE', default="/tmp/hello2",
        help="Render an image to the specified path",
    )

    print(">>> args")
    args = parser.parse_args(argv)  # In this example we won't use the args

    print(args)

    if not args.text:
        print("Error: --text=\"some string\" argument not given, aborting.")
        parser.print_help()
        print("see you...")
        return

    # Run the example function
    example_function(args.text, args.save_path, args.render_path)
    #create_simple_scene(args.save_path, args.render_path)

    print("Batch job finished, exiting")
    print("batch job finished, exiting")

if __name__ == "__main__":
    main()

スクリプトの解説

スクリプトは大きく分けて3つの部分から構成されます。

example_function

この関数は、テキストオブジェクトを作成し、EeveeレンダーエンジンでBloom効果を有効にして、シーンをレンダリングします。

  • テキストオブジェクトの作成
  • エミッションマテリアルの追加
  • カメラとサンライトの設定
  • レンダリングと保存の設定

main

この関数は、コマンドライン引数を解析し、上記のexample_functionを実行します。

  • 引数解析
  • example_functionの呼び出し

スクリプトの実行部

最後に、スクリプトが直接実行された場合にmain関数を呼び出します。

コマンドラインからの実行

Blenderをインストールしたディレクトリに移動し、以下のコマンドを実行します。

blender --background --factory-startup --python /path/to/background_job.py -- --text="Your Text" --render="/path/to/render.png" --save="/path/to/save.blend"
  • --background はBlenderをバックグラウンドモードで起動します。
  • --factory-startup はユーザー設定を無視して、デフォルト設定でBlenderを開始します。
  • --python はPythonスクリプトを指定します。
  • --text, --render, --save はスクリプトに渡す引数です。

結果の確認

コマンドを実行した後、指定したレンダーパスに画像ファイルが、保存パスにBlenderファイルが生成されていることを確認します。

まとめ

この記事では、BlenderのPython APIを使用して、テキストを含むシンプルな3Dシーンを自動的に作成し、レンダリングする方法を紹介しました。この技術は、3Dグラフィックスの自動化やバッチ処理に非常に有効です。今後は、Twitter用に作成した動画をインスタグラムやTiktok用に変換するスクリプトを作成していきます。

リポジトリ

GitHub - Sunwood-ai-labs/VideoPortraitMaker
Contribute to Sunwood-ai-labs/VideoPortraitMaker development by creating an account on GitHub.

参考サイト

Blenderのpythonテンプレートを読み解く その2(background_job.py) - MRが楽しい
本日は Blender の小ネタ枠です。 Blenderのpythonテンプレートを読み解いて、どんな機能を持つテンプレートなのか確認します。 今回は「background_job」テンプレートです。 pythonテンプレートの利用方法 ビューを開き、メニューから テンプレート -> python を開きます。 様々な...
Pythonスクリプトでレンダリング|PianoMusic
 最近、Blenderでレンダリングを手動で行っていて枚数が多くなってくるとあまりにも面倒くさかった。そこで、Pythonスクリプトで自動化(プログラミングで処理)できないかな調べて実際にやってみたので忘れないうちに記録として... Pythonスクリプト  今回はパノラマ画像を環境マップとして読み込んでレンダリン...

コメント

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