2009年08月29日

スカイテクスチャ、ランプテクスチャ、ヘイローテクスチャ

テクスチャといえばマテリアルに設定するマテリアルテクスチャをまず思い浮かべると思いますが、Blenderにはそれ以外にもテクスチャが使用される場所がいくつかあります。

前回の記事では、テクスチャタイプがTEX_IMAGEのときに処理をしている場所にTEX_VECTEXでも同じ処理が行われるように修正を行いました。(blender/source/blender/render/intern/source/texture.c)
その修正を行った場所はdo_material_tex()の中以外に、do_halo_tex()、do_sky_tex()、do_lamp_tex()など6ヶ所ありました。

スカイテクスチャ、ランプテクスチャ、ヘイローテクスチャについては、単純にイメージテクスチャと同じ座標データが得られるようにしただけではvectexの処理が有効にはならないようで、前回の更新後の状態ではvectexを使用すると解像度が255ピクセルの画像が表示されるだけで、レンダリング画像で要求される最適な解像度のテクスチャは得られていませんでした。
ちなみにvectexはOSAがオフになっている場合など、テクスチャの解像度計算ができない状況ではタイル画像のサイズ(縦横255ピクセル)でテクスチャを表示します。
pic090829_04.jpg pic090829_05.jpg

今回は、スカイテクスチャ、ランプテクスチャ、ヘイローテクスチャでvectexが使用できるようにソースコードの修正をしています。
pic090829_01.jpg pic090829_02.jpg pic090829_03.jpg

今回から、QtSVGを使用したバージョンのダウンロードファイルをGraphicAll.orgに置かせてもらうようにしました。

実行ファイルはこちらになります。(実行ファイルへの直接リンクではなくて、ダウンロードページへのリンクとなります)
Linux 64bit

Linux 32bit

Windows 32bit

ソースファイルのパッチ (Blender2.49.2_Vectex_QtSVG_patch.zip)

AGGバージョンについても更新しています。
AGGバージョンの方は、これまでと同じようにSeesaaのサーバに同じURLで置いてあります。
AGGバージョンのソースファイルのパッチ (Blender_modified_patch090826.zip)

前回からの修正点は、上に書いたようにマテリアルテクスチャ以外のテクスチャへの対応を行ったことです。
バグフィックスについてはとくに行っていません...と思っていましたが、テクスチャプレビュー画面にあるデフォルトボタンを押したときにvectexパネルのresolution数値ボタンの値が「1.0」に初期化されなかったのを修正していました。


今回からは、更新時のリンクの書き換えの手間を省略するため、ソースファイルのパッチについても同じURLを使い続けることにしました。
その代わりに、前回の更新からの変更のみを出力したDiffファイルを毎回用意するようにしたいと思います。
おそらくこの方がソースコードの変更内容を確認するためには便利かと思います。
前回からの修正内容 (changes090829.zip)

ちなみに、この修正内容のファイルは開発用UbuntuのPCにローカルに置いているSubversionリポジトリで「svn diff」コマンドを使用して出力しています。
ソースファイル全体のパッチについては日本語が入ると紛らわしかったりする関係で、Linuxのdiffコマンドを使っています。
私がSubversionを使い始めたのはこのプロジェクトを開始した後ということもあって、まだ十分に使いこなせているとは言い難いのですが、このツールのおかげでQtバージョンとAGGバージョンを平行して更新し続けることができたりして色々と助かっています。


ここから修正内容の解説をしたいと思いますが、C言語のソースコードを読み慣れている方の場合、svn diffコマンドの出力を見るだけで十分というか、その方がわかりやすいかもしれません。

スカイテクスチャ、ランプテクスチャ、ヘイローテクスチャの処理は、

blender/source/blender/render/intern/source/texture.c
にある、do_sky_tex()、do_lamp_tex()、do_halo_tex()の中と、

blender/source/blender/render/intern/source/pixelshading.c
の中にあるいくつかの関数の中に書かれています。

