mieki256's diary



2024/11/23() [n年前の日記]

#1 [blender] blenderでobjをエクスポートした際の色を補正

_昨日 の続き。blenderから Wavefront形式(.obj .mtl)でエクスポートした際、全体的に色が暗くなる件について。

blenderの画面ではガンマ補正したRGB値に基づいて色を表示してるけど、objでエクスポートすると、.mtlファイル(マテリアル情報が記述されたファイル)内にはガンマ補正してないRGB値が出力されてしまう。だから、エクスポートした .obj .mtl をそのまま使うと、blender上での見た目より色が暗くなってしまう。

故に、blenderでの見た目に近づけたいなら、.mtl内の Kd (Diffuse color) をガンマ補正してやればいい。

そんなわけで、.mtlファイルを読み込んで、Kdの値をガンマ補正して出力するPythonスクリプトを書いてみた。linearrgb_to_srgb() という関数が肝。

環境は Windows10 x64 22H2 + Python 3.10.10 64bit。

_add_gamma.py
import argparse
import os
import sys
import re

# DBG = True
DBG = False


def linearrgb_to_srgb(c):
    """Gamma correction."""
    r = 0.0
    if c < 0.0031308:
        r = 0.0 if c < 0.0 else c * 12.92
    else:
        r = 1.055 * pow(c, (1.0 / 2.4)) - 0.055
    return round(r, 6)


def main():
    parser = argparse.ArgumentParser(description="Adding gamma correction to mtl file.")
    parser.add_argument("infile", type=str, help=".mtl filename")
    args = parser.parse_args()
    infile = args.infile

    if not os.path.isfile(infile):
        print("Error : Not found %s" % (infile))
        sys.exit()

    bakfile = "%s.orig.bak" % (infile)

    # read file
    with open(infile) as f:
        lines = [s.rstrip() for s in f.readlines()]

    pattern = r"^Kd (\d+\.\d+) (\d+\.\d+) (\d+\.\d+)$"
    newlines = []
    for s in lines:
        result = re.match(pattern, s)
        if result:
            r = linearrgb_to_srgb(float(result.group(1)))
            g = linearrgb_to_srgb(float(result.group(2)))
            b = linearrgb_to_srgb(float(result.group(3)))
            nline = "Kd %f %f %f" % (r, g, b)
            newlines.append(nline)
            if DBG:
                print("%s  ->  %s" % (s, nline))
        else:
            newlines.append(s)

    # backup file. rename original file
    os.rename(infile, bakfile)

    # write file
    with open(infile, "w", newline="\n") as fo:
        for s in newlines:
            fo.write("%s\n" % (s))


if __name__ == "__main__":
    main()

使い方は以下。
python add_gamma.py INPUT.mtl
INPUT.mtl が、入力する.mtlファイル。

実行すると、元の .mtl を .mtl.orig.bak にリネームしてバックアップを残してから、元の .mtlファイル名でガンマ補正後の .mtl を保存する。

二度も三度も実行しないように注意。何度もガンマ補正されて色がどんどんおかしくなってしまうので。

動作確認 :

正しく変換できてそうか、PythonスクリプトでOpenGLを使って描画してみる。以下の環境で動作確認した。
  • Windows10 x64 22H2
  • Python 3.10.10 64bit
  • PyOpenGL 3.1.7
  • PyOpenGL-accelerate 3.1.7
  • glfw 2.7.0
  • PyWavefront 1.3.3

各モジュールは以下でインストールできるのではないかな…。
python -m pip install PyOpenGL PyOpenGL-accelerate glfw PyWavefront

動作確認用のPythonスクリプト。

_draw_opengl_glfw.py
import glfw
from OpenGL.GL import *
from OpenGL.GLU import *
import pywavefront
import argparse

model_kind = 1

modeldata = [
    {"file": "./models/cube01.obj", "scale": 2.0},
    {"file": "./models/suzanne01.obj", "scale": 5.0},
    {"file": "./models/car.obj", "scale": 7.0},
]

