從Blender裡萃取模組資料(11)
前言
在之前的 從Blender裡萃取模組資料(9) 裡有示範如何萃取 Texture 的資料,但 Blender 2.8 以後的 Material 採取 Node 編輯的方式設定,所以該範例的方法已無法在 Blender 2.8 以後使用,這次就來研究如何萃取,在此把學習的過程做個紀錄。
內容
在開始之前請先將 Material 調成以下
新增 Texture 給 Base Color |
這次需要示範如何萃取 Image Texture 的資料,所以要新增一張圖並連結到"Base Color"。這次的範例會示範如何萃取連結到"Base Color"接口的 Image Texture,具體的範例如下
import bpy import bmesh tagMesh = bpy.data.meshes["Cube"] #Create bmesh from mesh tagBMesh = bmesh.new() tagBMesh.from_mesh(tagMesh) # def find_node_by_name( srcNode , nodeName): if srcNode.name == nodeName: return srcNode tagNode = None for socket in srcNode.inputs: for link in socket.links: tagNode = find_node_by_name( link.from_node , nodeName ) if tagNode != None: break if tagNode != None: break # return tagNode # def find_node_from_node_socket( srcNode , socketName , nodeName): tagSocket = srcNode.inputs.get( socketName ) if tagSocket == None: return None tagNode = None for link in tagSocket.links: tagNode = find_node_by_name( link.from_node , nodeName ) if tagNode != None: break # return tagNode #Start extract matList = tagMesh.materials for mat in matList: print( 'material name:' , mat.name ) outputNode = mat.node_tree.nodes.get( 'Material Output' ) if outputNode != None: principledBSDFNode = find_node_by_name( outputNode , 'Principled BSDF' ) if principledBSDFNode != None: imageNode = find_node_from_node_socket( principledBSDFNode , 'Base Color' , 'Image Texture' ) if imageNode != None: print( 'Image name:' , imageNode.image.name) print( 'Image width: ', imageNode.image.size[0] ) print( 'Image height: ', imageNode.image.size[1] ) print( 'Image format: ', imageNode.image.file_format ) print( 'Image path: ' , imageNode.image.filepath_from_user() ) print( 'Image Node interpolation:' , imageNode.interpolation ) print( 'Image Node projection:' , imageNode.projection ) print( 'Image Node extension:' , imageNode.extension ) print( 'Image source:' , imageNode.image.source ) print( 'Image color space:' , imageNode.image.colorspace_settings.name ) else: #Show default value socket = principledBSDFNode.inputs.get( 'Base Color' ) for val in socket.default_value: print( 'data:' , val ) #Free bmesh... tagBMesh.free()
範例裡有兩個工具 Function ,分別為"find_node_by_name"與"find_node_from_node_socket",第一個是基於某個 Node 來從所有的輸入接口尋找目標 Node ,第二則是基於某個 Node 的指定接口來尋找目標 Node 。萃取的過程比想像中簡單,透過 Material 裡的"node_tree"來取得輸出的 Node (Material Output),接著透過"find_node_by_name"來尋找"Principled BSDF" Node ,用工具 Function 來尋找是因為不知道中間會過多少個 Node ,接著就透過"find_node_from_node_socket"從"Base Color"接口來尋找"Image Texture",範例在找到"Image Texture"時就取得 Node 的相關資料,當沒取得時就取得"Base Color"的顏色資料(當該接口沒有被連結時)。
在取得"Base Color"的顏色資料時是透過"default_value",但官方的說明文件上沒有這個屬性,在 [ blender.stackexchange.com ] PyNodes API: transferring data between nodes with sockets 裡我才找到有這個屬性可以提取。這次範例的工具 Function 都只找出第一個目標 Node ,無法解決多個目標 Node 的狀況。由於 Material 採取 Node 編輯,但由於每個遊戲引擎的 Node 規格不太一樣,就算詳細萃取的每個 Node 的資料也很難可以匯出給其它的遊戲引擎,所以只提供這樣不完整的萃取方法,如果這樣不夠用以後在補新的。
參考資料
[ docs.blender.org ] Blender 2.92.0 Python API Documentation
[ blender.stackexchange.com ] PyNodes API: transferring data between nodes with sockets
沒有留言:
張貼留言