2009年07月17日

バンプマッピングとファイルブラウザ機能の追加

vectex内臓版Blenderをいくつかの機能アップを行ったものに更新しました。
実行ファイルはこちらです。
Linux 32bit(Blender_Qt_vectex_Linux32090709.tar.gz)
Linux 64bit(Blender_Qt_vectex_Linux64090709.tar.gz)
Windows 32bit(Blender_Qt_vectex_win32090709.zip)

ソースファイルのパッチはこちら(Blender_Qt_vectex_patch090717.zip)です。

※追記(2009/07/19) 実行ファイルをバグ修正版と置き換えました。
修正したバグの内容は、vectexは全ピクセル同色のタイル画像がある場合そのタイルを破棄して代わりにその色情報だけを保持しますが、その判別部分に問題があり全ピクセルが完全に同色でないときにも完全同色タイルとして処理してしまうことがありました。

ソースファイルのパッチはこちら(Blender_Qt_vectex_patch090719.zip)です。

Aggを使ったバージョンも同じ内容で更新してあります。
Linux 32bit(Blender_modified_Linux32090611.tar.gz)
Linux 64bit(Blender_modified_Linux64090611.tar.gz)
Windows 32bit(Blender_modified_win32090611.zip)

こちらバージョンのパッチはこちら(Blender_agg_vectex_patch090717.zip)です。

実行ファイルの方は、前回アップロードしたファイルを新しいものと交換していますので、Qt、AGGのどちらともURLは以前のものと同じです。


ここから今回の更新内容の説明になります。
今回はBlenderに組み込んだvectexにバンプマッピングを使用できるように修正を行いました。
さらに、前回追加済みのファイルブラウザの機能に加え、SVGファイル名に相対パスを使用できるように修正を加えています。

○バンプマップ
ずいぶん前になりますが、プラグイン版のvectexを使ってkururuの服のテクスチャをSVGファイルに置き換えてレンダリングを試してみたことがありました。
そのときは、texture pluginのプログラムの内容についてまったく知識がなかったこともあり、バンプマップが機能しない理由について完全に見当違いなことを書いてしまいました。
実は、Blenderのtexture pluginでバンプマッピングを使えるようにするためには、自前で法線を計算してそれをBlenderのレンダラに返すようにプログラムを作る必要があります。
プラグイン版のvectexには、そのようなバンプマッピング用の法線計算を行う処理はどこにも書かれていません。

vectexでは一旦キャッシュ上にSVGをレンダリングしたビットマップ画像を作成するという処理を行っています。
作成された画像は通常のイメージテクスチャと何も変わらないものなので、バンプマッピングに対応するために必要な処理というのは基本的にはBlenderのイメージテクスチャのソースコードをそのままコピーしてくるだけで済むように思えます。

Blenderのイメージテクスチャの計算が行われている関数は、source/blender/render/intern/source/imagetexture.cというファイルにある
  1. imagewrap() (line:105-)
  2. imagewraposa() (line:629-)
この2つの関数となります。

imagewrap()はRenderパネルのOSAボタンがオフのときに使用され、imagewraposa()はOSAボタンがオンのときに使用されます。
pic090717_01.jpg

