2009年04月26日

Blender Partyに参加しました

昨日25日に東京の神保町で行われたBlender Party @Tokyo,Japan 2009に参加させていただきました。
当日は天気があいにくの雨で、強風などの注意も必要という天気予報でしたので、電車が止まったりしないか不安でしたが、無事に開始時間に会場に到着できました。

私はどちらかというとコミュニケーション能力があまり高い方ではなくて、初対面の方とお話したりするのは得意ではないのですが、yamyamさん、真猿さん、藤堂+さんなどと同席させていただいて、色々とお話を聞くことができました。
私はまだBlender関連のサイトを十分に把握できていなくて、ハンドルネームを聞いてもその方がどのようなサイトを運営されているのか、どのような作品を作られているのかが分からなかったりして、少々とまどってしまいました
。事前にもう少し調べておけばよかったかなと反省しています。

イベントの方は、上映会、実演共にとても楽しめました。実演をされたトニーさん、ekakiyaさん、shigeさん、大変勉強になりました。ありがとうございました。

そして、前回の記事に書いたようにVFXコンテストに私も応募させていただいていたのですが、なんと審査員特別賞の3番目に選んでいただきました。少しでもイベントを盛り上げることに役に立てたら...というつもりで作ったのに、賞品をいただいてしまって、なんだかとても申し訳ない気持ちです。(賞品はほとんどが関係者の方の自腹とのことです。)投票していただいた皆様、賞品を用意していただいた皆様、本当にありがとうございました。
ちなみに1番となったのは、FDTKさんの「BLUE HERO & Making」、2番はnaokiさんの「Polt」 という作品です。おめでとうございます。

当日の様子をライブ中継した動画がこちらで見られるということです。私も少し拝見させていただきながら、この記事を書いています。

オフ会というものに参加したのは今回が初めてなのですが、Blenderという共通する言語のある人と直接会うことができ、とても貴重な体験をさせていただくことができました。このような会を開催して下さった皆さんに、とても感謝しています。幹事をされたまんださん、グリマーさん、daywalkerさん、ありがとうございました。

posted by mato at 21:26| Comment(2) | Blender | このブログの読者になる | 更新情報をチェックする

2009年04月21日

BPTJ2009のVFXコンテスト

4月25日(土)18:00から、Blender Party @Tokyo,Japan 2009 が東京の神保町 ダイニングカフェ・エスペリアで開催されます。私も参加させていただく予定です。Blender Patyについての詳細はこちらをご参照ください。

これに合わせて行われているVFXコンテストに私も応募させていただきました。コンテストにエントリーされている全ての動画は、こちらで確認できます。

私が応募した動画はこちらになります。
動画の中で使用しているのは、Poser用フィギュアとして公開している「くるる」をBlender用に移植している途中段階のものです。まだ、色々と設定が詰まっていないので正式に公開していませんが、このブログでも以前サンプルデータとして使ったりしています。

作成した動画は16秒と短めです。内容は、机の上でフィギュアになりきっているつもりのくるるさんが、カメラに見つかって焦っているというシーンを作りました。
pic090421_16.jpg

ちなみにくるるさんのまわりにある本物のフィギュアですが、こちらはスペインのリヤドロという陶器のフィギュアです。ちょっと値段が高めですが、デパートの洋食器などの贈答品の売り場に置かれていることがあります。もし実物を見ていただけたら、その造型の素晴らしさに驚かれるのではないかと思います。機会があったら、探してみてください。

ということで、今回は少しプログラミングのことから離れて、応募した動画のことを書いてみようと思います。
このコンテストのテーマとなっているのが、実写映像と3DCGの合成です。私はカメラトラッキング(マッチムーブ)ソフトというものを初めて使用しました。カメラトラッキングのために使用したのはVoodooというソフトウェアです。
pic090421_01.jpg pic090421_04.jpg