SCRW, SCRH = 1280, 720
WDWTITLE = "Draw wavefront obj"
FOV = 50.0

winw, winh = SCRW, SCRH
ang = 0.0
obj = None


def init_animation(infile):
    global ang, obj
    ang = 0.0
    obj = pywavefront.Wavefront(infile)


def render(scale):
    global ang, obj
    ang += 45.0 / 60.0

    # init OpenGL
    glViewport(0, 0, winw, winh)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(FOV, float(winw) / float(winh), 1.0, 1000.0)

    # clear screen
    glClearDepth(1.0)
    glClearColor(0, 0, 0, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    glDepthFunc(GL_LESS)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_BLEND)
    glEnable(GL_NORMALIZE)

    glEnable(GL_CULL_FACE)
    glFrontFace(GL_CCW)
    # glCullFace(GL_FRONT)
    glCullFace(GL_BACK)

    # set lighting
    light_pos = [1.0, 1.0, 1.0, 0.0]

    # light_ambient = [0.2, 0.2, 0.2, 1.0]
    light_ambient = [0.5, 0.5, 0.5, 1.0]

    light_diffuse = [1.0, 1.0, 1.0, 1.0]
    light_specular = [0.8, 0.8, 0.8, 1.0]

    glLightfv(GL_LIGHT0, GL_POSITION, light_pos)
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular)
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)

    # obj move and rotate
    glTranslatef(0.0, 0.0, -20.0)
    glScalef(scale, scale, scale)
    glRotatef(20.0, 1, 0, 0)
    # glRotatef(ang * 0.5, 1, 0, 0)
    glRotatef(ang, 0, 1, 0)

    # set material
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)
    # glColorMaterial(GL_FRONT, GL_DIFFUSE)
    glEnable(GL_COLOR_MATERIAL)

    # draw obj
    for mesh in obj.mesh_list:
        for mat in mesh.materials:
            # glMaterialfv(GL_FRONT, GL_AMBIENT, mat.ambient)
            # glMaterialfv(GL_FRONT, GL_DIFFUSE, mat.diffuse)
            # glMaterialfv(GL_FRONT, GL_SPECULAR, mat.specular)
            glColor4fv(mat.diffuse)
            gl_floats = (GLfloat * len(mat.vertices))(*mat.vertices)
            count = len(mat.vertices) / mat.vertex_size
            glInterleavedArrays(GL_T2F_N3F_V3F, 0, gl_floats)
            glDrawArrays(GL_TRIANGLES, 0, int(count))


def key_callback(window, key, scancode, action, mods):
    if action == glfw.PRESS:
        if key == glfw.KEY_ESCAPE or key == glfw.KEY_Q:
            # ESC or Q key to exit
            glfw.set_window_should_close(window, True)


def resize(window, w, h):
    if h == 0:
        return
    set_view(w, h)


def set_view(w, h):
    global winw, winh
    winw, winh = w, h
    glViewport(0, 0, w, h)


def main():
    global model_kind

    objname = ""
    scale = 1.0
    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--model", type=int, help="Model type 0 - 2")
    parser.add_argument("-o", "--obj", type=str, help=".obj filename")
    parser.add_argument("-s", "--scale", type=float, help="Scale")
    args = parser.parse_args()

    objname = modeldata[model_kind]["file"]
    scale = modeldata[model_kind]["scale"]

    if args.model is not None:
        model_kind = args.model
        if model_kind < len(modeldata):
            objname = modeldata[model_kind]["file"]
            scale = modeldata[model_kind]["scale"]
    else:
        if args.obj is not None:
            objname = args.obj
        if args.scale is not None:
            scale = args.scale

    if not glfw.init():
        raise RuntimeError("Could not initialize GLFW3")
        return

    window = glfw.create_window(SCRW, SCRH, WDWTITLE, None, None)
    if not window:
        glfw.terminate()
        raise RuntimeError("Could not create an window")
        return

    # set OpenGL 1.1
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 1)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 1)

    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    glfw.window_hint(glfw.DEPTH_BITS, 24)

    glfw.set_key_callback(window, key_callback)
    glfw.set_window_size_callback(window, resize)
    glfw.make_context_current(window)
    glfw.swap_interval(1)

    set_view(SCRW, SCRH)

    init_animation(objname)

    # main loop
    while not glfw.window_should_close(window):
        render(scale)
        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.destroy_window(window)
    glfw.terminate()


