2024/11/21(木) [n年前の日記]
#1 [blender] blenderでobjエクスポートした際の色について調べてた
blender 4.2.3 LTS で svg をインポートして、Wavefront (.obj) でエクスポートした際に、マテリアルカラー(.mtl) 内の値(Kd)が、svg のソレとは全然違う値になる件について調べてた。
以下のやり取りで気になる話が…。
_RGB values wrong when exporting as OBJ - Support / Materials and Textures - Blender Artists Community
ガンマ補正がどうとか言ってるように見える。
blender 4.2.3 ではなく、blender 2.79b なら違う結果になるのだろうかと気になって、blende 2.79b で試してみたけれど、やはり .mtl 内の値は svg のソレと全然違う値になっていた。ただ、blender上で各マテリアルのベースカラーを確認した際、「ガンマ補正済」と表示されている点が気になった。
また、シーン設定 → カラーマネジメント → 表示デバイス、が通常では「sRGB」になっているけれど、ここを「なし」にすると、全体的に色が黒くなることに気づいた。その状態で確認したベースカラー値は、.mtl に記述されている値とほぼ同じになっている気がする…。
つまり、以下の状態なのではないかと…。仮説だけど。
以下のやり取りで気になる話が…。
_RGB values wrong when exporting as OBJ - Support / Materials and Textures - Blender Artists Community
ガンマ補正がどうとか言ってるように見える。
blender 4.2.3 ではなく、blender 2.79b なら違う結果になるのだろうかと気になって、blende 2.79b で試してみたけれど、やはり .mtl 内の値は svg のソレと全然違う値になっていた。ただ、blender上で各マテリアルのベースカラーを確認した際、「ガンマ補正済」と表示されている点が気になった。
また、シーン設定 → カラーマネジメント → 表示デバイス、が通常では「sRGB」になっているけれど、ここを「なし」にすると、全体的に色が黒くなることに気づいた。その状態で確認したベースカラー値は、.mtl に記述されている値とほぼ同じになっている気がする…。
つまり、以下の状態なのではないかと…。仮説だけど。
- blenderは内部的に、ガンマ補正してないRGB値を持っていて、普段はガンマ補正をしてから blender上でRGB値を表示している。
- Wavefront (.obj) でエクスポートする際は、ガンマ補正してないRGB値をそのまま出力している。
◎ ソースを眺めてみた :
blender 2.79b のスクリプトを眺めてみた。
.obj のエクスポートは、以下の Pythonスクリプトが担当してるはず。
Kd (Diffuse color)の出力部分は以下だろうか。
おそらく、blender が持ってるマテリアル情報をそのまま出力しているように思える。
svg をインポートする処理を眺めてみた。以下のファイルが関係してそう。
関連する部分は以下だろうか。
svg内で記述されていたRGB値は、diffuse_color に 0.0 - 1.0 の範囲で入るけど…。関数 srgb_to_linearrgb() を通した値を最終的なRGB値として処理している。
srgb_to_linearrgb() も同じファイル内で記述されていた。内容は以下。
svgファイル内に記述されていたRGB値を、この関数を通して確認してみたところ、.mtl に記述された値とほぼ同じになった。つまり…。
ということは、この関数と逆の計算をする関数を作って、.mtl 内の値を計算してやれば…。
.obj のエクスポートは、以下の Pythonスクリプトが担当してるはず。
(blender 2.79bインストールフォルダ)\2.79\scripts\addons\io_scene_obj\export_obj.py
Kd (Diffuse color)の出力部分は以下だろうか。
fw('Kd %.6f %.6f %.6f\n' % (mat.diffuse_intensity * mat.diffuse_color)[:]) # Diffuse
おそらく、blender が持ってるマテリアル情報をそのまま出力しているように思える。
svg をインポートする処理を眺めてみた。以下のファイルが関係してそう。
(blender 2.79bインストールフォルダ)\2.79\scripts\addons\io_curve_svg\import_svg.py
関連する部分は以下だろうか。
def SVGGetMaterial(color, context): """ Get material for specified color """ materials = context['materials'] rgb_re = re.compile('^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,(\d+)\s*\)\s*$') if color in materials: return materials[color] diff = None if color.startswith('#'): color = color[1:] if len(color) == 3: color = color[0] * 2 + color[1] * 2 + color[2] * 2 diff = (int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16)) elif color in svg_colors.SVGColors: diff = svg_colors.SVGColors[color] elif rgb_re.match(color): c = rgb_re.findall(color)[0] diff = (float(c[0]), float(c[1]), float(c[2])) else: return None diffuse_color = ([x / 255.0 for x in diff]) if context['do_colormanage']: diffuse_color[0] = srgb_to_linearrgb(diffuse_color[0]) diffuse_color[1] = srgb_to_linearrgb(diffuse_color[1]) diffuse_color[2] = srgb_to_linearrgb(diffuse_color[2]) mat = bpy.data.materials.new(name='SVGMat') mat.diffuse_color = diffuse_color mat.diffuse_intensity = 1.0 materials[color] = mat return mat
svg内で記述されていたRGB値は、diffuse_color に 0.0 - 1.0 の範囲で入るけど…。関数 srgb_to_linearrgb() を通した値を最終的なRGB値として処理している。
srgb_to_linearrgb() も同じファイル内で記述されていた。内容は以下。
def srgb_to_linearrgb(c): if c < 0.04045: return 0.0 if c < 0.0 else c * (1.0 / 12.92); else: return pow((c + 0.055) * (1.0 / 1.055), 2.4);
svgファイル内に記述されていたRGB値を、この関数を通して確認してみたところ、.mtl に記述された値とほぼ同じになった。つまり…。
- blenderでsvgをインポートする際に、svg内のRGB値は、srgb_to_linearrgb() で変換されてから読み込まれてる。
- blender内部ではガンマ補正されてないRGB値を持ってるけれど、UI上では補正されたRGB値が表示されてる。
- Wavefront (.obj)形式でエクスポートすると、blende内部で持ってたガンマ補正されてないRGB値が、.mtl内にそのまま出力される。
ということは、この関数と逆の計算をする関数を作って、.mtl 内の値を計算してやれば…。
[ ツッコむ ]
以上です。