カメラトラッキング自体が今回初めて体験するものだったのですが、少しだけ使ってみた範囲では、なかなか思い通りのカメラ設定を得るのは難しいように感じました。使用する素材の動画の内容によって状況がかなり変わってくると思いますが、
  1. エンコードによって画質が落ちている場合、得られるカメラデータの精度も落ちる。
  2. 短い時間ならきちんとしたカメラデータが得られる動画でも、ある程度以上の長さをまとめて解析しようとするとうまくいかない。
というようなことを、実際に作業した中で体験しました。

一度解析が終わった後で、さらにデータを修正することでカメラデータの精度を向上させることもできるようですが、今回はそこまでする時間の余裕がありませんでした。机の上を撮影した動画はいくつか試した中では比較的良好なカメラのデータが得られました。上の画像に映っている猫の動画では、映像とカメラの動きが今ひとつ一致しませんでした。


カメラトラッキングを使っていることというのが今回の動画で一番の重要項目ですが、それ以外にも何点か工夫している部分がありますので、ここからはその辺りについて書いてみたいと思います。

まず、キャラクターの髪、衣服にはクロスシミュレーションを使用していますが、メッシュの形状自体をシミュレーションするのではなく、より少ない頂点数のメッシュでシミュレーションを行い、実際の形状の変形はそれらのシミュレーション用のメッシュに追従させたアーマチュアによって行っています。
pic090421_15.jpg

このように実際にレンダリングするメッシュとシミュレーションのメッシュを分離するという方式は、そのままシミュレーションするよりもセットアップに手間はかかりますが、一度設定を完了してしまえば色々と応用ができる可能性があります。
  1. IPOカーブにベイクすることで自由に編集できる。
  2. 同じシミュレーション設定で全く別の形状を動かすことができる。
今回、衣服のシミュレーションを普通に行うとキャラクターがくしゃみをする動作の反動で激しく変形しすぎてしまい、パラメータの調整だけでは制御が難しい感じになりました。これに対して、シミュレーション用のメッシュで裾の部分を本来よりもかなり長めに伸ばすことで、変形を抑えるようにしています。
pic090421_06.jpg pic090421_07.jpg

髪についてはシミュレーション結果をそのまま使用していますが、服の方はキャラクターのボディーが貫通する部分がかなりあったため、シミュレーションした結果をIPOカーブで編集できるようにベイクし、手動でキーフレームの調整を行っています。

以前、Vimeoにアップした動画では一定の間隔でキーフレームを間引くようにして、残したキーフレームを調整するというような方法を行いました。
しかし、今回の動画では瞬間的にかなり大きな変動が起こるため、キーフレームを間引くことはせず、全フレームにキーフレームを打った状態のままで使用しています。この場合、問題の起こっている部分のキーフレームを全て手作業で修正するというのは困難なため、
  1. まず、ボディーが服を貫通しているフレーム位置を調べ、特定のボーンについてある程度の範囲にわたってキーフレームを削除する。
  2. ボディーが服を貫通しないようにボーンの回転を調整し、適当な位置にキーフレームを打つ。
というような形で修正しました。
pic090421_08.jpg

この辺りについては、NLAエディタを使用すれば変形を修正するためだけのアクションを作成することもできますし、もっとうまく修正する方法があるかもしれません。


そして、もう一つ工夫した点を上げるとすると、髪のレンダリングの部分でアンチエイリアスがうまく利かなかったのを修正していることです。これについては、もしかすると何かのスイッチで解消できるのを私が知らないだけだったのかもしれませんが、「Render」パネルのOSAの値を変えたり、マテリアルの「Links and Pipeline」パネルでFull Osaを使用してもうまく回避できませんでした。

下の画像を見ていただくと、影の暗い部分の形がフレームごとに全く違う位置、形状になっているのがわかると思います。動画として再生する場合、この部分はちらちらと明滅するように表示され、せっかくカメラトラッキングで実写映像と動きを合わせていても、映像的にとても違和感のあるものになってしまいます。
pic090421_09.jpg

これに対する解決策としては、一旦倍の解像度でレンダリングしたものをもとの解像度に縮小する、ということでかなり改善できました。
pic090421_10.jpg