このうち、vectexに関わる部分は解像度の計算に使用するdxt、dytの値を得るというものです。
まず、スカイテクスチャから見てみます。

dxt、dytの内容については、do_sky_tex()の中の始めの方で、
            /* dxt dyt just from 1 value */
            if(dxyview) {
                dxt[0]= dxt[1]= dxt[2]= dxyview[0];
                dyt[0]= dyt[1]= dyt[2]= dxyview[1];
            }
            else {
                dxt[0]= dxt[1]= dxt[2]= 0.0;
                dyt[0]= dyt[1]= dyt[2]= 0.0;
            }
というように記述されているだけです。
この関数が呼ばれる前に、別の関数のなかでdxyview[]という配列にあらかじめ計算済みの値が渡されています。

しかし、dxt[0]、dxt[1]、dxt[2]をすべて同じ値にしてしまうのは、ちょっと手抜きなような気がします。
これでは、vectexの中の解像度の計算で正しい値を計算できません。

ということで、この部分を次の様に修正しています。
            /* dxt dyt just from 1 value */
            if (mtex->tex->type==TEX_VECTEX) {
                if(dxyview) {
                    dxt[0]= dxyview[0];
                    dyt[1]= dxyview[1];
                    if(R.wrld.skytype & WO_SKYPAPER) {
                        dxt[0]= dxyview[0] * 2.0;
                        dyt[1]= dxyview[1] * 2.0;
                    }
                    dxt[1] = dxt[2] = dyt[0] = dyt[2] = 0.0;
                }
                else {
                    dxt[0]= dxt[1]= dxt[2]= 0.0;
                    dyt[0]= dyt[1]= dyt[2]= 0.0;
                }
            }

            else {
                if(dxyview) {
                    dxt[0]= dxt[1]= dxt[2]= dxyview[0];
                    dyt[0]= dyt[1]= dyt[2]= dxyview[1];
                }
                else {
                    dxt[0]= dxt[1]= dxt[2]= 0.0;
                    dyt[0]= dyt[1]= dyt[2]= 0.0;
                }
            }
スカイタイプにPaperが選択されているとき、引数に格納されてくる値はそれ以外のときと違った値が渡されますが、実際に数値を確認してみたところ、本来渡されるはずの値の半分になっていたので2倍にしています。

スカイテクスチャの設定を行うパネルには「View」「Global」「AngMap」「Sphere」「Tube」「Object」の6つの座標系が選べるようになっています。
上の変更だけで対応できるのは、このうちの「View」「Global」だけで、後の4つの座標系が選ばれた場合にはそれぞれに対応した計算処理を自力で作成する必要があるようです。

そのためには、そもそもdxt、dytという値が何なのかを完全に理解していないと無理だと思うのですが、幸いなことに前回の修正のときにとても参考になるコードを見つけることができました。

前回、マテリアルテクスチャの座標系で「Refl」を選択したときにvectexの処理中にBlenderが異常終了してしまう問題に対応していますが、その際に
blender/source/blender/render/intern/source/shadeoutput.c (line:809-)
void calc_R_ref(ShadeInput *shi)
という関数の中でdxt、dytの値を計算しているコードがあることを知りました。