if __name__ == "__main__":
    main()

使用するモデルデータ(.obj .mtl)は以下。draw_opengl_glfw.py と同階層に models/ というフォルダを作成して、その中に .obj と .mtl を入れておく。

_models.zip

使い方は以下。
Usage:
    python draw_opengl_glfw.py [-m N] [--obj INPUT.obj] [--scale N]

    -m N, --model N     : モデル種類 0 - 2 を指定
    -o FILE, --obj FILE : .objファイル名
    -s N, --scale N     : 拡大縮小率
python draw_opengl_glfw.py -m 0
python draw_opengl_glfw.py -m 1
python draw_opengl_glfw.py -m 2

python draw_opengl_glfw.py --obj models/car.obj --scale 7.0

動作結果。以下の順番でスクリーンショットを並べてある。
  1. blenderでの表示
  2. ガンマ補正無しの .mtl を使用
  3. ガンマ補正有りの .mtl を使用

model_suzanne01_ss01.png
draw_opengl_glfw_ss03.png
draw_opengl_glfw_ss04_gamma.png

model_car_ss01.png
draw_opengl_glfw_ss05.png
draw_opengl_glfw_ss06_gamma.png

ガンマ補正してあるほうが、blender上での見た目に近い。

もっとも、照明の当て方次第で色合いはどうしても変わってしまうけれど…。

余談。PyOpenGLについて :

Python から OpenGL を利用できるようにする PyOpenGL は、pip でネット経由でインストールすることもできるけど。
python -m pip install PyOpenGL

その版にはGLUT関係の dll が入ってないようで、もしかするとGLUTを使ったスクリプトが動かなかったりするかもしれない。

その場合は、非公式版パッケージを入手してインストールして使うのもアリかも。非公式版なら freeglut.dll 等も入ってる。

_Releases - cgohlke/pyopengl-build
_Ultravioletrayss/OpenGLfile
_cgohlke/pyopengl-build: Build PyOpenGL wheels for Windows

Python 3.10.10 64bit の場合、PyOpenGL-3.1.8-cp310-cp310-win32.whl か PyOpenGL-3.1.7-cp310-cp310-win32.whl、PyOpenGL_accelerate-3.1.7-cp310-cp310-win32.whl を入手してインストールすることになる。

まあ、ネット経由でインストールして使えているなら、それに越したことはないけれど…。

#2 [cg_tools] epsのバージョンについて少しだけ調べてた

3DCGソフト Shade は eps ファイルをインポートできるらしいのだけど、手持ちの Shade 12 Standard、Shade 10.5 Standard では、Inkscape その他からエクスポートした eps をインポートすることができなくて、一体どんな eps ならインポートできるのか気になってきた。

_Shade3D 公式 | Illustratorからのデータインポートについて。
Shadeは、Illustratorのデータ(.ai、.eps)をインポートすることができます。
Illustrator 9以上のデータをShadeにインポートするには、Illustratorで保存時のオプションを以下の設定にする必要があります。

* 互換性:illustrator ver.8.0以前
* プレビュー:なし(EPSの場合)

Shade3D 公式 | Illustratorからのデータインポートについて。 より


8.0以前と言うことは、5.5 や 7.0 で保存した eps が対象ということだろうか?

参考ページ :

Creator行について :