ただし、画像全体に対してこれを行ってみたところ、髪以外の部分では元のレンダリングにくらべて画像がぶれているような感じになってしまい、これはこれでちょっと問題があるという状況になりました。最終的には、髪だけを部分レンダリングしたものをアニメーション出力し、元の画像と2倍にレンダリングしたものを合成しています。
pic090421_11.jpg pic090421_12.jpg

pic090421_13.jpg pic090421_14.jpg

本来ならば、髪の部分だけを2倍にレンダリング、縮小を行った画像をオリジナルに合成すればいいのですが、今回は試しに全体を2倍サイズでレンダリングしてみたものが既に作成済みだったため、上のNode Editorの画面では髪の部分レンダリングはアルファ値のみを使用しています。
posted by mato at 01:16| Comment(2) | Blender | このブログの読者になる | 更新情報をチェックする

2009年04月13日

vectexをマルチスレッドレンダリング対応にする(2)

前回、Blenderのマルチスレッドレンダリングで排他制御を行う関数をテクスチャプラグインから使用できるようにしました。Blenderの内部ではBLI_lock_thread()、BLI_unlock_thread()という関数名になっていましたが、実際にエクスポートしている関数はlock_thread()、unlock_thread()という名前にしてあります。あとは、vectexのソースコードの中から、マルチスレッドレンダリング時に問題が起こっている部分を探し出して、その部分をエクスポートした関数で挟み込むだけです。

と、言葉で書くと簡単そうなのですが、このプログラムがどんな風に動作しているのか全然わからない状態では問題のある場所を探すのは難しそうです。それほど行数が多いわけではないのですが、vectexの内部で重要な役割を果たしているミップマップの処理は、レンダリング時のカメラの設定次第で(描画されるポリゴンの面積がどのような状態になるかによって)挙動が変化するので、ソースコードを眺めるだけで処理の流れを追うのは難しそうです。

ありがたいことに、vectexのソースコードには開発時に使われたデバッグ用のコードがそのまま残っています。コンパイル時にMakefileの一部を修正すると、ソースコードに埋め込まれたマクロが有効になって、Blenderを起動させたコンソールにデバッグ用の文字列を出力させることができます。

INC=-fPIC -shared -O -ansi -Iblender -Iagg-2.5/include -Iexpat-1.95.8/lib
# -DDEBUG

この部分で「#」を削除して、「-DDEBUG」をINCの定義のオプションに追加します。

INC=-fPIC -shared -O -ansi -Iblender -Iagg-2.5/include -Iexpat-1.95.8/lib -DDEBUG

pic090413_01.jpg pic090413_02.jpg

「DEBUG」定数が設定されるとvectex.hに書かれたマクロが有効になり、vectex.c、vectex_agg.cppの各関数内に埋め込まれたマクロがprintf()関数に展開されます。
pic090413_03.jpg
#ifdef DEBUG
#define MSG0(F)         printf(F);
#define MSG1(F,A)       printf(F, (A));
#define MSG2(F,A,B)     printf(F, (A), (B));
#define MSG3(F,A,B,C)   printf(F, (A), (B), (C));
#define MSG4(F,A,B,C,D) printf(F, (A), (B), (C), (D));
#else
#define MSG0(F)
#define MSG1(F,A)
#define MSG2(F,A,B)
#define MSG3(F,A,B,C)
#define MSG4(F,A,B,C,D)

#endif
例えば、vectex.cの中のcreate_tile_set()という関数の中には、2つのMSGマクロが埋め込まれています。
pic090413_13.jpg
...
MSG0("[create_tile_set]\n");
...   
MSG2("- created tile set max_pow=%d size=%d\n", instance->tile_set_max_power, size);
...
この関数create_tile_set()が実行されると、コンソール画面に「create_tile_set」「-created tile set max_pow= xxx size= xxx」というように表示されることになります。逆に言えば、コンソール画面に表示される内容から、ソースコードのどの部分が実行されたのかがわかるということになります。

