本日は Blender の技術調査枠です。
Blender のpythonスクリプトでユニティちゃんのレンダリング負荷を軽減する手順を記事にします。
以下の記事を Blender の pythonスクリプトで実行してみたものです。
bluebirdofoz.hatenablog.com
実行手順
ユニティちゃんのFBXファイルをBlnderに取り込みます。
FBXの所在やFBXファイルをインポートする際の注意点は以下の記事を参照してください。
bluebirdofoz.hatenablog.com
後述の UnityChan_Converter.py をテキストエディタ―に読み込みます。
[スクリプト実行]を行い、処理が完了するのを待ちます。
処理が完了すると、オブジェクト/マテリアル/テクスチャ が一つにまとめられたユニティちゃんモデルが作成されます。
テクスチャは読み込んだFBXファイルのテクスチャフォルダに出力されています。
作成されたモデルを再びFBXファイルとしてエクスポートすれば Unity で利用可能です。
Batchs/SetPass calls の負荷が 1 になっていることが分かります。
読み込み時の注意点は以下の記事を参照してください。
上記の例では透明化が必要なテクスチャを含むため、シェーダは Unlit/Transparent Cutout を利用しています。
bluebirdofoz.hatenablog.com
実行スクリプト
これまで作成した「Blenderで利用可能なpythonスクリプト」のノウハウを組合せて作成しました。
少し長いですが、スクリプト全文を貼付しておきます。
アーマチュアの設定/オブジェクト結合/テクスチャ作成/ベイク/マテリアル設定など一通りの機能を実行しているので学習サンプルとして良いです。
参照オブジェクト名などがユニティちゃんに依存しているため、他のモデルでは正常に動作しません。
# sysインポート(引数取得のため) import sys # osインポート(ファイルパス取得のため) import os # mathインポート(角度計算のため) import math # bpyインポート(Blender操作のため) import bpy # bpyインポート(頂点操作のため) import bmesh # メイン関数 def main(): # バックグラウンド実行時の処理を行う # バックグラウンド実行時はFBXファイルの読み込みを行う if not check_background_import(): # FBX読み込み失敗など実行不可の場合は処理を終了する return # ユニティちゃんをBlender内で再構築する setup_unitychan() # ユニティちゃんの最適化を行う optimisation_unitychan() # バックグラウンド実行時の処理を行う # バックグラウンド実行時はFBX形式でエクスポートする if not check_background_export(): # FBXエクスポート失敗など実行不可の場合は処理を終了する return return # ユニティちゃんをBlender内で再構築する # 戻り値 def setup_unitychan(): # アーマチュア外の顔オブジェクトの位置/回転リセットを行う for facemesh in get_objectlist_facemesh(): clear_object_transform(arg_objectname=facemesh.name) # 体に合わせるため、顔オブジェクトを以下の通り操作する # 拡大縮小を0.01倍/X軸方向に90度回転 for facemesh in get_objectlist_facemesh(): add_object_transform(arg_objectname=facemesh.name, arg_rotation=(90,0,0),arg_scale=(0.01,0.01,0.01)) # 顔オブジェクトのトランスフォームを適用する for facemesh in get_objectlist_facemesh(): apply_object_transform(arg_objectname=facemesh.name) # 顔オブジェクトをアーマチュアへ関連付ける setparent_armature_facemesh() # 顔オブジェクトにウェイト付けを行う # 全ての顔オブジェクトは Character1_Head に追従する for facemesh in get_objectlist_facemesh(): vertexgroup_name='Character1_Head' weighting_object_vertexgroup( arg_objectname=facemesh.name, arg_vertexgroup=vertexgroup_name ) # Unityへの再取り込み時のための調整を行う # 元アニメーションと干渉するHumanoidリグ以外のボーンをリネームする # [J_***]という名称のボーンが対象となる rename_bones_Uniquerig() # シェーディングを「マテリアル」に設定する shading_change_material() # 環境照明を設定する lighting_add_environment() # カラーテクスチャの色情報のみ有効化する for mat in bpy.data.materials: active_color_toptexture(mat.name) # 透明オブジェクトの透明化設定を行う for transmesh in get_objectlist_transmesh(): transparent_object_mesh(arg_objectname=transmesh.name) return # ユニティちゃんに以下の最適化を行う # - オブジェクトの結合 # - マテリアルの統合 # 戻り値 def optimisation_unitychan(): # ベイク干渉防止のため、cheekオブジェクトの頂点位置を微調整する # アーマチュアオブジェクトの拡大縮小 0.01 X軸回転 90 の状態であることを考慮する # 以下の設定で blender の座標系でY軸方向に-0.01頂点位置が補正される adjustment_object_cheek(arg_objectname='cheek', arg_position=(0,0,1)) # 結合オブジェクト名を設定する joinname='JoinedObj' # 複製オブジェクト名を設定する duplicateame='OptimumModel' # 全メッシュオブジェクトを統合する join_mesh_objects(arg_joinedname=joinname) # 結合オブジェクトを複製する duplicate_object_rename(arg_objectname=joinname, arg_dupname=duplicateame) # 複製オブジェクトのマテリアルを全削除する remove_materialslot_object(arg_objectname=duplicateame) # 複製オブジェクトのUVマップを全削除する delete_UVmap_mesh(arg_objectname=duplicateame) # 複製オブジェクトのUVマップをスマートUV投影で作成する uv_smart_project(arg_objectname=duplicateame) # 画像のディレクトリパスを取得する imagedirpath=get_dirpath_topimage(arg_objectname=joinname) # 画像のファイル名を設定する imagename='BakeTexture' # ベイク元オブジェクトからベイク先オブジェクトへベイクを実行する bake_texture_select2active( arg_sourcename=joinname,arg_destinationname=duplicateame, arg_savefilepath=imagedirpath,arg_savefilename=imagename) # 指定オブジェクトにテクスチャを参照するマテリアルを適用する new_material_textureset( arg_objectname=duplicateame, arg_applyfilepath=imagedirpath, arg_applyfilename=imagename) # 指定オブジェクトの透明化設定を行う transparent_object_mesh(arg_objectname=duplicateame) # 指定オブジェクトを削除する unlink_object(arg_objectname=joinname) return # バックグラウンド実行時のインポート処理を定義する # 戻り値 実行可否(True/False) def check_background_import(): # 実行可能か判定を行う check_result=False # バックグラウンド実行時は bpy.app.background が True if bpy.app.background: # バックグラウンド実行時処理 # プロジェクトへのFBX読み込みから実施 print('blender is CUI') # 引数チェック if len(sys.argv) > 4: # 5つ目の引数にFBXファイルへのパス fbx_filepath=sys.argv[4] # プロジェクト内のオブジェクトを全て削除する delete_all_objects() # FBXファイル(ユニティちゃん)を読み込む import_file_unitychanfbx(fbx_filepath) # 処理が完了すれば実行可 check_result=True else: # FBXファイルへのパスが足りない print('blender.exe --background --python [python_filepath] [fbx_filepath]') # 引数が足りない場合は実行不可 check_result=False else: # GUI実行時 # FBX読み込みは完了済みの想定で動作する print('blender is GUI') # 実行可 check_result=True return check_result # バックグラウンド実行時のエクスポート処理を定義する # 戻り値 実行可否(True/False) def check_background_export(): # 実行可能か判定を行う check_result=False return check_result # プロジェクト内のオブジェクトを全て削除する # 戻り値 def delete_all_objects(): # 全シーンオブジェクトを削除する for item in bpy.context.scene.objects: bpy.context.scene.objects.unlink(item) # 全データオブジェクトを削除する for item in bpy.data.objects: bpy.data.objects.remove(item) # 全メッシュデータを削除する for item in bpy.data.meshes: bpy.data.meshes.remove(item) # 全マテリアルデータを削除する for item in bpy.data.materials: bpy.data.materials.remove(item) return # FBXモデルをインポートする(デフォルト設定) # 引数 arg_filepath:ファイルパス # 戻り値 def import_file_fbx(arg_filepath='C:\\UnityChan\\Models\\unitychan.fbx'): # FBXインポート # bpy.ops.import_scene.fbx() bpy.ops.import_scene.fbx( filepath=arg_filepath, use_manual_orientation=False, global_scale=1.0, bake_space_transform=False, use_custom_normals=True, use_image_search=True, use_alpha_decals=False, decal_offset=0.0, use_anim=True, anim_offset=1.0, use_custom_props=True, use_custom_props_enum_as_string=True, ignore_leaf_bones=False, force_connect_children=False, automatic_bone_orientation=False, primary_bone_axis='Y', secondary_bone_axis='X', use_prepost_rot=True ) return # FBXモデルをインポートする # ユニティちゃんモデルを読み込むため、以下のアーマチュア設定をデフォルトから変更する # - リーフボーンを無視:有効 # - 子を強制的に接続:有効 # 引数 arg_filepath:ファイルパス # 戻り値 def import_file_unitychanfbx(arg_filepath='C:\\UnityChan\\Models\\unitychan.fbx'): # FBXインポート # bpy.ops.import_scene.fbx() bpy.ops.import_scene.fbx( filepath=arg_filepath, use_manual_orientation=False, global_scale=1.0, bake_space_transform=False, use_custom_normals=True, use_image_search=True, use_alpha_decals=False, decal_offset=0.0, use_anim=True, anim_offset=1.0, use_custom_props=True, use_custom_props_enum_as_string=True, ignore_leaf_bones=True, force_connect_children=True, automatic_bone_orientation=False, primary_bone_axis='Y', secondary_bone_axis='X', use_prepost_rot=True ) return # アーマチュア外の顔オブジェクトをリストで取得する # 引数 # 戻り値 顔オブジェクトリスト def get_objectlist_facemesh(): # 空リストを作成 facemesh_list=[] # 選択する顔オブジェクトは以下の通り # BLW_DEF,EL_DEF,EYE_DEF,MTH_DEF, # eye_L_old,eye_R_old,eye_base_old,head_back facenames=('BLW_DEF','EL_DEF','EYE_DEF','MTH_DEF', 'eye_L_old','eye_R_old','eye_base_old','head_back') # 各キーの存在確認を行い、存在すればリストに追加する for facename in facenames: if check_objecttype_mesh(facename): facemesh=bpy.context.scene.objects[facename] facemesh_list.append(facemesh) print(facemesh_list) return facemesh_list # 透明化対象のオブジェクトをリストで取得する # 引数 # 戻り値 顔オブジェクトリスト def get_objectlist_transmesh(): # 空リストを作成 transmesh_list=[] # 選択するオブジェクトは以下の通り # BLW_DEF,EL_DEF,eye_base_old,cheek transnames=('BLW_DEF','EL_DEF','eye_base_old','cheek') # 各キーの存在確認を行い、存在すればリストに追加する for transname in transnames: if check_objecttype_mesh(transname): transmesh=bpy.context.scene.objects[transname] transmesh_list.append(transmesh) print(transmesh_list) return transmesh_list # 指定したオブジェクトがメッシュオブジェクトかチェックする # 引数 arg_objectname:指定オブジェクト名 # 戻り値 存在有無(True/False) def check_objecttype_mesh(arg_objectname='Default'): # 存在有無の判定を行う check_result=False # シーン内のオブジェクトのキーリストを取得 keys=bpy.context.scene.objects.keys() # 各キーの存在確認を行う if arg_objectname in keys: # オブジェクトを取得する check_ob=bpy.context.scene.objects[arg_objectname] # オブジェクトの種別をチェックする if check_ob.type=='MESH': # メッシュオブジェクトであればTrueを返す check_result=True print(arg_objectname + ' is MESH') return check_result # ユニティちゃんのアーマチュアを取得する # 引数 # 戻り値 アーマチュアオブジェクト def get_object_armature(): # 空オブジェクトを作成 armature=None # 取得するアーマチュアオブジェクト名はCharacter1_Reference check_ob=bpy.context.scene.objects['Character1_Reference'] # オブジェクトの種別をチェックする if check_ob.type=='ARMATURE': # アーマチュアオブジェクトであればオブジェクトを返す armature=check_ob return armature # 指定オブジェクトのトランスフォームをクリアする # 引数 arg_objectname:指定オブジェクト名 # arg_location:位置クリア # arg_rotation:回転クリア # arg_scale:拡大縮小クリア # 戻り値 def clear_object_transform(arg_objectname='Default', arg_location=True,arg_rotation=True,arg_scale=True): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態にする ob.select=False # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトを選択状態にする selectob.select=True # 位置をクリアする if arg_location: bpy.ops.object.location_clear(clear_delta=False) # 回転をクリアする if arg_rotation: bpy.ops.object.rotation_clear(clear_delta=False) # 拡大縮小をクリアする if arg_scale: bpy.ops.object.scale_clear(clear_delta=False) # 処理を完了したら非選択状態にする selectob.select=False return # 指定オブジェクトのトランスフォームを変更する # 引数 arg_objectname:指定オブジェクト名 # arg_location:指定位置値(加算) # arg_rotation:指定回転値(加算) # arg_scale:指定拡大縮小値(乗算) # 戻り値 def add_object_transform(arg_objectname='Default', arg_location=(0,0,0),arg_rotation=(0,0,0),arg_scale=(1,1,1)): # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 位置を変更する selectob.location.x += arg_location[0] selectob.location.y += arg_location[1] selectob.location.z += arg_location[2] # 回転を変更する selectob.rotation_euler.x += 2*math.pi/360*arg_rotation[0] selectob.rotation_euler.y += 2*math.pi/360*arg_rotation[1] selectob.rotation_euler.z += 2*math.pi/360*arg_rotation[2] # 拡大縮小を変更する selectob.scale.x *= arg_scale[0] selectob.scale.y *= arg_scale[1] selectob.scale.z *= arg_scale[2] return # 指定オブジェクトのトランスフォームを適用する # 引数 arg_objectname:指定オブジェクト名 # arg_location:位置適用 # arg_rotation:回転適用 # arg_scale:拡大縮小適用 # 戻り値 def apply_object_transform(arg_objectname='Default', arg_location=True,arg_rotation=True,arg_scale=True): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態にする ob.select=False # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトを選択状態にする selectob.select=True # トランスフォームを適用する bpy.ops.object.transform_apply( location=arg_location, rotation=arg_rotation, scale=arg_scale ) # 処理を完了したら非選択状態にする selectob.select=False return # 顔オブジェクトとアーマチュアの関連付けを行う # 引数 # 戻り値 def setparent_armature_facemesh(): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態にする ob.select=False # 顔オブジェクトを選択状態にする for facemesh in get_objectlist_facemesh(): facemesh.select=True # アーマチュアオブジェクトを選択状態かつアクティブする armatureob=get_object_armature() armatureob.select=True bpy.context.scene.objects.active=armatureob # アーマチュアの関連付けを実行する bpy.ops.object.parent_set(type='ARMATURE') return # 指定オブジェクトにウェイト付けを行う # 1.指定オブジェクトの頂点グループをクリアする # 2.指定オブジェクトに頂点グループを追加する # 3.指定オブジェクトの全頂点にウェイト 1 を割り当てる # 引数 arg_objectname:指定オブジェクト名 # arg_vertexgroup:追加頂点グループ名 # 戻り値 def weighting_object_vertexgroup( arg_objectname='Default', arg_vertexgroup='DefaultGroup'): # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトをアクティブにする bpy.context.scene.objects.active=selectob # オブジェクトの全頂点グループをクリアする selectob.vertex_groups.clear() # オブジェクトに新規頂点グループを追加する selectob.vertex_groups.new(name=arg_vertexgroup) # 追加した頂点グループをアクティブにする selectob.vertex_groups.active_index=selectob.vertex_groups[arg_vertexgroup].index # 編集モードに移行する bpy.ops.object.mode_set(mode='EDIT', toggle=False) # 全頂点を選択状態にする bpy.ops.mesh.select_all(action='SELECT') # 頂点グループの設定ウェイト値を 1 にする bpy.context.scene.tool_settings.vertex_group_weight=1.0 # 頂点グループのウェイト値を選択頂点に割り当てる bpy.ops.object.vertex_group_assign() # オブジェクトモードに戻る bpy.ops.object.mode_set(mode='OBJECT', toggle=False) return # Humanoidリグ以外のボーンをリネームする # 引数 # 戻り値 def rename_bones_Uniquerig(): # アーマチュアオブジェクトを取得する armatureob=get_object_armature() # アーマチュア内のボーンを走査する for bone in armatureob.data.bones: # ボーンの名前を取得する bonename=bone.name # 名前が[J_]で始まっていれば変更対象とする if bonename.find('J_') == 0: # ボーンの名前を変更する bone.name='RN' + bonename print(bone.name) return # 全ての3Dビューのシェーディングを「マテリアル」に設定する # 引数 # 戻り値 def shading_change_material(): # 全てのスクリーンを走査する for area in bpy.context.screen.areas: # エリアタイプが3Dビューであるか if area.type == 'VIEW_3D': # エリア内の各スペースを走査する for space in area.spaces: # スペースが3Dビューの表示領域であるか if space.type == 'VIEW_3D': # シェーディングを「マテリアル」に設定する space.viewport_shade='MATERIAL' return # 環境照明を設定する # 引数 # 戻り値 def lighting_add_environment(): # blender内のワールドを取得する env_world=bpy.context.scene.world # ワールドの環境照明を有効化する env_world.light_settings.use_environment_light=True return # カラーテクスチャの色情報のみ有効化する # 引数 arg_materialname:指定マテリアル名 # 戻り値 def active_color_toptexture(arg_materialname='Default'): # 対象マテリアルを取得する selectmat=bpy.data.materials[arg_materialname] # テクスチャスロットが一つ以上あることを確認する if len(selectmat.texture_slots) > 0: # テクスチャスロットの一つ目のテクスチャの色情報を有効化する selectmat.texture_slots[0].use_map_color_diffuse=True return # 対象のオブジェクトを透明化する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def transparent_object_mesh(arg_objectname='Default'): # 対象オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # オブジェクトの透過表示を有効化する selectob.show_transparent=True # マテリアルスロットが一つ以上あることを確認する if len(selectob.material_slots) > 0: # マテリアルスロットの一つ目のマテリアルを取得する targetmat=selectob.material_slots[0].material # マテリアルの透過を有効化する targetmat.use_transparency=True # 透過モードを「Z値透過」に設定する targetmat.transparency_method='Z_TRANSPARENCY' # アルファ値を 0.0 に設定する targetmat.alpha=0.0 # テクスチャスロットが一つ以上あることを確認する if len(targetmat.texture_slots) > 0: # テクスチャスロットの一つ目のテクスチャの色情報を有効化する targetmat.texture_slots[0].use_map_alpha=True return # 指定オブジェクトの全頂点を指定量だけ移動する # 引数 arg_objectname:指定オブジェクト名 # arg_position:移動量 # 戻り値 def adjustment_object_cheek(arg_objectname='Default', arg_position=(0,0,0)): # 指定オブジェクトを取得する selectob = bpy.context.scene.objects[arg_objectname] # 変更オブジェクトをアクティブに変更する bpy.context.scene.objects.active = selectob # 編集モードに移行する bpy.ops.object.mode_set(mode='EDIT', toggle=False) # 編集モードであることを確認する if selectob.mode == 'EDIT': # メッシュデータを取得する meshdata=bmesh.from_edit_mesh(selectob.data) # 頂点を走査する for v in meshdata.verts: # 移動量を加算する v.co.x += arg_position[0] v.co.y += arg_position[1] v.co.z += arg_position[2] # オブジェクトモードに戻す bpy.ops.object.mode_set(mode='OBJECT', toggle=False) return # 全メッシュオブジェクトを統合する # 引数 arg_joinedname:結合オブジェクト名 # 戻り値 def join_mesh_objects(arg_joinedname=''): # シーン中の全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # オブジェクトがメッシュであるか確認する if ob.type == 'MESH': # メッシュであれば選択状態にする ob.select=True # 対象アクティブオブジェクトに切り替える # メッシュはアクティブオブジェクトに結合される bpy.context.scene.objects.active = ob else: # メッシュでなければ選択状態にしない ob.select=False # オブジェクトの結合を実行する bpy.ops.object.join() # 変更オブジェクト名が指定されている場合は名前変更 if len(arg_joinedname) > 0: # アクティブオブジェクトの名前を変更する bpy.context.scene.objects.active.name=arg_joinedname return # オブジェクトを複製する # 引数 arg_objectname:指定オブジェクト名 # arg_dupame:変更オブジェクト名 # 戻り値 def duplicate_object_rename(arg_objectname='Default', arg_dupname=''): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態に設定する ob.select=False # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトをアクティブに変更する #bpy.context.scene.objects.active = selectob # 指定オブジェクトを選択状態にする selectob.select=True # オブジェクトを複製する bpy.ops.object.duplicate_move( OBJECT_OT_duplicate=None, TRANSFORM_OT_translate=None) # 指定オブジェクトの選択状態を解除する selectob.select=False # 複製オブジェクト名が指定されている場合は名前変更 if len(arg_dupname) > 0: # 複製オブジェクトの名前を取得する duplicated_objectname=arg_objectname + ".001" # 複製オブジェクトを取得する duplicatedob=bpy.data.objects[duplicated_objectname] # 複製オブジェクトの名前を変更する duplicatedob.name=arg_dupname return # 指定オブジェクトのマテリアルスロットを全て削除する # ※ スロットの削除のため、マテリアルのデータは削除しない # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def remove_materialslot_object(arg_objectname='Default'): # 対象オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # マテリアルスロットが全て削除されるまでループ for loop_index in range(len(selectob.material_slots)): # 先頭のマテリアルスロットを選択状態にする selectob.active_material_index=0 # マテリアルスロットを削除する bpy.ops.object.material_slot_remove() return # 指定オブジェクトのUVマップを全削除する # ※ メッシュデータのUVマップを削除するため、 # 同じメッシュデータを参照するオブジェクトは影響を受ける # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def delete_UVmap_mesh(arg_objectname='Default'): # 対象オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # オブジェクトがメッシュであるか確認する if selectob.type == 'MESH': # データ名を取得する dataname=selectob.data.name # オブジェクトが保持するメッシュを取得する targetmesh=bpy.data.meshes[dataname] # UVマップが全て削除されるまでループ for loop_index in range(len(targetmesh.uv_textures)): # 先頭のUVマップを選択状態にする targetmesh.uv_textures.active_index=0 # UVマップを削除する bpy.ops.mesh.uv_texture_remove() return # 指定オブジェクトのUVマップをスマートUV投影で作成する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def uv_smart_project(arg_objectname='Default'): # 指定オブジェクトを取得する selectob = bpy.context.scene.objects[arg_objectname] # 変更オブジェクトをアクティブに変更する bpy.context.scene.objects.active=selectob # 編集モードに移行する bpy.ops.object.mode_set(mode='EDIT', toggle=False) # 頂点を全選択した状態とする bpy.ops.mesh.select_all(action='SELECT') # デフォルト設定のスマートUV展開を実行する # 角度制限:66,島の余白:0,エリアウェイト:0,アスペクト比の補正:True,UV境界に合わせる:True bpy.ops.uv.smart_project(angle_limit=66, island_margin=0, user_area_weight=0, use_aspect=True, stretch_to_bounds=True) # オブジェクトモードに戻す bpy.ops.object.mode_set(mode='OBJECT', toggle=False) return # 指定オブジェクトが参照するテクスチャ画像のディレクトリを取得する # 引数 arg_objectname:指定オブジェクト名 # 戻り値 先頭テクスチャ画像のディレクトリパス def get_dirpath_topimage(arg_objectname='Default'): # テクスチャ画像のディレクトリパスを返す image_dirpath='' # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトにマテリアルが1つ以上あることを確認する if len(selectob.material_slots) > 0: # 先頭マテリアルを取得する material_top=selectob.material_slots[0].material # マテリアルにテクスチャスロットが一つ以上あることを確認する if len(material_top.texture_slots) > 0: # 先頭テクスチャのイメージを取得する image_top=material_top.texture_slots[0].texture.image # ファイルパスを取得する image_filepath=image_top.filepath # 読み込み元のディレクトリパスを取得 image_dirpath=os.path.dirname(image_filepath) return image_dirpath # ベイク用の新規テクスチャを作成する # 引数 arg_savefiledir:保存ファイルディレクトリ # arg_savefilename:保存ファイル名 # 戻り値 def new_texture_1024x1024png( arg_savefilepath='C:\\UnityChan\\Models\\UnityChanShader\\Texture', arg_savefilename='NewTexture'): # 新規画像を作成する img=bpy.data.images.new(name=arg_savefilename, width=1024, height=1024, alpha=True) # 保存ファイルパスを作成する savepath=arg_savefilepath + '\\' + arg_savefilename + '.png' # 保存ファイルパスを指定する img.filepath_raw = savepath # ファイルフォーマットをPNGに設定する img.file_format = 'PNG' # 画像を保存する img.save() return # ベイク元オブジェクトからベイク先オブジェクトへベイクを実行する # 引数 arg_sourcename:ベイク元オブジェクト名 # arg_destinationname:ベイク先オブジェクト名 # arg_savefiledir:ベイク画像保存ファイルディレクトリ # arg_savefilename:ベイク画像保存ファイル名 # 戻り値 def bake_texture_select2active( arg_sourcename='Default',arg_destinationname='Default', arg_savefilepath='C:\\UnityChan\\Models\\UnityChanShader\\Texture', arg_savefilename='NewTexture'): # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する for ob in bpy.context.scene.objects: # 非選択状態に設定する ob.select=False # ベイク先オブジェクトを取得する destinationob = bpy.context.scene.objects[arg_destinationname] # ベイク先オブジェクトをアクティブに変更する bpy.context.scene.objects.active=destinationob # 編集モードに移行する bpy.ops.object.mode_set(mode='EDIT', toggle=False) # 頂点を全選択した状態とする bpy.ops.mesh.select_all(action='SELECT') # 新規画像を作成する bakeimage=bpy.data.images.new(name=arg_savefilename, width=1024, height=1024, alpha=True) # 保存ファイルパスを作成する savepath=arg_savefilepath + '\\' + arg_savefilename + '.png' # 保存ファイルパスを指定する bakeimage.filepath_raw = savepath # ファイルフォーマットをPNGに設定する bakeimage.file_format = 'PNG' # 画像を一旦保存する(初期化のため) bakeimage.save() # 全てのスクリーンを走査する for area in bpy.context.screen.areas: # エリアタイプが3Dビューであるか if area.type == 'VIEW_3D': # エリアタイプをUV画像エディターに変更する area.type='IMAGE_EDITOR' # エディター内の各領域を走査する for space in area.spaces: # 領域が画像エディターの表示領域であるか if space.type == 'IMAGE_EDITOR': # 画像エディターの表示領域の作成した画像を設定する space.image=bakeimage # ベイク元オブジェクトを取得する sourceob = bpy.context.scene.objects[arg_sourcename] # ベイク元オブジェクトを選択状態にする sourceob.select=True # ベイク先オブジェクトを選択状態にする destinationob.select=True # ベイクモードを「テクスチャ」に設定する bpy.data.scenes['Scene'].render.bake_type="TEXTURE" # ベイク先を「選択→アクティブ」を有効に設定する bpy.context.scene.render.use_bake_selected_to_active=True # ベイク処理を実行する bpy.ops.object.bake_image() # 画像を再保存する bakeimage.save() # 全てのスクリーンを走査する for area in bpy.context.screen.areas: # エリアタイプがUV画像エディターであるか if area.type == 'IMAGE_EDITOR': # エリアタイプを3Dビューに戻す area.type='VIEW_3D' # ベイク先オブジェクトをオブジェクトモードに戻す bpy.ops.object.mode_set(mode='OBJECT', toggle=False) return # 指定オブジェクトにテクスチャを参照するマテリアルを適用する # 引数 arg_objectname:指定オブジェクト名 # arg_applyfiledir:反映画像保存ファイルディレクトリ # arg_applyfilename:反映画像保存ファイル名 # 戻り値 def new_material_textureset( arg_objectname='Default', arg_applyfilepath='C:\\UnityChan\\Models\\UnityChanShader\\Texture', arg_applyfilename='NewTexture'): # 指定オブジェクトを取得する selectob = bpy.context.scene.objects[arg_objectname] # 変更オブジェクトをアクティブに変更する bpy.context.scene.objects.active=selectob # 新規マテリアルを作成する new_material=bpy.data.materials.new(arg_applyfilename) # マテリアルスロットを追加する bpy.ops.object.material_slot_add() # 作成したマテリアルスロットに新規マテリアルを設定する bpy.context.object.active_material=new_material # 新規テクスチャを作成する new_texture=bpy.data.textures.new(arg_applyfilename,type='IMAGE') # マテリアルにテクスチャスロットを追加する new_texture_slot=new_material.texture_slots.add() # 作成したテクスチャスロットに新規テクスチャを設定する new_texture_slot.texture=new_texture # 反映画像のファイルパスを取得する allpy_filepath=arg_applyfilepath + '\\' + arg_applyfilename + '.png' # 反映画像を読み込み apply_image=bpy.data.images.load(filepath=allpy_filepath) # 作成した新規テクスチャに画像を設定する new_texture.image=apply_image return # 指定オブジェクトを削除する # ※ 参照切りのため、メッシュデータは削除しない # 引数 arg_objectname:指定オブジェクト名 # 戻り値 def unlink_object(arg_objectname='Default'): # 指定オブジェクトを取得する selectob=bpy.context.scene.objects[arg_objectname] # 指定オブジェクトの参照をシーンから削除する bpy.context.scene.objects.unlink(selectob) return # main関数の呼び出し main()