そのコードはこのような内容です。
void calc_R_ref(ShadeInput *shi)
{
    float i;

    /* shi->vn dot shi->view */
    i= -2*(shi->vn[0]*shi->view[0]+shi->vn[1]*shi->view[1]+shi->vn[2]*shi->view[2]);

    shi->ref[0]= (shi->view[0]+i*shi->vn[0]);
    shi->ref[1]= (shi->view[1]+i*shi->vn[1]);
    shi->ref[2]= (shi->view[2]+i*shi->vn[2]);
    if(shi->osatex) {
        if(shi->vlr->flag & R_SMOOTH) {
            i= -2*( (shi->vn[0]+shi->dxno[0])*(shi->view[0]+shi->dxview) +
                (shi->vn[1]+shi->dxno[1])*shi->view[1]+ (shi->vn[2]+shi->dxno[2])*shi->view[2] );

            shi->dxref[0]= shi->ref[0]- ( shi->view[0]+shi->dxview+i*(shi->vn[0]+shi->dxno[0]));
            shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*(shi->vn[1]+shi->dxno[1]));
            shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dxno[2]));

            i= -2*( (shi->vn[0]+shi->dyno[0])*shi->view[0]+
                (shi->vn[1]+shi->dyno[1])*(shi->view[1]+shi->dyview)+ (shi->vn[2]+shi->dyno[2])*shi->view[2] );

            shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*(shi->vn[0]+shi->dyno[0]));
            shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*(shi->vn[1]+shi->dyno[1]));
            shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*(shi->vn[2]+shi->dyno[2]));
        }
        else {

            i= -2*( shi->vn[0]*(shi->view[0]+shi->dxview) +
                shi->vn[1]*shi->view[1]+ shi->vn[2]*shi->view[2] );

            shi->dxref[0]= shi->ref[0]- (shi->view[0]+shi->dxview+i*shi->vn[0]);
            shi->dxref[1]= shi->ref[1]- (shi->view[1]+ i*shi->vn[1]);
            shi->dxref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);

            i= -2*( shi->vn[0]*shi->view[0]+
                shi->vn[1]*(shi->view[1]+shi->dyview)+ shi->vn[2]*shi->view[2] );

            shi->dyref[0]= shi->ref[0]- (shi->view[0]+ i*shi->vn[0]);
            shi->dyref[1]= shi->ref[1]- (shi->view[1]+shi->dyview+i*shi->vn[1]);
            shi->dyref[2]= shi->ref[2]- (shi->view[2]+ i*shi->vn[2]);
        }
    }
}
始めの部分でshi->ref[]には、vectex()関数に渡されるテクスチャの座標値(texvecに相当する値)をビュー座標から計算した値が入力されています。
そして、その後の部分では基本的には始めの部分のテクスチャ座標の計算とかなり似ていますが、shi->dxrefにはビュー座標のX成分のみを増加した時のテクスチャ座標値を計算し、その値を始めに計算したshi->ref[]から引いた値を格納しています。
shi->dyrefには、ビュー座標のY成分のみを増加して同じことを行っています。
shi->dxrefがdxt、shi->dyrefがdytに対応しています。

これは、レンダリングされる画像のピクセルの位置とそこで使用されるテクスチャの画像のピクセルの位置との対応で考えるなら、レンダリング画像の1ピクセル横の位置に対応するテクスチャ画像の座標のX成分がdxt[0]、Y成分がdxt[1]となり、レンダリング画像の1ピクセル上か下の位置に対応するテクスチャ画像のX成分がdyt[0]、Y成分がdyt[1]となります。

ということで、do_sky_tex()関数の中でも同じように処理を行おうとしたのですが、なぜか計算値が正しくならないようです。
色々と調べた結果、この関数に引数として渡されるビュー座標loの値が、この関数に渡される前に変更されているのが問題の原因だと分かりました。

loの値はdo_sky_tex()関数が呼ばれる直前に、shadeSkyView()関数の中で行列演算されています。
テクスチャ座標のX成分、Y成分をそれぞれ増加させる処理は、その行列演算を行う前にする必要があるようです。

そのため、ちょっと危険な感じもするのですが、その計算処理を元の関数shadeSkyView()から削除してdo_sky_tex()の中に移動しました。
shdeSkyView()関数の該当箇所のすぐ次の行でdo_sky_tex()関数が呼ばれているので処理的に問題は起こらないはずですが、できれば勝手にこのような変更はしたくないところです。
(修正を行った後で気づいたのですが、テクスチャ座標に加える増分の方に同じ行列演算を行ってしまうことでも問題が解決できそうな気がします。do_lamp_tex()関数の中ではそうなっています。あとで修正し直すかもしれません。)