imagewrap()の中でバンプマッピングに関係する部分は、以下の部分です。(line:207-235)
int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, TexResult *texres)
{
...
    if(texres->nor) {

        if(tex->imaflag & TEX_NORMALMAP) {
            // qdn: normal from color
            texres->nor[0] = 2.f*(texres->tr - 0.5f);
            texres->nor[1] = 2.f*(0.5f - texres->tg);
            texres->nor[2] = 2.f*(texres->tb - 0.5f);
        }
        else {
            /* bump: take three samples */
            val1= texres->tr+texres->tg+texres->tb;

            if(x<ibuf->x-1) {
                float col[4];
                ibuf_get_color(col, ibuf, x+1, y);
                val2= (col[0]+col[1]+col[2]);
            }
            else val2= val1;

            if(y<ibuf->y-1) {
                float col[4];
                ibuf_get_color(col, ibuf, x, y+1);
                val3= (col[0]+col[1]+col[2]);
            }
            else val3= val1;

            /* do not mix up x and y here! */
            texres->nor[0]= (val1-val2);
            texres->nor[1]= (val1-val3);
        }
...
}

このうち、始めの方の数行はバンプマップではなくて通常ノーマルマップと呼ばれている機能のものです。
バンプマッピングについては、簡単に説明すると

現在描画中のピクセルの色のRGB値を普通に合計してval1とします。
横方向に1ピクセル先の色を取得して、そのRGB値を合計してval2とします。
縦方向に1ピクセル先の色を取得して、そのRGB値を合計してval3とします。
Blenderのレンダラに計算結果を渡すために引数で受け取っているtexresの中の法線データ用の場所に
 X成分:val1-val2
 Y成分:val1-val3
を格納する、というようなものになっています。

imagewraposa()の方は、ミップマップがオンのときとオフのときそれぞれについて必要な処理が違っていて、これに比べるとかなりややこしいものになっています。
ミップマップがオフのときは、先ほどのものとかなり近い内容です。(line:950-967)
int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, float *dyt, TexResult *texres)
{
...
        if((tex->imaflag & TEX_INTERPOL)) {

            /* sample 1 pixel minimum */
            if (minx < 0.5f / ibuf->x) minx = 0.5f / ibuf->x;
            if (miny < 0.5f / ibuf->y) miny = 0.5f / ibuf->y;
        }

        if(texres->nor && (tex->imaflag & TEX_NORMALMAP)==0) {
           
            boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend);
            val1= texres->tr+texres->tg+texres->tb;
            boxsample(ibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend);
            val2= texr.tr + texr.tg + texr.tb;
            boxsample(ibuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend);
            val3= texr.tr + texr.tg + texr.tb;
            /* don't switch x or y! */
            texres->nor[0]= (val1-val2);
            texres->nor[1]= (val1-val3);
        }
...
}

違っているのは、RGB成分を普通に足すのではなくboxsample()という関数で計算していること、val2、val3の色を取得する位置を縦横1ピクセル先ではなくて
 val2: x方向にdxt[0]、y方向にdxt[1]足した場所
 val3: x方向にdyt[0]、y方向にdyt[1]足した場所
となっていることです。

詳しいことはわからないのですが、dxt、dytにはOSAでアンチエイリアスの計算をするためにデータが渡されています。
dxt[0]、dyt[0]には現在描画中の位置に対応するテクスチャのUV座標、dxt[1]、dyt[1]には次に描画する位置に対応するUV座標が入っているようです。
※追記(2009/07/21)  現在描画中の位置のUV座標はtexvecに渡されています。dxt、dytには、UV座標上での現在の描画位置から次の描画位置までの差分のベクトルが入っているのではないかと思います...が、まだはっきりわかっていません。
このデータを使ってvectexでは現在描画中のピクセルの解像度を計算しています。

imagerap()の中の処理の用に縦横1ピクセル先というように指定してval2、val3を計算してしまうと、レンダリング画像内でのテクスチャの向きが変化したときにバンプマップの凹凸が正しく計算されません。
pic090717_02.jpg pic090717_03.jpg

ミップマップがオンの場合、かなり長くなるのでソースコードは省略しますが、異なる解像度の画像を現在レンダリングしている位置に応じた重み付けをして重ねるという処理をします。
さらにbumpscapeという値を計算し、オブジェクトがカメラから離れてテクスチャの解像度が低くなるほどバンプマップの凹凸の深さが小さくなるようにしています。
pic090717_04.jpg pic090717_05.jpg

以上のような内容のソースコードをvectex()関数の中に追加しました。
vectexではBlenderのイメージテクスチャで「MipMap」や「interpol」を指定したときに使用されるミップマップ処理や補完処理とは全く別の独自の計算で同様の処理を行っていますので、追加したコードもそれに合わせていろいろと修正をしています。
この部分は、特に参考にできるコードとかもないので自分で考えて書いているため、もしかすると色々と勘違いしたりしてバグがあったりするかもしれません。

