2019年11月5日火曜日

オブジェクト毎にランダムな色を割り当てる(Blender2.80)

アニメの背景作画をされている ぎおさんが
Blenderを活用した背景作画のメイキングを公開されていましたが
この中でオブジェクト毎に色を割り当ててマスクとして利用していました
作業に使ってみえるスクリプトがBlender2.8では動かないとのことで
ノード関連のスクリプトの練習がてら 同様のスクリプトを作成してみました

マテリアル毎にランダムな色を割り当てた上で
色を割り当てていないオブジェクトにも新しいランダム色のマテリアルを作成します
元々マテリアルの設定がされていた場合に元のノードは残した状態で単色のマテリアルを接続するようにしてあります。
import bpy, random

#色設定をするノードの名前
node_name = "random_colormix"
#マテリアル設定対象になるオブジェクトの種類
target_type = ['MESH', 'CURVE', 'SURFACE', 'META', 'FONT']
#カラーマスク設定に必要なレンダリング設定
bpy.context.scene.render.filter_size = 0 #アンチエイリアス的な処理 デフォルトは1.50
bpy.context.scene.view_settings.view_transform = 'Standard' #設定そのままの色に
bpy.context.scene.render.dither_intensity = 0 #自然諧調時に階段状になるのを防ぐノイズをオフに
#ランダムで設定する色の諧調数
color_step = 16
#色データの生成
def random_color():
    rgb = [random.randint(0,color_step)/color_step for i in range(3)]
    rgb.append(1.0)
    return (rgb)

#新規マテリアルの作成
def new_material():
    color = random_color()
    (r,g,b) = [int(f*color_step) for f in color[:3]]
    mat_name = '#%02x%02x%02x' % (r,g,b)
    bpy.data.materials.new(name = mat_name)
    mat = bpy.data.materials[mat_name]
    mat.use_nodes = True
    node_tree = mat.node_tree
    #material_output
    mat_node = node_tree.nodes['Principled BSDF']
    set_Pr_node( mat_node, color, node_name)
    return(mat)

#既存のマテリアルがある場合の設定
def node_rerute(mat):
    node_tree = mat.node_tree
    matout = node_tree.nodes['Material Output']
    mat_node = node_tree.nodes.new('ShaderNodeBsdfPrincipled')
    #cordinate node location
    mat_node.location = (matout.location.x -300, matout.location.y -150)
    set_Pr_node( mat_node, random_color(), node_name)
    node_tree.links.new( mat_node.outputs[0], matout.inputs[0] )

#プリンシプルBSDFを設定
def set_Pr_node( mat_node, color, name = ""):
    if name :mat_node.name = node_name
    mat_node.inputs['Base Color'].default_value = (0,0,0,1)
    mat_node.inputs['Specular'].default_value = 0
    mat_node.inputs['Sheen Tint'].default_value = 0
    mat_node.inputs['Emission'].default_value = color

#シーン内のマテリアルの設定の変更
for mat in bpy.data.materials:
    if mat.use_nodes:
        mat_node = mat.node_tree.nodes.get(node_name)
        if mat_node:
            set_Pr_node( mat_node, random_color())
        else:
            node_rerute(mat)
    else:
        mat.use_nodes = True
        mat_node = mat.node_tree.nodes['Principled BSDF']
        set_Pr_node( mat_node, random_color(), node_name)

#シーン内全てのオブジェクトにマテリアル割り当て
for obj in bpy.context.scene.objects:
    if not obj.type in target_type: continue
    if len(obj.material_slots) == 0:
        mat = new_material()
        obj.data.materials.append(mat)
    elif obj.material_slots[0].name == "":
        obj.material_slots[0].link = 'DATA'
        mat = new_material()
        obj.data.materials[0] = mat

カラーマスクを出力する時に邪魔になるカラーマネージメント機能もオフになるようにスクリプト上で設定しています。
マスク色を作るノードにプリンシプルBSDFを使っているため
このノードのアルファの入力に画像テクスチャを適応することで 画像で透過させることもできます
(透過させる場合アルファのブレンドモード等の設定も必要です)
Eeveeでのオブジェクト毎の色も もう少しすれば機能が追加されるでしょうが
思いつく範囲で使い勝手がいいように調整してみました。

何かのお役に立つことがあれば幸いです。

1 件のコメント:

  1. 素晴らしいスクリプトをありがとうございます。EEVEEはObjectIDもMaterialIDもないためマスク出すのにいちいちCycles使っていましたが、これを使えばビューポートレンダリングで済むので大変助かります。

    返信削除