blender/source/blender/render/intern/source/pixelshading.c (line:514-)
void shadeSkyView(float *colf, float *rco, float *view, float *dxyview, short thread)
{
...
    if(R.wrld.skytype & WO_SKYTEX) {
        VECCOPY(lo, view);
                       
        /*if(R.wrld.skytype & WO_SKYREAL) {
            MTC_Mat3MulVecfl(R.imat, lo);
           
            SWAP(float, lo[1],  lo[2]);
           
        }*/
        do_sky_tex(rco, lo, dxyview, hor, zen, &blend, skyflag, thread);
    }
...
}
blender/source/blender/render/intern/source/texture.c (line:3052)
void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, float *blend, int skyflag, short thread)
{
...
    float tempvec2[3],lo1[3], lo2[3];
   
    if (R.r.scemode & R_NO_TEX) return;
    /* todo: add flag to test if there's a tex */
    texres.nor= NULL;
   
    lo1[0] = lo[0];
    lo1[1] = lo[1];
    lo1[2] = lo[2];
    if(R.wrld.skytype & WO_SKYREAL) {
        MTC_Mat3MulVecfl(R.imat, lo);
       
        SWAP(float, lo[1],  lo[2]);
       
    }

...
}
このようにlo1という配列を作って行列演算の前の値を保存しておき、それを使ってビュー座標をX、Y方向に増加させた値でテクスチャ座標を計算し、ソースコードに元々ある計算で得られる値からその値を引いて、dxt、dytの値を計算します。