ということで、早速、DEBUGモードでコンパイルされたvectexを使用して、vectexに付属のサンプルtest-static.blendでのレンダリングの過程を調べてみたいと思います。
Blenderを起動し、test-static.blendを開いた直後のコンソール画面です。
pic090413_04.jpg
[plugin_tex_getversion]
[plugin_getinfo]
[plugin_init]
[plugin_instance_init]
- plugin started enabled
[create_instance]
[create_backend]
- open ok
[create_tile_set]
- created tile set max_pow=8 size=9
> creating tile #8 res=256 [0,0]


スレッド数を1にしてレンダリングしてみます。
pic090413_05.jpg
- growing tile set max_pow=10 size=29 old_size=9
> creating tile #26 res=1024 [3,1]
> creating tile #11 res=512 [1,0]
> creating tile #27 res=1024 [3,2]
> creating tile #12 res=512 [1,1]
> creating tile #9 res=512 [0,0]
> creating tile #7 res=128 [0,0]
> creating tile #28 res=1024 [3,3]
> creating tile #13 res=1024 [0,0]
> creating tile #6 res=64 [0,0]
> creating tile #5 res=32 [0,0]
> creating tile #4 res=16 [0,0]
> creating tile #3 res=8 [0,0]
> creating tile #22 res=1024 [2,1]
> creating tile #23 res=1024 [2,2]
> creating tile #18 res=1024 [1,1]
> creating tile #17 res=1024 [1,0]
> creating tile #25 res=1024 [3,0]
> creating tile #21 res=1024 [2,0]
> creating tile #1 res=2 [0,0]
> creating tile #0 res=1 [0,0]
> creating tile #2 res=4 [0,0]
- growing tile set max_pow=11 size=93 old_size=29
> creating tile #70 res=2048 [5,1]
> creating tile #78 res=2048 [6,1]

1枚の画像をレンダリングするだけで、かなりたくさんのテクスチャが生成されているようです。

今度は、test-static.blendを読み込んでスレッド数4でレンダリングします。レンダリングが途中でフリーズした状態のコンソール画面へのデバッグ出力です。
pic090413_06.jpg
[plugin_tex_getversion]
[plugin_getinfo]
[plugin_init]
[plugin_instance_init]
- plugin started enabled
[create_instance]
[create_backend]
- open ok
[create_tile_set]
- created tile set max_pow=8 size=9
> creating tile #8 res=256 [0,0]
- growing tile set max_pow=10 size=29 old_size=9
> creating tile #27 res=1024 [3,2]
> creating tile #26 res=1024 [3,1]
> creating tile #11 res=512 [1,0]
> creating tile #7 res=128 [0,0]
何回かレンダリングすると、その時々で出力される内容は変わりますが、「creating tile ...」というようにデバッグ出力している関数、あるいはそこから呼ばれている関数を実行している途中でレンダリングがフリーズしているようです。

「creating tile」という文字列が出力されるMSGマクロは一つだけで、449行目のget_tile()という関数の中です。get_tile()関数の中を見てみると、memory_alloc()、memcpy()、memory_free()などのメモリ処理に関わる関数がたくさん使われています。
pic090413_07.jpg pic090413_08.jpg

この関数の中のどこかで問題が起こっていそうなのでそれを調べてみてもいいのですが、とりあえずはこの関数を呼び出している部分を探して、get_tile()関数の呼び出し部分をlock_thread()、unlock_thread()で囲んでみたいと思います。検索してみると、get_tile()関数を呼び出しているのは507行目と547行目の2ヶ所だけで、sample()という関数の中です。
pic090413_09.jpg pic090413_11.jpg
...
lock_thread(LOCK_IMAGE);
int index = get_tile(instance, x, y, power);
unlock_thread(LOCK_IMAGE);
...
このようにlock_thread()、unlock_thread()関数、LOCK_IMAGE定数を使うには、これらのプロトタイプ宣言が書かれているutil.hをインクルードする必要があります。
...
#include "plugin.h"
#include "util.h"