epsファイルをテキストエディタで開いてみると、「%%Creator: 〜」と書かれた行がある。例えば Inkscape 1.4 で保存した eps は以下になってる。
%%Creator: cairo 1.18.2 (https://cairographics.org)

前述の解説ページによると、Adobe Illustrator の場合、以下の3種類の行が存在するようで…。
CreatorTool
AI8_CreatorVersion
Creator

eps の場合、以下になってる、とある。
  • CreatorTool は v11以降に存在。
  • AI8_CreatorVersion は v8 以降に存在。
  • Creator は全バージョンにある。

Shade は「v8以前の eps にしろ」と言ってるから、CreatorTool と AI8_CreatorVersion は存在しなくてもいいのだろう…。となると、Creator と書かれた行さえあればいいということになるのかな。

Inkscape で保存した eps の一部を以下に修正してみた。
%%Creator: Adobe Illustrator(R) 5.5

仮に Shade が「どのツールでエクスポートした eps か」を逐一チェックして処理する/しないを分けているなら、この修正で行けそうだけど…。しかし、Shade でインポートしてみても変化はなかった。

まさか改行コードが関係してないか。通常は、eps は LF で保存されるように見えるけど…。試しに CRLF や CR に変更してみたけれど、Shade上では変化無し。

サンプルファイルが欲しい :

Shade で読み込める eps のサンプルファイルとかどこかにないのかな。Adobe Illustrator 5.5 や 7.0 で保存された eps でもいい。Shade は本当に eps をインポートできるのか? そこからして怪しい。サンプルファイルさえあれば検証できるのに。

いやまあ、フォーラム等を眺めると、昔はインポートできていた事例もあったらしいけど…。

もしかして Mac ならインポートできて Windows はインポートできないというオチだったりしないか?

Adobe Illustrator のバージョンが上がるたびに、出力される .ai も .eps も中身が変わっているというのが結構困る…。

バグの有無も気になる :

Illustrator 7.0 - 10.0 は出力した eps にバグがあるという話も気になる。まさか、バグがある状態の eps じゃないと Shade は正常にインポートできない、なんてオチだったりして…?

_Illustrator - TeX Wiki
_Illustrator 7.0/8.0/9.0 BeginData Bug

余談。Metafile to EPS Converterをインストールしてみた :

Metafile to EPS Converter というツールを使うと、wmf を eps に変換できるらしい。Windows10 x64 22H2上でインストールしてみた。

_LyX wiki | Windows / MetafileToEPSConverter
_Metafile to EPS Converter - TeX Wiki

metafile2eps.exe を入手して実行。今回は "D:\Prog\Metafile to EPS Converter" にインストールしてみた。

たしかに wmf から eps に変換できた。ただ、出力された eps の中身を眺めたら、「%%Creator: PScript5.dll Version 5.2.2」と記述されていた。

以下のページで解説されてる、MS Publisher Color Printer で epsを出力した場合も、「%%Creator: PScript5.dll Version 5.2.2 」と同じ記述になるので、仮想プリンタ経由で eps を出力しているのかもしれない。

_PowerPoint から TeX 用の EPS ファイルを生成する方法 - ソフトウェア開発日記

もちろん、Shade ではインポートできない eps になる。

2024/11/22(金) [n年前の日記]

#1 [blender] blenderでobjエクスポートした際の色について調べてた。その2

_昨日 に引き続き、blender 4.2.3 LTS で svg をインポートして Wavefront (.obj) でエクスポートした際に、マテリアルカラー(.mtl) 内の値(Kd、Diffuse color)が svg のソレとは全然違う値になる/全体的に色が暗くなる件について調べてた。

svg をインポートした際に、scripts\addons\io_curve_svg\import_svg.py 内で、srgb_to_linearrgb() を経由してRGB値が変換されることは分かった。ということは、逆の計算をする関数を作れば、.mtl内のRGB値を、元々のsvgのRGB値に戻すことができるのではないかと。

関数の名前からして、逆の計算をする関数が ―― linearrgb_to_srgb() が既に書かれてそうだなと…。ググってみたら見つかった。

_color management - What's the exact gamma correction Blender uses? - Blender Stack Exchange
_blender/source/blender/blenlib/intern/math_color.c at master - dfelinto/blender
_#94202 - crash or error when using GPUFrameBuffer.read_color(... data=data) - blender - Blender Projects
_FBX SDK Python2020 の使用例2(マテリアル書き込み編 ブレンダーからクリップスタジオモデラーマテリアル画面へ)|ハイドロキャリス

C言語で書かれている事例は以下。
float linearrgb_to_srgb(float c)
{
  if (c < 0.0031308f) {
    return (c < 0.0f) ? 0.0f : c * 12.92f;
  }

  return 1.055f * powf(c, 1.0f / 2.4f) - 0.055f;
}

Pythonで書くなら以下になるのだろうか。
def linearrgb_to_srgb(c):
    if c < 0.0031308:
        if c < 0.0:
            return 0.0
        return c * 12.92
    return (1.055 * pow(c, (1.0 / 2.4)) - 0.055)

検証 :

手持ちのsvgファイル内では「#242424」と書かれていたRGB値が、blenderからエクスポートした .mtl内では以下の値になっていた。

newmtl Black01
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.017642 0.017642 0.017642
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 0

PythonのIDLE上で計算してみる。
>>> hex(int(0.017642 * 255.0))
'0x4'
0x24 になってほしいのに、0x04 になってる。全然違う。

linearrgb_to_srgb() を定義して試してみた。
>>> def linearrgb_to_srgb(c):
...     if c < 0.0031308:
...         if c < 0.0:
...             return 0.0
...         return c * 12.92
...     return (1.055 * pow(c, (1.0 / 2.4)) - 0.055)
... 
>>> hex(int(linearrgb_to_srgb(0.017642) * 255.0))
'0x24'

0x24になった。合ってる気がする。

他の値も入れて確認してみたけれど、一部の値で誤差が出るのか全てがピッタリにはならなかったものの、ほとんど大体は合ってる値になった。

ということで、blender からエクスポートした Wavefront (.obj) の色が全体的に暗い場合は、この関数を通してRGB値を求め直してやれば本来のRGB値に近づいてくれる可能性が高い。

ただ、blender に .obj をインポートする際は、RGB値を変更しちゃうと二重に変換がかかってしまってマズイことになりそう。

実際に表示してみた :

しばらく前に、Wavefront形式(.obj .mtl)を読み込んで、C言語のヘッダファイルの形に変換するPythonスクリプトを書いたのだけど。

_mieki256's diary - OpenGLでモデルデータを読み込めそうか調べてる。その6

そのスクリプトに、前述の関数を追加して、.mtl内の Kd にガンマ補正をかけたRGB値を出力するように修正して、見た目がどう変わるか確認してみた。

せっかくだから github にアップロードしておいた。pyobj2c.py が、obj → c に変換するPythonスクリプト。

_mieki256/pyobj2c

OpenGLで描画するサンプルソース 01_drawobj.c をコンパイルして動かしてみた。スクリーンショットを以下の順番で並べてある。
  1. blender 4.2.3 LTSのマテリアルプレビュー
  2. pyobj2c.pyでガンマ補正した描画結果
  3. ガンマ補正無しの描画結果

blender423_ss02.png
01_drawobj_ss11_gamma.png
01_drawobj_ss11.png

blender423_ss01.png
01_drawobj_ss12_gamma.png
01_drawobj_ss12.png

blender423_ss03.png
01_drawobj_ss13_gamma.png
01_drawobj_ss13.png

ガンマ補正をかけたほうが、全体的に色が明るくなって、blender上での見た目に近い色になっていることが分かる。まあ、照明の当て方次第で色は変わってしまうものだけど…。それでも、今までの .obj .mtl より全然マシな見た目になってくれたかなと…。

#2 [cg_tools] Ghostscriptをインストールした

Windows10 x64 22H2上で、Ghostscript 10.04.0 32bit と 64bit をインストールしてみた。

経緯 :

3DCGソフト Shade は、epsf (eps?) をインポートできるという情報を見かけて、試してみるかと Inkscape 1.4 64bit で eps を保存してみたものの、本当に保存できたのか確認しようとして悩んでしまった。epsファイルを閲覧するためのソフトがない…。

IrfanView なら eps も開けるっぽいので、IrfanView 4.70 32bit で開こうとしたところ、「開けない。Ghostscriptがインストールされてないのとちゃうか?」と言ってきた。Ghostscript が必要になるのか…。

ググってみたら、Inkscape で eps を開くためにも、Ghostscript が必要になるらしい。

しかし困った。Inkscapeは64bit版で、IrfanViewは32bit版。Ghostscriptも32bit/64bitを合わせなければいけないのでは。…とりあえず32bit版と64bit版の両方をインストールしてみよう。

インストール :

_Ghostscript : Downloads
_Ghostscript/Windows - TeX Wiki
_【Ghostscript】InkscapeでEPSファイルを開く方法 - きまぐれモノログ。
_InkscapeでEPSファイルを読み込むための設定手順 | tipLog - kimama blog
_InkscapeでEPSファイルを開く方法、Ghostscriptのインストール

公式サイトから gs10040w32.exe と gs10040w64.exe を入手。それぞれ実行して、D:\gs\gs10.04.0.32bit\ と D:\gs\gs10.04.0.64bit\ にインストールしてみた。

環境変数で、GS_PATH と GS32_PATH を作成。Ghostscript の bin/ とlib/ を指定。
GS_PATH=D:\gs\gs10.04.0.64bit\bin;D:\gs\gs10.04.0.64bit\lib
GS32_PATH=D:\gs\gs10.04.0.32bit\bin;D:\gs\gs10.04.0.32bit\lib

環境変数PATHに、前述の変数を追記。
PATH=%PATH%;%GS_PATH%;%GS32_PATH%

この状態なら、IrfanView 4.70 32bit で eps を開けたし、Inkscape 1.4 64bit でも eps を開けた。

ちなみに、前述のページによると、Inkscape で開こうとした際、ファイル選択ダイアログ内に epsファイルが見えなかったら、Ghostscript が Inkscapeから見えてなくて、epsを開けない状態になっている、とのこと。

インストールし直した :

その後ググっていたら、GSview 5.0 は Ghostscript 10.04.0 に対応していないという話を見かけた。Ghostscript 9.52 + GSview 5.0 の組み合わせがいいらしい…。そちらを使うことにしよう…。先ほどインストールした Ghostscript 10.04.0 32bit/64bit をコントロールパネル経由で両方アンインストール。

_Ghostscript と GSview のインストール #TeX - Qiita
_Ghostscript 9.54.0 and GSview 5.0 J (Official Site)

gs952w32full-gpl.exe と gsv50w32.exe を入手。実行してインストール。今回は、D:\gs\gs9.52_32bit\ と D:\gs\Ghostgum\ にインストールした。

環境変数 GS_PATH を設定。
GS_PATH=D:\gs\gs9.52_32bit\bin;D:\gs\gs9.52_32bit\lib

環境変数 PATH を指定。
PATH=%PATH%;%GS_PATH%

IrfanView 4.70 32bit と Inkscape 1.4 64bit で eps が開けることを確認。32bit版しかインストールしていないのに、どちらも動いてる。

GSviewが動かない :

GSview 5.0 で、Inkscape から保存した eps を開こうとするとエラーが出て開けない…。

_QA: Windows 用 gs-9.25 と gsview-5.0 について | TeX
_GSview - TeX Wiki

  • GSview のメニュー Options → Safer のチェックを外す。
  • かつ、Options → Advanced Configure... → Ghostscript Options: に「-dSAFER」を追加。
この設定で開けるようになった。

#3 [cg_tools] 3DCGソフトでベクターファイルがインポートできるか試した

手持ちの3DCGソフトでベクターファイルがインポートできるか試してみた。

blenderの場合 :

blender 4.2.3 LTS なら、Inkscape 1.4 から保存した svg も dxf もインポートすることができた。
  • svg は面の色が反映される。
  • dxf はパスの線だけが読み込まれる。塗りは反映されない。
そもそも dxf に塗りは無さそう。Inkscape で dxf を開き直しても線しか出てこない。

Shadeの場合 :

Shade 12 Standard 64bit は epsf(eps?) と dxf のインポートに対応している、と記述がある。しかし…。

epsのインポートは全然ダメ。Inkscape 1.4 で保存した eps をインポートしようとしたのだけど、ルートパートは追加されたものの、中身は空。インポートできてないっぽい。

ググった感じだと、Shade はかなり古いフォーマットの eps じゃないと読み込めないようで…。Adobe Illustrator から eps を書き出す際は Legacy なんとかを選べ云々という話を見かけた。

_Shade3D 公式 | Illustratorからのデータインポートについて。

古い eps を出力してくれるツールはないものかと、色々なアプリで eps を作成してインポートできないか試してみたけれど、どれもダメ。
  • EPS-draw 4.51 : Shade が「EOFエラー: 読み込み中にファイルの終端に達しました。」とエラーダイアログを出す。
  • Tgif 4.2 + Ubuntu Linux 20.04 LTS : 同じエラーが出る。
  • Xfig 3.2.7b + Ubuntu Linux 20.04 LTS : 同じエラーが出る。
  • Affinity Designer 2.5.5 : 同じエラーが出る。
  • Ipe 7.2.29 x64 : エラーは出ないけど、ルートパートの中身は空。
  • LibreOffice Draw 24.8.3.2 x64 : エラーは出ないけど、ルートパートの中身は空。
どれも IrfanView 4.70 32bit や Inkscape 1.4 で開ける eps なのだけど…。

_EPS-draw
_The Ipe extensible drawing editor


以下のページを参考にして、Windows10 x64 22H2上で仮想プリンタ(MS Publisher Color Printer)をインストールして、印刷結果をepsとしてファイル保存して Shade でインポートできるか試したけれど、これもダメ。

_PowerPoint から TeX 用の EPS ファイルを生成する方法 - ソフトウェア開発日記

どの eps もインポートできない。一体どんな eps ならインポートできるのか…。

eps と違って dxf なら、Inkscape から保存した dxfファイルもすんなりインポートできた。もっとも、全てが単なる直線になってしまうけれど…。下絵の代わりぐらいにはなるのだろうか。

svgをインポートできるようになる有料プラグインもあるらしい。

_SVG Importer - Shade3D 公式 | 商品紹介

ただ、動作環境が「Shade 3D ver.14 Standard/Professional 以降 (*Shade 3D Basicを除く)」となっている。自分が持ってる Shade 12 Standard では使えないかもしれない。

つまるところ、自分の手持ちの Shade でベクターデータをインポートする方法は無いと思っておいたほうが良さそう。blenderのようにはいかない…。

Wings 3Dの場合 :

Wings 3D 2.4 x64 で svg をインポートしてみたところ、たしかにインポートはできるものの、自動で立体化されてしまった。加えて、ポリゴンも無駄にたくさん分割されている。

import_svg_to_wings3d_24_ss01.png


wmf をインポートしてみたところ、こちらも自動で立体化されるものの、無駄なポリゴン分割はされなかった。

import_svg_to_wings3d_24_ss02.png

ただ、奥側の面を削除することができない…。一部のポリゴンを選んで消したつもりでも、全部が消えてしまう…。

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 に記述されている値とほぼ同じになっている気がする…。

つまり、以下の状態なのではないかと…。仮説だけど。

ソースを眺めてみた :

blender 2.79b のスクリプトを眺めてみた。

.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 内の値を計算してやれば…。

以上、3 日分です。

過去ログ表示

Prev - 2024/12 -
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project