MRが楽しい

MRやVRについて学習したことを書き残す

Blender2.8で利用可能なpythonスクリプトを作る その41(テクスチャを指定したマテリアルの設定)

本日は Blender の技術調査枠です。
Blender2.8で利用可能なpythonスクリプトを作ります。

指定の画像テクスチャを使ったたプリンシプルBSDFのマテリアルを作成する

指定の画像テクスチャを使ったプリンシプルBSDFのマテリアルを作成し、指定のオブジェクトに設定します。
指定オブジェクトのマテリアルは一旦全て削除されます。
実行例では blend ファイルと同ディレクトリに存在する画像ファイルを指定しています。
・make_bsdfmaterial_texture.py

# bpyインポート
import bpy
# パス操作のため
import os

# 指定オブジェクトに指定の画像テクスチャを設定したプリンシプルBSDFのマテリアルを設定する
def set_bsdfmaterial_texture(arg_objectname="Default", arg_texturepath="") -> bool:
    """指定オブジェクトに指定の画像テクスチャを設定したプリンシプルBSDFのマテリアルを設定する

    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
        arg_texturepath {str} -- テクスチャパス (default: {""})

    Returns:
        bool -- 実行正否
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # 指定オブジェクトがメッシュオブジェクトか確認する
    if selectob.type != 'MESH':
        # メッシュオブジェクトでない場合は処理しない
        return False

    # 指定オブジェクトの全マテリアルを削除する
    delmat_result = delete_material_all(arg_objectname=arg_objectname)
    if delmat_result != True:
        # マテリアル削除に失敗した場合は処理しない
        return False

    # マテリアル名を設定する
    make_materialname = "TextureMaterial"

    # 作成マテリアルにプリンシプルBSDFノードを追加して画像ノードをカラーとして接続する
    new_result = new_bsdfmaterial_texture(arg_materialname=make_materialname, arg_texturepath=arg_texturepath)
    if new_result != True:
        # マテリアル作成に失敗した場合は処理しない
        return False

    # 作成マテリアルをセットする
    set_result = add_material_target(arg_objectname=arg_objectname, arg_materialname=make_materialname)
    if set_result != True:
        # セットに失敗した場合は処理しない
        return False

    return True

# 指定オブジェクトの全マテリアルを削除する
def delete_material_all(arg_objectname="Default") -> bool:
    """指定オブジェクトに新規マテリアルを作成する

    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})

    Returns:
        Bool -- 実行正否
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # 指定オブジェクトがメッシュオブジェクトか確認する
    if selectob.type != 'MESH':
        # メッシュオブジェクトでない場合は処理しない
        return False

    # オブジェクトをアクティブにする
    bpy.context.view_layer.objects.active = selectob

    # マテリアルスロットを走査する
    for material_slot in selectob.material_slots:

        # マテリアルスロットをアクティブにする
        selectob.active_material = material_slot.material

        # マテリアルスロットを削除する
        bpy.ops.object.material_slot_remove()
    
    return True