#include "vectex.h"
...
vectex.cを以上のように修正し、vectexのソースコードのblenderフォルダ内にあるutil.hを前回作成したBlender修正版のものに置き換えれば、コンパイルの準備が完了です。コンパイルしてできたvectex.soファイルをBlender修正版で読み込んでtest-static.blendをレンダリングしてみると、スレッド数4でも問題なくレンダリングが完了しました。
pic090413_12.jpg

lock_thread()、unlock_thread()関数を使用する場所については、get_tile()関数の中をもっと調べて本当に問題の起こっている部分だけを囲むようにした方がいいのかもしれませんが、動作に問題がなければこのままでもよさそうです。

こちらに今回の修正版vectexを置いておきます。

vectex_modified2.zip

このvectexは前回作成した修正版Blender専用となります。通常のBlenderからは使用できませんのでご注意ください。
posted by mato at 22:03| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする

2009年04月09日

スレッドロック関数のエクスポート

前回、Blenderのマルチスレッドレンダリングで排他制御をするために必要な関数を探しました。
Blenderのソースコードのディレクトリ中の「source/blender/blenlib/intern/threads.c」にあるBLI_lock_thread()、BLI_unlock_thread()という2つの関数です。今回は、これらの関数をBlenderの外部のプログラム、テクスチャプラグインで使用できるようにエクスポートしてみたいと思います。

しかし、このような変更を加えるというのは、勝手にプラグインの仕様を変えてしまうようなものですが大丈夫なのでしょうか。結果から言うと全然大丈夫ではなくて、このような変更を加えたBlender本体とそれに合わせて作成されたテクスチャプラグインは、その組み合わせでのみ正しく動作します。修正版のBlenderで普通のテクスチャプラグインを使うことはできると思いますが、修正版Blender用のテクスチャプラグインを通常のBlenderから使用することはできません。
これについては、最終的にはvectexの機能をBlenderの本体の中に組み込むというようなことも考えてはいますが、とりあえずはこの方法でvectexをマルチスレッドレンダリング対応で使用できるようにしたいと思います。

Blenderのソースコードの中でテクスチャプラグインに関連する部分は、「/source/blender/blenpluginapi」というディレクトリの中に入っています。
pic090408_01.jpg pic090408_02.jpg

ここには、Blenderの実行ファイルのある場所に置かれている「plugins/include」ディレクトリの中にあるヘッダファイルと同じものがあります。その他に、「intern」というディレクトリがあって、そこにはpluginapi.cというファイルが入っています。
pic090408_03.jpg

pluginapi.cには、plugin.h、util.h、iff.hなどでエクスポートされている関数の本体が書かれています。スレッドをロックするための関数をエクスポートするためには、このファイルの適当な場所にエクスポートする関数を追加すればいいようです。他のエクスポートされている関数の記述をみると、実際にエクスポートされている関数名はBlenderの内部で使用している関数名とは微妙に違った名前に変更されているようです。今回は、lock_thread()、unlock_thread()というようにしてみました。Blenderの他の場所で同じ名前の関数が使われていないことを確認してあります。
pic090408_05.jpg
LIBEXPORT void lock_thread (int type)
{
    BLI_lock_thread(type);
}

LIBEXPORT void unlock_thread (int type)
{
    BLI_unlock_thread(type);
}
こんな風に関数の中でBLI_lock_thread()関数を使用するためには、この関数のプロトタイプ宣言の書かれているBLI_threads.hをインクルードする必要があるのですが、今回はたまたま他の部分でスレッドに関連する機能が使われていたらしく、すでにインクルードされていました。
pic090408_04.jpg

この他に、pluginapi.cというファイルの最後には、エクスポートされている全ての関数名が記述されているpluginapi_force_ref()という関数がありますので、この部分にも修正を加えます。
pic090408_10.jpg
        ...
        GET_INT_FROM_POINTER( gamwarp ) +
        GET_INT_FROM_POINTER( lock_thread ) +
        GET_INT_FROM_POINTER( unlock_thread );
}