とりあえず、自分でテストした範囲ではなんとか動いているようです。
参考までに、2400x2400ピクセルのpng画像を使った場合とvectexを使った場合の画像を比較できるように並べておきます。
pic090717_06.jpg pic090717_07.jpg

pic090717_08.jpg pic090717_09.jpg

○ファイルブラウザ
vectexを使っているとSVGファイルの指定をするのにフルパスでキーボードから名前を入力しなければならず、かなり面倒に感じました。
できるだけ早くなんとかしたかったこともあって、前回の更新のときにファイルブラウザの機能はすでに搭載済みです。
前回はこの機能について書けなかったので、今回詳しく書きたいと思います。

まず、ファイルブラウザを表示させるためのボタンが必要です。
そのボタンは、
source/blender/src/buttons_shading.cの中のvectexパネルの設定を行っているtexture_panel_vectex()関数の中で追加します。(line:796)
static void texture_panel_vectex(Tex *tex)
{
...
    uiDefIconBut(block, BUT, B_LOADVECTEX, ICON_FILESEL,    160, 100, 20, 20, 0, 0, 0, 0, 0, "Select the directory/name for saving animations");
...
}

この記述自体は、Outputパネルのアニメーションファイルの保存場所を指定するボタンの記述を参考にしています。
pic090717_11.jpg
(ツールティップの文字列がそのままになっていました。次の更新のときに直したいと思います...)

この記述は、ボタンが押されたときに「B_LOADVECTEX」というメッセージが送信されるように、uiDefIconBut()関数の3つめの引数を指定してあります。

このB_LOADVECTEXというのは番号を割り当てられているマクロ記述の定数で、
source/blender/include/butspace.h
というファイルの中に記述を追加しています。(line:256)
...
#define B_BANDCOL        1319
#define B_LOADTEXIMA1    1320
#define B_TEXPRV        1321
#define B_LOADVECTEX    1322
...
B_LOADVECTEXというメッセージを受け取って処理をするのは、
source/blender/src/buttons_shading.c の中の do_texbuts() (line:279-)
という関数です。

この中にプラグイン選択ボタンの処理があるので、それを参考に次のようにコードを追加しています。
void do_texbuts(unsigned short event)
{
...
    case B_LOADVECTEX:
        sa= closest_bigger_area();
        areawinset(sa->win);
       
        if(tex->vtex_instance_data) strcpy(str, tex->vtex_file_name);
#ifdef _WIN32
        else {
            if (strcmp (U.textudir, "/") == 0)
                strcpy(str, G.sce);
            else
                strcpy(str, U.textudir);
        }
#else
        else strcpy(str, U.textudir);
#endif
       
        activate_fileselect_args(FILE_SPECIAL, "SELECT SVG FILE", str, load_vectex_file, tex, NULL);

break;

...

}

ここで、最初の部分はファイルブラウザを開くためのウィンドウ上の位置を適当に確保しているようです。
最後のactive_fileselect_args()という関数が実際にファイルブラウザを開く処理をしているようです。
この関数では、ファイルブラウザで選択したファイルに対してどのように処理をするべきかを指示するため、4つ目の引数でload_vectex_file()という関数の名前を指定しています。
このような関数は元々存在していなくて、自分で追加する必要があります。
間の部分は、active_fileselect_args()関数からファイル名を受け取るための文字列に、デフォルト値としてあらかじめ文字列を設定しておくためのものですが、わざわざ書かなくてもとくに問題ないようなものです。
ただし、この文字列がまったく初期化されていないと、意味不明な文字列がファイルブラウザに表示されてしまいます。