# 指定テクスチャを設定したプリンシプルBSDFマテリアルを作成する
def new_bsdfmaterial_texture(arg_materialname="TextureMaterial", arg_texturepath="") -> bool:
    """指定テクスチャを設定したプリンシプルBSDFマテリアルを作成する

    Args:
        arg_materialname (str, optional): 作成マテリアル名. Defaults to "TextureMaterial".
        arg_texturepath (str, optional): 参照テクスチャパス. Defaults to "".

    Returns:
        bool: 実行正否
    """

    # テクスチャパスを確認する
    if arg_texturepath == "":
        # テクスチャパスが未設定の場合は処理しない
        return False

    # テクスチャの存在確認を行う
    if os.path.isfile(arg_texturepath) != True:
        # 指定テクスチャが存在しない場合は処理しない
        return False

    # 指定名のマテリアルを取得する
    check_mat = bpy.data.materials.get(arg_materialname)

    # 指定名のマテリアルが存在するか確認する
    if check_mat != None:
        # 指定名のマテリアルが既に存在する場合は処理しない
        return False

    # 新規マテリアルを作成する
    newmaterial = bpy.data.materials.new(arg_materialname)

    # ノードを使用する
    newmaterial.use_nodes = True

    # 処理対象のマテリアルを取得する
    target_mat = newmaterial

    # ターゲットマテリアルのノード参照を取得
    mat_nodes = target_mat.node_tree.nodes

    # マテリアル内の全ノードを走査する
    for delete_node in mat_nodes:
        # 一旦デフォルトのノードを全て削除する
        mat_nodes.remove(delete_node)
    
    # テクスチャノードの追加
    texture_node = mat_nodes.new(type="ShaderNodeTexImage")

    # テクスチャノードをアクティブにする
    mat_nodes.active = texture_node

    # 指定画像を読み込む
    loadimage = bpy.data.images.load(filepath=arg_texturepath)

    # テクスチャノードに新規画像を設定する
    texture_node.image = loadimage

    # ノードリンクの取得
    mat_link = target_mat.node_tree.links

    # プリンシプルBSDFノードを追加する
    bsdf_node = mat_nodes.new(type="ShaderNodeBsdfPrincipled")

    # 出力ノードを追加する
    output_node = mat_nodes.new(type="ShaderNodeOutputMaterial")

    # テクスチャノードのカラーとプリンシプルBSDFノードのベースカラーを接続する
    mat_link.new(texture_node.outputs[0], bsdf_node.inputs[0])
    
    # 放射ノードの放射と出力ノードのサーフェスを接続する
    mat_link.new(bsdf_node.outputs[0], output_node.inputs[0])

    return True

# 指定オブジェクトに指定名のマテリアルを追加する
def add_material_target(arg_objectname="Default",
      arg_materialname="NewMaterial", arg_usenode=True) -> bool:
    """指定オブジェクトに新規マテリアルを作成する

    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
        arg_materialname {str} -- 追加マテリアル名 (default: {"NewMaterial"})
        arg_usenode {bool} -- ノード利用の有無 (default: {True})

    Returns:
        Bool -- 実行正否
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    selectob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if selectob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return False
    
    # 指定オブジェクトがメッシュオブジェクトか確認する
    if selectob.type != 'MESH':
        # メッシュオブジェクトでない場合は処理しない
        return False

    # オブジェクトをアクティブにする
    bpy.context.view_layer.objects.active = selectob

    # 指定名のマテリアルを取得する
    add_mat = bpy.data.materials.get(arg_materialname)

    # 指定名のマテリアルが存在するか確認する
    if add_mat == None:
        # 指定名のマテリアルが存在しない場合は新規マテリアルを作成する
        add_mat = bpy.data.materials.new(arg_materialname)

        # ノードの利用指定を確認する
        if arg_usenode == True:
            # ノードを使用する
            add_mat.use_nodes = True

    # 指定名のマテリアルスロットを取得する
    check_matslot = selectob.material_slots.get(arg_materialname)

    # 指定マテリアルが存在するか確認する
    if check_matslot != None:
        # 指定名のマテリアルスロットが既に存在する場合は処理しない
        return False

    # マテリアルスロットを追加する
    bpy.ops.object.material_slot_add()

    # 追加したマテリアルスロットに指定名のマテリアルを設定する
    selectob.active_material = add_mat

    return True

# 現在のblendファイルが存在するディレクトリパスを取得する
# 未保存のプロジェクト上で実行すると、空文字が返る
def get_blend_dirpath() -> str:
    """現在のblendが存在するディレクトリパスを取得する
    未保存のプロジェクト上で実行すると、空文字が返る

    Returns:
        str: blendディレクトリパス
    """

    # blendファイルのパスを取得
    filepath = bpy.data.filepath

    # 空文字が返った場合は処理せず終了する
    if filepath == "":
        return ""

    # ディレクトリパスを取得する
    dirpath = os.path.dirname(filepath)

    return dirpath


# 関数の実行例
# テクスチャパスを作成する
texturepath = get_blend_dirpath() + "\\HoloMon_400x400.png"
set_bsdfmaterial_texture(arg_objectname="Cube", arg_texturepath=texturepath)

f:id:bluebirdofoz:20200614200026j:plain