pluginapi.cに行う修正はここまでです。
次に、ヘッダファイルにプロトタイプ宣言を追加します。エクスポートする関数の内容によって、plugin.h、util.h、iff.hのどのファイルに記述するべきかが違ってくるようですが、今回はmallocN()、callocN、freeN()などと同じ場所に置いておくのがよさそうに思いましたのでutil.hに書くことにしました。こちらにはBLI_lock_thread()関数に渡す引数「LOCK_IMAGE」と、ついでにもう一つ「LOCK_CUSTOM1」の定数のマクロ定義も追加しておきました。
pic090408_07.jpg
...
#define LOCK_IMAGE    0

#define LOCK_CUSTOM1    1
...
LIBIMPORT          void lock_thread (int type);
LIBIMPORT          void unlock_thread (int type);
...

そして、Linuxでは使用しませんが、Windowsでプラグインを作成する際に使用するplugin.DEFファイルにも修正が必要です。ファイルの最後に今回エクスポートした関数名を追加しました。
pic090408_08.jpg
...
lock_thread
unlock_thread
以上で、ソースファイルの修正は終了です。あとは、これらの修正が反映された状態でソースファイルからBlenderをビルドするだけです。Blenderをソースコードからビルドする方法については、以前書いたこちらの記事に参考になるページへのリンクがあります。
ということで、ビルド済みの修正版Blenderをこちらに用意しました。

Blender_modified_Linux64_090408.tar.gz
Blender_modified_Linux32_090408.tar.gz
Blender_modified_win32_090408.zip

今回は、SVNリポジトリからではなく、TARファイルとして配布されているBlender2.48aのソースコードを使用してビルドしています。
Linuxでのビルドはとくに問題なくできましたが、現在のところWindowsではFFMpeg関連のライブラリで問題が起こるようなので、コンパイルの設定でFFMpegを外しています。また、Windows版ではVisual C++ 2008 SP1の再頒布可能パッケージが必要なようです。Linuxでは、OpenAL、OpenEXRなどいくつかのライブラリをインストールしていないと動かない可能性があります。その場合、blenderの実行ファイルに対して「ldd」コマンドを実行すると、問題のあるライブラリを調べることができます。
pic090408_09.jpg

この修正版のBlenderは機能的には普通のものと何も変わらないので、現在のところはまったく意味がないです。次回は、この修正版のBlenderを使用してvectexに排他制御の組み込みを行う予定ですので、それまでもうしばらくお待ちください。
posted by mato at 00:07| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする

2009年04月05日

Blenderのマルチスレッドレンダリングでの排他制御

前回はSVGテクスチャプラグインのvectexを、プラグインバージョン5から6へとバージョンアップして、マルチスレッドレンダリング時にノイズが出る問題を回避できるようにしました。しかし、vectexではそれ以外にもマルチスレッドレンダリング時にレンダリングの途中でフリーズしてしまうという問題があります。解像度別に用意されるテクスチャマップを作成する際にスレッド間での競合が起こっていると想像できますが、なんらかの方法で競合部分に排他制御を組み込む必要がありそうです。

しかし、排他制御というのはスレッド本体の記述と対になって動作するものですので、Blender本体のソースコードを調べないで勝手に作ってしまうということはできません。しかし、逆に言えばBlender本体にはすでにマルチスレッドレンダリングに対応する排他制御が組み込まれているはずですので、それをそのまま真似するだけで済む可能性が高いということでもあります。

vectexではテクスチャマップを作成する際に、解像度別にいくつものテクスチャを用意するミップマップという機能を使用しています。BlenderのButtons Windowで「Shading」「Texture buttons」を選択し、「Map Image」パネルを表示させると、左上に「MipMap」というボタンがあります。(Texture Typeで「Image」を選択している場合)
pic090404_01.jpg

Blenderの標準機能の中にミップマップがあり、マルチスレッドレンダリングを行った場合でも問題なく動作しているということを考えると、このミップマップを処理している部分で何か参考になるようなコードが見つかりそうな気がします。ここからは、Blender2.48aのソースコードを使用して、Blenderの内部でマルチスレッドレンダリングの排他制御がどのように行われているのか調べていきたいと思います。

