【Blender】ハイポリを参照するカスタムプロパティを使ってテクスチャをベイクする

投稿者: | 2023-06-11

スクリプトでテクスチャをベイクするときに、ベイク先とベイク元のオブジェクトのセットが必要なので、ベイク先にベイク元のオブジェクトを参照するカスタムプロパティを設定してみました。

参照を保存するスクリプト

import bpy

# アクティブなオブジェクトを取得します
active_obj = bpy.context.view_layer.objects.active

# 選択されているがアクティブではないオブジェクトを取得します
selected_but_not_active = [obj for obj in bpy.context.selected_objects if obj != active_obj]

# もしアクティブなオブジェクトが存在しない、または他のオブジェクトが選択されていない場合は、エラーメッセージを表示します
if active_obj is None or len(selected_but_not_active) == 0:
    print("Error: No active object or no other objects selected.")
else:
    # アクティブなオブジェクトのカスタムプロパティに、選択されているがアクティブではないオブジェクトを格納します
    active_obj["selected_objects"] = selected_but_not_active

# カスタムプロパティに格納された各オブジェクトの名前を表示します
for obj in active_obj["selected_objects"]:
    if obj:
        print(obj.name)

まず、bpyモジュールをインポートして、アクティブ選択中のオブジェクトを取得します。

selected_but_not_active = [obj for obj in bpy.context.selected_objects if obj != active_obj]

リスト内包表記でそれ以外の選択オブジェクトをすべて取得します。

selected_but_not_active = [obj for obj in bpy.context.selected_objects if obj != active_obj]

for文によって選択中のオブジェクトをループし、if文の条件に合うものをリストに追加しています。

アクティブなオブジェクトが無いか、リストの要素数が0のときは以降の処理を行いません。

if active_obj is None or len(selected_but_not_active) == 0:
    print("Error: No active object or no other objects selected.")

そうでない場合、アクティブオブジェクトに「selected_objects」というカスタムプロパティを定義しリストを代入します。

else:
    # アクティブなオブジェクトのカスタムプロパティに、選択されているがアクティブではないオブジェクトを格納します
    active_obj["selected_objects"] = selected_but_not_active

最後にカスタムプロパティに含まれるすべてのオブジェクトの名前を出力します。

    for obj in active_obj["selected_objects"]:
        if obj:
            print(obj.name)

スクリプトを実行

シーンにハイポリとローポリを2つずつ作りました。

ローポリは2つともCubeで、ハイポリはSuzanneです。

ハイポリはSuzanneを分割したもので、赤と青に色分けされています。

ローポリのオブジェクトプロパティにはカスタムプロパティが設定されていません。

テキストエディタにスクリプトをコピペして、アウトライナーで2つのハイポリと1つ目のローポリを選択します。ローポリが黄色になるようにします。

再生アイコンをクリックすると、アクティブにしたローポリに「selected_objects」というカスタムプロパティが追加されました。アイテム数が2です。

オブジェクトプロパティ
システムコンソール

もう一つのローポリには、片方のハイポリだけを設定してみます。

この場合、アイテム数が1になります。カスタムプロパティを削除したいときは、「×」アイコンをクリックを押します。

オブジェクトプロパティ

ベイクするスクリプト

スクリプトで設定を変更して自動でベイクするのスクリプトを修正して、上のカスタムプロパティを使ってベイクしてみます。

import bpy
from enum import Enum
 
class TextureMapType(Enum):
    Albedo = 'Base Color'
    Metallic = 'Metallic'
    Roughness = 'Roughness'
    Normal = 'Normal'
 
 
# 1. オブジェクトが選択されていることを確認
selected_objects = bpy.context.selected_objects
if len(selected_objects) < 1:
    raise ValueError("Error: Select at least two objects.")
 
# 2. RenderプロパティのRender EngineをCyclesに変更
render_engine = bpy.context.scene.render.engine
if render_engine != 'CYCLES':
    bpy.context.scene.render.engine = 'CYCLES'
 
 
# 3. RenderプロパティのDeviceを「GPU Compute」に設定する
bpy.context.preferences.addons['cycles'].preferences.get_devices()
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'CUDA'
bpy.context.scene.cycles.device = 'GPU'
bpy.context.scene.cycles.use_cuda = True
 
 
# 4. BakeパネルのBake Typeを列挙体の値によって変更する
bake_type = TextureMapType.Albedo  # 任意の列挙体の値を設定
if bake_type == TextureMapType.Albedo or bake_type == TextureMapType.Metallic or bake_type == TextureMapType.Roughness:
    bpy.context.scene.cycles.bake_type = 'EMIT'
elif bake_type == TextureMapType.Normal:
    bpy.context.scene.cycles.bake_type = 'NORMAL'
 
 
# 5. Selected to Activeのチェックを入れる
bpy.context.scene.render.bake.use_selected_to_active = True


