🐵
Blender Pythonでテクスチャを頂点カラーにベイクする
概要
Blender内のPythonを利用して、メッシュの各頂点のUV座標からテクスチャの色を取得し、頂点カラーにベイクする方法を紹介します
環境
- Blender 3.6
Pythonスクリプト
import bpy
import numpy as np
# UV座標からテクスチャの色を返す関数
def tex2D(TexBufferList, UvPos):
# 参考 https://blender.stackexchange.com/questions/139384/get-rgb-value-of-texture-from-face-on-mesh
# UV座標値を0.0~1.0の範囲にクランプ
UvPos = np.clip(UvPos, 0.0, 1.0)
# UV座標がテクスチャ上のどこのピクセルに該当するか計算
UvPixcelPos_X = round(UvPos[0] * (TexSize_X - 1)) # [px] ※0始まり
UvPixcelPos_Y = round(UvPos[1] * (TexSize_Y - 1)) # [px] ※0始まり
# ピクセル座標からテクスチャの色が格納されたバッファリストのインデックスを計算
ListIndex = 4 * ((TexSize_X * UvPixcelPos_Y) + UvPixcelPos_X)
# 色を取得
r = TexBufferList[ListIndex + 0] # Red
g = TexBufferList[ListIndex + 1] # Green
b = TexBufferList[ListIndex + 2] # Blue
a = TexBufferList[ListIndex + 3] # Alpha
return [r, g, b, a]
# 選択されているメッシュを取得 (オブジェクト名から取得する場合はbpy.data.objects["オブジェクト名"]
Mesh = bpy.context.view_layer.objects.active
UvLayer = Mesh.data.uv_layers[0].data # 0番目のUVレイヤを取得
Material = Mesh.material_slots[0].material # 0番目のマテリアルを取得
# マテリアルに割り当てられているテクスチャを取得
Texture = Material.node_tree.nodes["Image Texture"].image
# テクスチャサイズを取得
TexSize_X = Texture.size[0] # X方向 [px]
TexSize_Y = Texture.size[1] # Y方向 [px]
# テクスチャの色が格納されたバッファ配列(1次元)をリストとして取得
# テクスチャの左下を原点にして、X方向優先に1ピクセル目のR,G,B,A、2ピクセル目のR,G,B,A...と
# 連続して色がfloatで格納されている。X方向が終端まで行ったら次の+Y行目に進んでいく。
# リストにしないと色の取得が凄く遅い (参考 https://blender.stackexchange.com/questions/124264/how-to-read-a-pixel-from-an-image-texture-with-python
TexBufferList = list(Texture.pixels)
# 頂点カラーを取得。なかったら追加する
if not Mesh.data.vertex_colors:
# Blender3.3以前の場合はbpy.ops.mesh.vertex_color_add()と書く
Mesh.data.vertex_colors.new()
VertexColors = Mesh.data.vertex_colors[0].data
# メッシュの各ポリゴンごとにループ
for poly in Mesh.data.polygons:
# 現在何ポリゴン目を処理しているか表示
print("Polygon progress: %d / %d" % (poly.index + 1, len(Mesh.data.polygons)))
# 現在のポリゴンの各頂点ごとにループ
for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
UvPos = UvLayer[loop_index].uv # 現在の頂点のUV座標を取得
TextureColor = tex2D(TexBufferList, UvPos) # UV座標からテクスチャの色を取得
# テクスチャから拾った色を頂点カラーに設定
VertexColors[loop_index].color = TextureColor
実行方法
-
例としてデフォルトキューブの各面を10分割し、適当な画像テクスチャを割り当てたモデルを用意します (下図のテクスチャはUV Checker Map Makerというサイトで生成しました)
-
「ウィンドウ」タブの「システムコンソール切り替え」をクリックし、スクリプトの実行結果やエラーが見えるようにする
-
「Sclipting」タブの「+新規」ボタンをクリックして以下のPythonスクリプトを貼り付ける
-
Cubeオブジェクトを選択した状態で「▶」ボタンをクリックし、スクリプトを実行する
実行結果
下図のようになれば成功です。
各頂点ごとにしか色を設定できないので、どうしても柄がぼやけてしまいます
色をはっきり分けたい場合は、色を分けたい境目に頂点を増やし、UV展開をし直す必要があります
エクスポート時の注意点
Unity(VRChat)向けにメッシュを出力する場合、エクスポート設定で頂点カラーに"リニア"を設定してください
応用例
VRChatのQuestアバターでよく使われるToonLitやStandard Liteシェーダーの頂点カラーを乗算する仕様を利用して、マテリアル数1、テクスチャメモリ0.00MBの超軽量Quest対応アバターを作れます
関連記事
Discussion