Blenderのソースコードは、展開するとサイズが86MBにもなる大規模なものです。
pic090404_03.jpg

しかし、実際にはコンパイル時にリンクされるライブラリまで含まれた状態で配布されています。Blender本体のソースコードはこのうちの一部分だけで、トップディレクトリの「source」の中にあります。(このsourceディレクトリだけでも28MBもありますので、大規模なものであることに変わりはないですが...。)
pic090404_02.jpg pic090404_05.jpg

このsourceディレクトリの下にはさらにいくつかのディレクトリがありますが、とりあえず細かいことは考えずにsourceディレクトリ以下の全てを対象として、grepコマンドでミップマップに関係しているソースファイルを探してみましょう。

ターミナルウィンドウを開き、blender-2.48aのトップディレクトリに移動して以下のようにコマンドを入力してみます。

grep -r mipmap *

grepのすぐ後の「-r」はディレクトリがあったらその中に対して再帰的に処理するというオプション、次の「mipmap」が検索したいキーワード、最後の「*」は検索の対象となるファイルで、この場合はすべてのファイルを指定しています。
pic090404_06.jpg

こうすると結果がずらっと表示されます。今回は250行程度なのでこの状態で端末画面をスクロールして探すこともできますが、下のようにしてテキストファイルにコマンドの結果を書き出しておけば、テキストエディタの検索コマンドなどを使うことができて便利かもしれません。(実際にこのコマンドで出力したテキストファイルtext090404_01.txt)

grep -r mipmap * > tmp.txt


このコマンドの出力を見るときには、まず、そのファイルがどこにあるのかというファイルのパスが、一つの重要な手がかりになります。
出力の最初の方の「extern/ffmpeg」「extern/glew」などexternディレクトリの下にはBlenderが使用している外部のライブラリが入っているので、今回は関係ありません。
「po/zh_CN」「po/ko」などのディレクトリには他国語対応のためのファイル、「source/gameengine」はゲームエンジン関連のファイル...というような感じで、あまり関係なさそうな部分は無視します。
pic090404_07.jpg

ずっと進んでいくと「source/blender/render」というディレクトリの下に「intern/sourece/imagetexture.c」というファイルがあります。「source/blender/render」というディレクトリには、レンダラのソースコードがまとめられているようです。現在探しているミップマップという機能は、Blenderのレンダラのテクスチャマップの機能の一部ですので、このファイルは探しているコードに関連している可能性が高そうです。
pic090404_08.jpg

実際に、テキストエディタで開いてみます。
テキストエディタには文字列の検索機能があると思いますので、「mipmap」を検索すると先ほどのgrepコマンドの検索で表示された行を探すのは簡単です。
pic090404_09.jpg pic090404_10.jpg

このファイル中には9ヶ所ほどmipmapという文字列が含まれていますが、imagewraposa()という関数中の655行目からの数行には、探していたミップマップのメモリ確保に関するコードらしきものが含まれています。
    /* mipmap test */
    if(tex->imaflag & TEX_MIPMAP) {
        if(ibuf->flags & IB_fields);
        else if(ibuf->mipmap[0]==NULL) {
            BLI_lock_thread(LOCK_IMAGE);
           
            if(ibuf->mipmap[0]==NULL)
                IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP);

            BLI_unlock_thread(LOCK_IMAGE);
        }
    }