active_fileselect_args()に渡したload_vectex_file()という関数です。(line:238-)
static void load_vectex_file(char *str, void *tex_v, void *unused)    /* called from fileselect */
{
    Tex *tex= tex_v;
   
    if(tex->type!=TEX_VECTEX) return;
   
    if(tex->vtex_instance_data)     strcpy(tex->vtex_file_name, str);   
   
    allqueue(REDRAWBUTSSHADING, 0);
    BIF_preview_changed(ID_TE);
}
この関数に引数で渡されるtex_vはsource/blender/makesdna/DNA_texture_types.hに定義されているTex構造体ですので、その中のtypeでTEX_VECTEXがセットされているか、vtex_instance_dataの実体が存在するかを確認した上で、vtex_file_nameにファイルブラウザから渡されたファイル名をコピーしています。

Tex構造体の中のvtex_file_nameが設定された後の処理は、普通にvectexパネルで文字列をキーボード入力で指定したときと同じです。
source/rblender/render/intern/source/texture.cのvectex()関数の中で常に文字列が変わったかどうかをチェックしています。

○拡張子の色別強調表示
ファイルブラウザが開いたとき、.blendファイルやpythonスクリプト、画像ファイルなど特定の種類のファイルに対して小さな四角形がファイル名の横に表示され、その色でファイルの種類を判別しやすいようになっています。

デフォルトのBlenderではSVGファイルについてはこの強調表示がされるようになっていません。
これでは色々と不便なのでSVGファイルにも小さな四角形を表示するようにしました。
pic090717_12.jpg

ただし、jpegファイルやPNGファイルと同じ画像ファイルにしてしまうと、vectexパネルから間違ってjpegファイルを開こうとしてしまったり、逆にイメージテクスチャを選択するときにSVGファイルを開いてしまったりすることになります。

そこで、SVGファイルのために新しいファイルタイプを作ってしまいました。
source/blender/makesdna/DNA_space_type.h (line:491)
...
#define SOUNDFILE            256
#define TEXTFILE            512
#define MOVIEFILE_ICON        1024 /* movie file that preview can't load */
#define FOLDERFILE            2048 /* represents folders for filtering */
#define VECTORIMAGEFILE        4096
...
ファイルブラウザで表示されるファイル名から拡張子を調べる関数がありますので、そこにSVGファイルのための処理を追加します。
source/blender/src/filesel.c (line:334-)
void test_flags_file(SpaceFile *sfile)
{
...
                else if(BLI_testextensie(file->relname, ".svg")) {
                    file->flags |= VECTORIMAGEFILE;

                }   
            }
        }
    }   
}
そして、同じファイルの中のprint_line()という関数に、VECTORIMAGEFILEに対応した新しい色の四角形をファイルブラウザに表示させるための記述を追加します。

source/blender/src/filesel.c (line:731-)
static void print_line(SpaceFile *sfile, struct direntry *files, int x, int y)
{
...
    else if(files->flags & VECTORIMAGEFILE) {
        cpack(0x7744dd);
        glRects(x-14,  y,  x-8,  y+7);

    }
...
}
cpack()という関数は、リトルエンディアン、ビッグエンディアンの違いに関係なくRGB値を文字列で指定するためのもので、
cpack(0x7744dd)
という指定は、RGB(221,68,119)という値となり、青より赤に近い薄紫色という感じになります。
pic090717_13.jpg

○相対パスの指定
BlenderではLinuxなどで通常使用される「.」(ドット)を指定した相対パスとは別の「//」という文字列をパスの先頭に指定することで、相対パスを表す仕組みになっているようです。

ファイルブラウザで「relative paths」というボタンを押してファイルを指定すると、この形式で相対パスを指定した文字列が返ってきます。
SVGファイルを開く関数でこの形式に対応するための処理を追加しました。
source/blender/vectex/vectex.cpp (line:58-)
EXT void *vtex_create_backend(char *filename)
{
...
    BLI_strncpy(str, filename, sizeof(str));
    if(str[0] != '.')
        BLI_convertstringcode(str, G.sce);

...
}
BLI_convertstringcode()という関数に絶対パスの入った文字列を渡すと、相対パスに変換されて戻ってきます。
ただ、この関数にLinuxの通常の「.」で始まる相対パスを渡すと問題があるようなので、その場合は処理をスキップするようにしています。
posted by mato at 23:02| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。