はじめに
前回解説した記事では、Unityの色空間設定がLinerでなければ使えないとのことでした。
Blender→Unity間で32bitテクスチャ(EXR)で数値の受け渡しを行う
しかし、そうなるともっと汎用性が欲しくなります。
というわけでインポート時に色空間が設定できるPNGテクスチャで数値の受け渡しを行いたいと思います。
(基礎的なビット演算の内容となります)
Blender
Blenderのバージョンは4.1.1 Stableを使っていきます。
テクスチャ作成
前回までと内容が被っているため、クラス化まで飛ばします。
import bpy
import numpy as np
class TextureClass:
def __init__(self, texture_name, width, height):
self.image = bpy.data.images.get(texture_name)
if not self.image:
self.image = bpy.data.images.new(texture_name, width=width, height=height, alpha=True)
elif self.image.size[0] != width or self.image.size[1] != height:
self.image.scale(width, height)
self.point = np.array(self.image.pixels[:])
self.point.resize(height, width * 4)
self.point[:] = 0
self.point_R = self.point[::, 0::4]
self.point_G = self.point[::, 1::4]
self.point_B = self.point[::, 2::4]
self.point_A = self.point[::, 3::4]
def SetPixel(self, py, px, r, g, b, a):
self.point_R[py][px] = r
self.point_G[py][px] = g
self.point_B[py][px] = b
self.point_A[py][px] = a
def Export(self):
self.image.pixels = self.point.flatten()
image = TextureClass('CustomTexture', 1, 1)
image.SetPixel(0, 0, 0.5, 0, 0, 0.5)
image.Export()
変数を分割
まず、主な整数型のintや浮動小数型のfloatは32bitです。
そして8bitテクスチャにRGBAがあるので、各色に2進数で分割しながら情報を書きこめば、8bit×4=32bitとなって1ピクセルにデータを納めることが出来ますね!
...
といきたいところですが、1つ確認事項があります。
それはAが0の時です。
目視できる色はありませんが、そのまま値を渡せるのでしょうか?
実は大丈夫なようです。
安心しました。
それでは気にすることなく、各種関数を作成していきましょう。
import bpy
import struct
import numpy as np
class TextureClass:
def __init__(self, texture_name, width, height):
self.image = bpy.data.images.get(texture_name)
if not self.image:
self.image = bpy.data.images.new(texture_name, width=width, height=height, alpha=True)
elif self.image.size[0] != width or self.image.size[1] != height:
self.image.scale(width, height)
self.point = np.array(self.image.pixels[:])
self.point.resize(height, width * 4)
self.point[:] = 0
self.point_R = self.point[::, 0::4]
self.point_G = self.point[::, 1::4]
self.point_B = self.point[::, 2::4]
self.point_A = self.point[::, 3::4]
def SetPixel(self, py, px, r, g, b, a):
self.point_R[py][px] = r
self.point_G[py][px] = g
self.point_B[py][px] = b
self.point_A[py][px] = a
def Export(self):
self.image.pixels = self.point.flatten()
+ def SplitFloat(f):
+ return [a / 255 for a in struct.unpack('BBBB', struct.pack('f', f))]
+
+ def SplitInt(i):
+ return [a / 255 for a in struct.unpack('BBBB', struct.pack('I', i))]
+
+ def SplitInt16(i1, i2):
+ return [a / 255 for a in struct.unpack('BBBB', struct.pack('HH', i1, i2))]
image = TextureClass('CustomTexture', 2, 2)
image.SetPixel(0, 0, *SplitFloat(0.12345))
image.SetPixel(0, 1, *SplitInt(123456))
image.SetPixel(1, 0, *SplitInt16(1234, 1234))
image.Export()
内容としては、32bitを8bitに分割し、0~255を0~1の範囲にする関数です。
def SplitFloat(f):
return [a / 255 for a in struct.unpack('BBBB', struct.pack('f', f))]
def SplitInt(i):
return [a / 255 for a in struct.unpack('BBBB', struct.pack('I', i))]
def SplitInt16(i1, i2):
return [a / 255 for a in struct.unpack('BBBB', struct.pack('HH', i1, i2))]
Unity
インポートするテクスチャの設定は以下の通りです。
sRGB:False
Format:RGBA 32bit
Shader
RGBA(fixed4)を元の変数型に戻す関数を作成します。
数値表示のシェーダーは、前回と同じくブタジエン氏の物を使用します。
fixed4 frag(v2f i) : SV_Target
{
int v1, v2;
CombineInt16(tex2D(_Texture, float2(0, 0.5)), v1, v2);
fixed4 col =
numbercol(float2(i.uv), CombineFloat(tex2D(_Texture, float2(0, 0))), 5, 1, 3, 4)
+ numbercol(float2(i.uv), CombineInt(tex2D(_Texture, float2(0.5, 0))), 5, 2, 7, 1)
+ numbercol(float2(i.uv), v1, 5, 3, 7, 1)
+ numbercol(float2(i.uv), v2, 5, 4, 7, 1)
;
return col;
}
関数の内容としては、0~1を0~255に戻して、順番通り並べています。
float CombineFloat(fixed4 color){
return asfloat(uint(color.r * 255.0) | (uint(color.g * 255.0) << 8) | (uint(color.b * 255.0) << 16) | (uint(color.a * 255.0) << 24));
}
float CombineInt(fixed4 color){
return asint(uint(color.r * 255.0) | (uint(color.g * 255.0) << 8) | (uint(color.b * 255.0) << 16) | (uint(color.a * 255.0) << 24));
}
void CombineInt16(fixed4 color, out int v1, out int v2){
v1 = asint(uint(color.r * 255.0) | (uint(color.g * 255.0) << 8));
v2 = asint(uint(color.b * 255.0) | (uint(color.a * 255.0) << 8));
}
結果
ちゃんと復元できていますね。
懸念点
この方法の欠点としては、テクスチャの読みこみ回数が増えることです。
exr形式のテクスチャを1回参照した時のデータ量は32×4=128なのですが、pngでは8×4=32と読みだせるデータ量が少ないです。
つまりexrと同等のことをしようとすると4倍テクスチャを参照しなければなりません。
自分が作成したVATではexrで6回参照していたため、pngに変更するとなると24回参照することになります。
そのため汎用性を持たせるために、GPU負荷を増やすことになりますね。