obj_count = 0
for obj in selected_objects:  
    
    # 7. Cage Objectにオブジェクトを設定
    cage_object_name = obj.name + "_Cage"
    cage_object = bpy.data.objects.get(cage_object_name)
    if cage_object:
        bpy.context.scene.render.bake.use_cage = True
        bpy.context.scene.render.bake.cage_object = cage_object
    else:
        bpy.context.scene.render.bake.use_cage = False
        bpy.context.scene.render.bake.cage_extrusion = 0.2
         
    bpy.context.scene.render.bake.use_clear = obj_count == 0
    
    bpy.ops.object.select_all(action='DESELECT')
    
    for selected_obj in obj["selected_objects"]:
        selected_obj.hide_set(False)
        selected_obj.select_set(True)

    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    # 9. Bakeボタンを押してテクスチャのベイクを始める
    bpy.ops.object.bake(type=bpy.context.scene.cycles.bake_type)
    obj_count += 1 
 
# 10. ベイクが終わったら、その旨をprintで出力
print("テクスチャのベイクが完了しました。")

まず必要なモジュールをインポートして、列挙体を定義しています。

import bpy
from enum import Enum
 
class TextureMapType(Enum):
    Albedo = 'Base Color'
    Metallic = 'Metallic'
    Roughness = 'Roughness'
    Normal = 'Normal'

選択中のオブジェクトの数を調べて、1より小さければエラーを出力します。

selected_objects = bpy.context.selected_objects
if len(selected_objects) < 1:
    raise ValueError("Error: Select at least two objects.")

レンダーエンジンを「Cycles」に設定し、Deviceを「GPU Compute」にします。

# 2. RenderプロパティのRender EngineをCyclesに変更
render_engine = bpy.context.scene.render.engine
if render_engine != 'CYCLES':
    bpy.context.scene.render.engine = 'CYCLES'
 
 
# 3. RenderプロパティのDeviceを「GPU Compute」に設定する
bpy.context.preferences.addons['cycles'].preferences.get_devices()
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = 'CUDA'
bpy.context.scene.cycles.device = 'GPU'
bpy.context.scene.cycles.use_cuda = True

列挙体の値によって、Bake Typeを変更します。今回はRGBノードをマテリアルアウトプットに直接接続して「Emit」タイプでベイクします。

bake_type = TextureMapType.Albedo  # 任意の列挙体の値を設定
if bake_type == TextureMapType.Albedo or bake_type == TextureMapType.Metallic or bake_type == TextureMapType.Roughness:
    bpy.context.scene.cycles.bake_type = 'EMIT'
elif bake_type == TextureMapType.Normal:
    bpy.context.scene.cycles.bake_type = 'NORMAL'

ハイポリのマテリアルのシェーダーノードの接続を変更しておきます。

Python スクリプトでシェーダーノードの接続を変更するでは、接続の変更を自動で行っています。

Selected to Activeのチェックをいれて、選択オブジェクトからアクティブオブジェクトへベイクするようにします。

bpy.context.scene.render.bake.use_selected_to_active = True

そして、選択したオブジェクトを一つ一つ処理します。ベイクするすべてのローポリを選択しているとします。

obj_count = 0
for obj in selected_objects:  

まずCageオブジェクトを検索して、無ければExtrusionを設定します。

    # 7. Cage Objectにオブジェクトを設定
    cage_object_name = obj.name + "_Cage"
    cage_object = bpy.data.objects.get(cage_object_name)
    if cage_object:
        bpy.context.scene.render.bake.use_cage = True
        bpy.context.scene.render.bake.cage_object = cage_object
    else:
        bpy.context.scene.render.bake.use_cage = False
        bpy.context.scene.render.bake.cage_extrusion = 0.2

このスクリプトでは、名前で検索していますが、ハイポリと同様にCageオブジェクトの参照をカスタムプロパティに設定することもできます。

すべてのローポリに同じテクスチャを使うので、はじめのオブジェクトだけClear Imageのチェックを入れます。

    bpy.context.scene.render.bake.use_clear = obj_count == 0

ベイクするには、ハイポリとローポリが選択されていて、ローポリがアクティブになっている必要があります。なので、まずすべての選択を解除して、ローポリのカスタムプロパティに含まれるオブジェクトをすべて選択します。

    bpy.ops.object.select_all(action='DESELECT')
    
    for selected_obj in obj["selected_objects"]:
        selected_obj.hide_set(False)
        selected_obj.select_set(True)

目のアイコンがオフになっているとエラーになるので、hide_setメソッドでハイポリを表示しています。

そして、ローポリをアクティブ選択したら、bpy.ops.object.bakeメソッドでベイクを開始し、オブジェクトのカウントを一つ上げます。

    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    # 9. Bakeボタンを押してテクスチャのベイクを始める
    bpy.ops.object.bake(type=bpy.context.scene.cycles.bake_type)
    obj_count += 1 
 
# 10. ベイクが終わったら、その旨をprintで出力
print("テクスチャのベイクが完了しました。")

ベイクスクリプトを実行

2つのローポリに同じマテリアルをつけ、Image Textureノードと画像を追加します。ベイク先のImage Textureノードを選択しておきます。

スクリプトで新規マテリアルにノードを設定してオブジェクトに割り当てるで新規マテリアルとテクスチャを作成して、ローポリに自動で割当てられます。Image Textureノードの選択をスクリプトで行うこともできます。

ローポリを複数選択して、ベイクのスクリプトを実行します。

プログレスバーは表示されませんが、ベイクが完了します。

ベイク前
ベイク後

ベイク元も正しく変更されています。

Low1

これでカスタムプロパティを使って複数のオブジェクトのベイクができました。

コメントを残す

メールアドレスが公開されることはありません。