まずは、AngMapの場合です。
            /* Grab the mapping settings for this texture */
            switch(mtex->texco) {
            case TEXCO_ANGMAP:
                /* only works with texture being "real" */
                fact= (1.0/M_PI)*acos(lo[2])/(sqrt(lo[0]*lo[0] + lo[1]*lo[1]));
                tempvec[0]= lo[0]*fact;
                tempvec[1]= lo[1]*fact;
                tempvec[2]= 0.0;
                co= tempvec;
                /*if(G.f & G_DEBUG) printf("  co[0]%f   co[1]%f\n", co[0], co[1]);*/   
               
                if ((mtex->tex->type==TEX_VECTEX) && (dxyview)) {
                    lo2[0] = lo1[0] + dxyview[0];
                    lo2[1] = lo1[1];
                    lo2[2] = lo1[2];
                    if(R.wrld.skytype & WO_SKYREAL) {
                        MTC_Mat3MulVecfl(R.imat, lo2);
                        SWAP(float, lo2[1],  lo2[2]);
                    }
                    fact= (1.0/M_PI)*acos(lo2[2])/(sqrt(lo2[0]*lo2[0] + lo2[1]*lo2[1]));
                    tempvec2[0]= lo2[0]*fact;
                    tempvec2[1]= lo2[1]*fact;
                    dxt[0] = tempvec[0] - tempvec2[0];
                    dxt[1] = tempvec[1] - tempvec2[1];
                   
                    lo2[0] = lo1[0];
                    lo2[1] = lo1[1] + dxyview[1];
                    lo2[2] = lo1[2];
                    if(R.wrld.skytype & WO_SKYREAL) {
                        MTC_Mat3MulVecfl(R.imat, lo2);
                        SWAP(float, lo2[1],  lo2[2]);
                    }
                    fact= (1.0/M_PI)*acos(lo2[2])/(sqrt(lo2[0]*lo2[0] + lo2[1]*lo2[1]));
                    tempvec2[0]= lo2[0]*fact;
                    tempvec2[1]= lo2[1]*fact;
                    dyt[0] = tempvec[0] - tempvec2[0];
                    dyt[1] = tempvec[1] - tempvec2[1];
                  
                    if (R.wrld.skytype & WO_SKYREAL) {
                    }
                    else {
                        fact = 10;
                        dxt[0]*=fact;
                        dxt[1]*=fact;
                        dyt[0]*=fact;
                        dyt[1]*=fact;
                    }   
                }
                break;

最後の部分は、スカイタイプの設定がRealになっていない場合、AngMapの計算そのものが正しく行われないらしく、vectexの解像度計算で必要以上に高い解像度でテクスチャが作成されるのを防ぐために適当にdxt、dytの値を補正しています。

ShpereとTubeはまとめて計算されています。
追加した処理は、AngMapとほとんど似たような感じです。
            case TEXCO_H_SPHEREMAP:
            case TEXCO_H_TUBEMAP:
                if(skyflag & WO_ZENUP) {
                    if(mtex->texco==TEXCO_H_TUBEMAP) tubemap(lo[0], lo[2], lo[1], tempvec, tempvec+1);
                    else spheremap(lo[0], lo[2], lo[1], tempvec, tempvec+1);
                    /* tube/spheremap maps for outside view, not inside */
                    tempvec[0]= 1.0-tempvec[0];
                    /* only top half */
                    tempvec[1]= 2.0*tempvec[1]-1.0;
                    tempvec[2]= 0.0;
                    /* and correction for do_2d_mapping */
                    tempvec[0]= 2.0*tempvec[0]-1.0;
                    tempvec[1]= 2.0*tempvec[1]-1.0;
                    co= tempvec;
                   
                    if ((mtex->tex->type==TEX_VECTEX) && (dxyview)) {
                        lo2[0] = lo1[0] + dxyview[0];
                        lo2[1] = lo1[1];
                        lo2[2] = lo1[2];
                        if(R.wrld.skytype & WO_SKYREAL) {
                            MTC_Mat3MulVecfl(R.imat, lo2);
                            SWAP(float, lo2[1],  lo2[2]);
                        }
                        if(mtex->texco==TEXCO_H_TUBEMAP) tubemap(lo2[0], lo2[2], lo2[1], &tempvec2[0], &tempvec2[1]);
                        else spheremap(lo2[0], lo2[2], lo2[1], &tempvec2[0], &tempvec2[1]);
                       
                        /* tube/spheremap maps for outside view, not inside */
                        tempvec2[0]= 1.0-tempvec2[0];
                        /* only top half */
                        tempvec2[1]= 2.0*tempvec2[1]-1.0;
                        tempvec2[2]= 0.0;
                        /* and correction for do_2d_mapping */
                        tempvec2[0]= 2.0*tempvec2[0]-1.0;
                        tempvec2[1]= 2.0*tempvec2[1]-1.0;
                       
                        dxt[0] = tempvec[0] - tempvec2[0];
                        dxt[1] = tempvec[1] - tempvec2[1];
                       
                        lo2[0] = lo1[0];
                        lo2[1] = lo1[1] + dxyview[1];
                        lo2[2] = lo1[2];
                        if(R.wrld.skytype & WO_SKYREAL) {
                            MTC_Mat3MulVecfl(R.imat, lo2);
                            SWAP(float, lo2[1],  lo2[2]);
                        }
                        if(mtex->texco==TEXCO_H_TUBEMAP) tubemap(lo2[0], lo2[2], lo2[1], &tempvec2[0], &tempvec2[1]);
                        else spheremap(lo2[0], lo2[2], lo2[1], &tempvec2[0], &tempvec2[1]);
                       
                        /* tube/spheremap maps for outside view, not inside */
                        tempvec2[0]= 1.0-tempvec2[0];
                        /* only top half */
                        tempvec2[1]= 2.0*tempvec2[1]-1.0;
                        tempvec2[2]= 0.0;
                        /* and correction for do_2d_mapping */
                        tempvec2[0]= 2.0*tempvec2[0]-1.0;
                        tempvec2[1]= 2.0*tempvec2[1]-1.0;
                       
                        dyt[0] = tempvec[0] - tempvec2[0];
                        dyt[1] = tempvec[1] - tempvec2[1];
                    }

                }
                else {
                    /* potentially dangerous... check with multitex! */
                    continue;
                }
                break;

次は、Objectの場合です。
こちらも、それほど違いはありません。
            case TEXCO_OBJECT:
                if(mtex->object) {
                    VECCOPY(tempvec, lo);
                    MTC_Mat4MulVecfl(mtex->object->imat, tempvec);
                    co= tempvec;
                   
                    if ((mtex->tex->type==TEX_VECTEX) && (dxyview)) {
                        lo2[0] = lo1[0] + dxyview[0];
                        lo2[1] = lo1[1];
                        lo2[2] = lo1[2];
                        if(R.wrld.skytype & WO_SKYREAL) {
                            MTC_Mat3MulVecfl(R.imat, lo2);
                            SWAP(float, lo2[1],  lo2[2]);
                        }
                        VECCOPY(tempvec2, lo2);
                        MTC_Mat4MulVecfl(mtex->object->imat, tempvec2);
                        dxt[0] = tempvec[0] - tempvec2[0];
                        dxt[1] = tempvec[1] - tempvec2[1];
                       
                        lo2[0] = lo1[0];
                        lo2[1] = lo1[1] + dxyview[1];
                        lo2[2] = lo1[2];
                        if(R.wrld.skytype & WO_SKYREAL) {
                            MTC_Mat3MulVecfl(R.imat, lo2);
                            SWAP(float, lo2[1],  lo2[2]);
                        }
                        VECCOPY(tempvec2, lo2);
                        MTC_Mat4MulVecfl(mtex->object->imat, tempvec2);
                        dyt[0] = tempvec[0] - tempvec2[0];
                        dyt[1] = tempvec[1] - tempvec2[1];
                    }

                }
                break;

次に、ヘイローテクスチャについてです。
こちらは、スカイテクスチャのように複数の座標系が選べるようにはなっていないので、修正内容はもっと少なくなっています。
void do_halo_tex(HaloRen *har, float xn, float yn, float *colf)
{
...
        if (mtex->tex->type==TEX_VECTEX) {
            if(mtex->projx) {
                dxt[0]= mtex->size[0]*dx;
                dyt[0]= 0.0;
            }
            else dxt[0]= dyt[0]= 0.0;
           
            if(mtex->projy) {
                dxt[1]= 0.0;
                dyt[1]= mtex->size[1]*dx;
            }
            else dxt[1]= dyt[1]= 0.0;
        }
        else {
            if(mtex->projx) {
                dxt[0]= mtex->size[0]*dx;
                dyt[0]= mtex->size[0]*dx;
            }
            else dxt[0]= dyt[0]= 0.0;
           
            if(mtex->projy) {
                dxt[1]= mtex->size[1]*dx;
                dyt[1]= mtex->size[1]*dx;
            }
            else dxt[1]= dyt[1]= 0.0;
        }
...
}

スカイテクスチャの処理の最初の部分で行ったのと似たようなことをしています。
元の処理でdxt[0]とdxt[1]、dyt[0]とdyt[1]が同じ値にされていたのを、dxt[1]、dyt[0]をそれぞれ0.0に変更することでvectexでの解像度計算が正しく行われるようにしています。

次にランプテクスチャです。
do_lamp_tex()関数では、スカイテクスチャやヘイローテクスチャとは違ってきちんとdxt、dytが計算されています。
しかし、なぜかvectexの計算で正しい解像度が計算されません。
調べてみると、dxt、dytの値を計算する部分のif文の条件式に(shi->osatex)という記述があり、この値がイメージテクスチャの場合と、そうでない場合とで違っているようです。
ということで、その値を設定している部分をvectexのときにも有効になるように変更することにします。

blender/source/blender/render/intern/source/convertblender.c (line:3326-)
static GroupObject *add_render_lamp(Render *re, Object *ob)
{
...
            if(G.rendering) {
                if(re->osa) {
                    if((la->mtex[c]->tex->type==TEX_IMAGE) || (la->mtex[c]->tex->type==TEX_VECTEX)) lar->mode |= LA_OSATEX;
                }
            }
...
}

この修正で、ランプテクスチャでもvectexが使用できるようになりました。
posted by mato at 00:46| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

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


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

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