IMB_makemipmap()という関数がそれです。
そして、その前後をはさむようにして「BLI_lock_thread(LOCK_IMAGE)」「BLI_unlock_thread((LOCK_IMAGE)」という関数が使われています。
「thread」「lock」「unlock」という単語が使われていることから考えて、Blenderのマルチスレッドで排他制御を行っている関数である可能性が高いです。

ミップマップのメモリ確保らしき関数の中身については、関数名に「mipmap」の文字列が含まれていますので、grepでの検索結果の中に入っています。「source/blender/imbuf/intern/filter.c」というファイルの中に書かれているようですので、一応こちらも確認しておいた方がいいかもしれません。
pic090404_11.jpg pic090404_12.jpg

ちょっと見ただけでは詳しいことはわかりませんが、IMB_allocImBuf()という関数でメモリの確保をしていると思われます。
pic090404_13.jpg

メモリ確保の処理はとりあえず今回は必要ありませんので、スレッドの排他制御のコマンドの方に戻ります。
この関数を使うには、引数の「LOCK_IMAGE」がどのようなものなのかを調べる必要がありそうですし、関数の定義がどうなっているのか見ておいた方がよさそうです。先ほどと同じようにして、grepコマンドで「BLI_lock_thread」をキーワードとしてソースファイルを検索してみましょう。この関数の定義が書かれている場所以外にも、この関数を使っている別のレンダリング処理なども見ておくと、何かの参考になるかもしれません。

grep -r BLI_lock_thread *

今度はかなり検索結果が絞られているようです。19行だけが表示されました(text090404_02.txt)。
pic090404_16.jpg

BLI_lock_thread()の定義は「source/blender/blenlib/intern/threads.c」に書かれているようです。そして、「source/blender/render」ディレクトリの中でシャドーバッファ、SSS、AOなどのレンダリング処理の際に、この関数が使われていることが分かります。それ以外にもフルイドシミュレーションの処理に使われたりもしているようです。

BLI_lock_thread()の定義が書かれているthreads.cを開いてみます。
pic090404_14.jpg pic090404_15.jpg

BLI_lock_thread()とBLI_unlock_thread()の定義部分(line:220-234)です。
void BLI_lock_thread(int type)
{
    if (type==LOCK_IMAGE)
        pthread_mutex_lock(&_image_lock);
    else if (type==LOCK_CUSTOM1)
        pthread_mutex_lock(&_custom1_lock);
}

void BLI_unlock_thread(int type)
{
    if (type==LOCK_IMAGE)
        pthread_mutex_unlock(&_image_lock);
    else if(type==LOCK_CUSTOM1)
        pthread_mutex_unlock(&_custom1_lock);
}
pthreadのミューテックスを使っています。pthread_mutex_lock()関数は、引数としてpthread_mutex_t型のポインタを取るようです。(pthread_mutex_lock()のmanページ)
同じファイルの「_image_lock」「_custom1_lock」の定義部分(line:95-98)です。
static pthread_mutex_t _malloc_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t _image_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t _custom1_lock = PTHREAD_MUTEX_INITIALIZER;
static int thread_levels= 0;    /* threads can be invoked inside threads */

詳しいことはわかりませんが、BLI_lock_thread()関数の引数は、先ほどのgrepの検索結果などから考えて、テクスチャ関係の処理では「LOCK_IMAGE」を使い、それ以外には「LOCK_CUSTOM1」を使っているようです。

とりあえず、ここまででマルチスレッドレンダリングの排他制御に必要な記述が大体分かりました。vectexのソースコードの中で、マルチスレッドレンダリング時にメモリ管理で問題が起こっている部分を探し、その前後を
  1. BLI_lock_thread(LOCK_IMAGE);
  2. BLI_unlock_thread(LOCK_IMAGE);
この2つの関数で囲めばいいようです。

そして、問題はこの関数をvectexのソースコードの中で使えるのかということです。
テクスチャプラグインのサンプルclouds2.cは、ソースコードの中でhnoise()という関数を使っています。hnoise()関数はBlenderの本体の方で定義が書かれていて、「plugins」「include」フォルダの中にあるplugin.hの中でエクスポートされることで、Blender本体とはまったく別にコンパイルされるテクスチャプラグインからでも使用できるようになっています。
このようにBlenderからエクスポートされている関数はhnoise()以外にもいくつかあり、util.hにはメモリ確保に関する*mallocN()、*callocN()、freeN()などの関数、iff.hにはイメージバッファ関連の多数の関数があります。

次回はそれらを参考にしBLI_lock_thread()、BLI_unlock_thread()という2つの関数をBlenderからエクスポートして、テクスチャプラグインから利用できるようにしてみたいと思います。
posted by mato at 04:33| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする
×

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