スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ファー

2006年09月29日 11:47

ってことで、Furシェーダーを書きました。

たまには3Dのこともやらないとね('A`)

fur.png



原理はいたって単純。
一度 SRC = SRCALPHA, DST = ZERO でアルファに適当な数値(あるけ は0.1をいれた)で書き出して、次に SRC = SRCALPHA, DST = ONE で法線方向に頂点を延ばすようなシェーダーで加算していきます。
この法線方向に頂点を延ばすようなシェーダーを適当な回数(あるけ は10回にしてみた)で同じように適当なアルファ値(あるけ は0.1をいれた)で加算すると上のような絵ができます。

毛の色だけちょこっと変えることも出来たりします。
テクスチャはなるべくノイズが入ってるようなテクスチャのほうが毛っぽく見えるかな。


簡単なんで1時間もあれば出来ます。

頂点を延ばすところに風の力なんかをいれてやるとちょこっと風になびいたりすることも可能かも。

blog_rank
スポンサーサイト

C++言語で関数型プログラミング 第2章

2006年09月22日 13:56

pelt の記事を挟んじゃったけど、クロージャーの記事の続きです。

前回は Lambda が非常に長ったらしいテンプレート引数を持ったクラスだってことと、テンプレートが複雑に絡み合って動作していることを書きました。



今回は Lambda がどうやって動いてるかを具体的に説明したいと思います。

まず Lambda の動作。
Lambda とは無名関数を扱うためのテンプレートです。
printf( "%d\n", (_1 * 10)(5) )
と書いたとき、50 と表示されるわけです。

何が無名なのか?
それは _1 を後で 5 と置き換えてるところです。
要するに、この 5 と書いてるところは何でもいれていいわけです。

実際、このまま使うだけでは大した威力もありません。
これがテンプレート関数と結びつくと色々面白いことが出来ます。

template int Test( T lambda )
{
  return lambda(7);
}

として
 Test( _1 * 10 )
を呼んでやると

結果が 70 と帰ってきます。
要するに、式を渡すことでパラメーターが帰るようになるわけです。

画期的だと思いませんか?
従来はパラメーターを渡すことで中で式に代入し、結果が返ってきました。
これだとパラメーターが同じ場合、式を取り替えるのは呼ぶ関数を変更することになります。

たとえば、曲線の補間処理を行う時、従来は曲線タイプを保持して計算処理を選択・実行していたところ、式自体を保持できるので曲線式自体をパラメーターとして保持できます。
曲線タイプを保持するより、よほど柔軟に処理できますよね。
ユーザーが自分で定義した曲線も処理できます。
(実際は数式しか保存できないので、曲線を定義するのは難しい)

他には、クイックソートの比較条件などを渡してやるなど、色々応用範囲はあります。



さて、実際どうやって実現してるかというと、、、TypeList というテンプレートがあります。
これ自体は

template< typename T, typename U >
struct TypeList
{
  typedef typename T Head;
  typedef typename U Tail;
};

と、まぁ、これだけの構造体なのですが、これを使用することで無限にテンプレートの引数を増やすことが出来ます。
要するに、TypeList を入れ子状にするわけです。

 TypeList< int, TypeList< float, double > >

などのように。
こうすれば、何個でもテンプレートの引数を増やすことが出来ます。

上のタイプは型の定義しかしていませんが、これに実体を持たせたものが Lambda で使用されています。



template< typename T, typename U >
struct Tuple
{
  typedef typename T Head;
  typedef typename U Tail;
  
  Head  h;
  Tail  t;
};


実体を持つことで実際にデータを保持することが出来、柔軟に(自動的に)型が決定される Lambda に使われるわけです。

さて、どうやって実装されているのか?
ためしに実装したのがありますのでこれを元に説明します。


ってことで、下にファイルを添付します。

LambdaSample1.txt


実行してみるとわかると思いますが、テンプレートが式を保存出来てることがわかると思います。

さて、この技術の肝となる部分は、、、

#define LAMBDA_OPERATOR_BINARY_TL( en, op )               \
  template< ActionType Act, typename Arg, typename B > inline     \
  Lambda< en, Tuple< Lambda, B > >             \
  op( const Lambda &a, const B &b )             \
  { return Lambda, B > >( a, b ); }   \

#define LAMBDA_OPERATOR_BINARY_LT( en, op )               \
  template< typename A, ActionType Act, typename Arg > inline     \
  Lambda< en, Tuple< A, Lambda > >             \
  op( const A &a, const Lambda &b )             \
  { return Lambda > >( a, b ); }   \

#define LAMBDA_OPERATOR_BINARY_LL( en, op )                        \
  template< ActionType ActA, typename ArgA, ActionType ActB, typename ArgB > inline   \
  Lambda< en, Tuple< Lambda, Lambda > >             \
  op( const Lambda &a, const Lambda &b )            \
  { return Lambda, Lambda > >( a, b ); }  \

#define LAMBDA_OPERATOR_BINARY( en, op )    \
  LAMBDA_OPERATOR_BINARY_TL( en, op )    \
  LAMBDA_OPERATOR_BINARY_LT( en, op )    \
  LAMBDA_OPERATOR_BINARY_LL( en, op )    \
  


LAMBDA_OPERATOR_BINARY( PLUS, operator + )
LAMBDA_OPERATOR_BINARY( MINUS, operator - )
LAMBDA_OPERATOR_BINARY( MULTIPLE, operator * )
LAMBDA_OPERATOR_BINARY( DIVIDE, operator / )
LAMBDA_OPERATOR_BINARY( SURPLUS, operator % )


この部分です。

やってることは、オペレーターの実装をしているんですけど、戻り値に Tuple を使用し新しい型を定義して入れ子で返してるのがわかると思います。
要するに、Lambda を + や * などのオペレーターで合成するたびに新しい型を戻り値の型として返してるワケです。


あるけ がテストで書いた、この Lambda 、機能としては全然ダメです。
戻り値が一番最初に突っ込んだ型に特定されちゃうこと
(たとえば上のファイルでいうと、(10 + sample1)() の場合、必ず戻り値は int になってしまう)
が、上げられます。


しかし、ほぼ BOOST の Lambda も同じようなトリックで動いています。
上の式に Unary(単項) と Binary(二項) の定義を追加し、引数を評価できるようにトリックを入れてやれば BOOST の Lambda が出来上がります。


次回は、それじゃ あるけ が作った TCClosure はどういった原理で動いてるの?を説明します。

あるけ が作った TCClosure は戻り値の型はかなり柔軟に対応されています。
また、今回 あるけ が書いた Lambda や BOOST の Lambda と違い、「戻り値の型」だけ指定してやれば式を「変数として保存する」ことも可能です。

BOOST の Lambda では式の保存を行うことはテンプレート関数やクラス以外で無理でした。

次回はもう少し具体的な話が出来ると思います。




blog_rank

ペルトマッピングあれこれ その2

2006年09月19日 17:54

前回からの続き~!
ってことでペルトマッピングのこと書きます。


前回はエッジの処理方法まで書きました。

後は各頂点間をバネモデルで結び、カッティングエッジ部分を外側に引っ張るだけです。


これだけなんですけど、そう簡単には行きません。

まず問題点を列挙しましょう。
・各頂点間を結ぶ時、どうやって新しく生成したエッジの頂点を対応させるか?
・カッティングエッジ部分を引っ張る方向はどうやってきめるのか?

この2つが大まかな問題として存在します。


それでは順に説明しましょう。
まず「どうやって新しく生成したエッジの頂点に対応させるか?」は、前回生成したエッジの座標系を使用します。
まず、カッティングエッジで使用されている頂点を含む三角形だけ処理します。
それ以外は単純にバネモデルで結ぶだけでOKです。

ちょっと長ったらしいので カッティングエッジで使用されている頂点 を TweakVert と呼称します。



さて、TweakVert を含む三角形には4つの場合があります。
・TweakVert を3つ含む三角形(カッティングエッジを2つ含む三角形)
・カッティングエッジを1つ含む三角形
・カッティングエッジは含まないけど TweakVert を2つ含む三角形
・TweakVert を1つ含む三角形


一番特殊なのは TweakVert を3つ含む三角形ですね。
この場合、「カッティングエッジの1筆書きリスト」で連続して3つ全ての頂点が出ている箇所を調べ(かならずあるはずです)
その対応頂点でバネモデルを作成します。


次に特殊なのは2番目のカッティングエッジを一つ含む三角形。
この場合は、TweakVert じゃない頂点がエッジ座標系のX軸との内積が正か負かで対応頂点(たとえば逆向きの方でX軸との内積が正だった場合、新しく生成された頂点になる)を決めます。


次はカッティングエッジは含まないけど TweakVert を2つ含む場合。
この場合だとほぼ次の TweakVert を1つ含む場合と一緒に処理できます。
用は、きちんと対応する TweakVert を探せば言い訳なので、一番近いエッジを探し、座標系のX軸との内積が正か負かを調べればOKです。
正の場合は順、負の場合は逆になるので、対応する頂点を設定します。


これで、バネモデルの構築は終わりました。



次の問題は TweakVert の引っ張る方向です。
コレに関してもエッジの座標系のX軸を使います。

まず下準備として TweakVert で「共分散行列を対角化する行列」を求めます。
要は「最適な姿勢のバウンティングボックス」を求めます。
なぜ求めるか?は「共分散行列を対角化する行列」を求める最中に、「一番偏りが少ない軸」を見つけることが出来るからです。
「一番偏りが少ない軸」を「面法線とする平面」は、投影するにはもってこいなわけです。


次に、エッジを構成している TweakVert にその TweakVert が使用しているエッジの座標系のX軸を累積加算していきます。
全ての TweakVert に対して処理が終わったら、加算結果を正規化し方向にします。

その後、各 TweakVert の位置から先ほど求めた平面に最短線を引きます。
また、モデル中央からも同様に平面に最短線を引きます。


次からは全てこの平面上で行う処理です。


先ほど求めた TweakVert の方向ベクトル、これをそのまま使用するだけじゃ綺麗に放射状に広がりません。
同じく先ほど求めたモデル中央からの最短線でぶつかった点と TweakVert の各位置から引いた最短線がぶつかった点で方向ベクトルを求めます。
これをいい割合で TweakVert の方向ベクトルに加算してやることで綺麗に放射する方向ベクトルが手に入ります。


ちょっとわかりにくいのでまとめてみると、、、
 ★「共分散行列を対角化する行列」から一番偏りが少ない軸を求め、
  その軸を法線とする平面を定義する。
 ①各 TweakVert が使用しているエッジの座標系のX軸を平均化した方向ベクトル
 ②モデル中央と★の平面との一番近い点 から 各 TweakVert と★の平面との一番近い点
  への方向ベクトル

 ①と②をステキな割合(あるけは ①:② = 1:2.5で設定しました)で
 混合した方向ベクトルを求める。


です。

後は展開先の平面の法線方向の射影を引いた分をこれまた正規化してやって方向を求めます。

そして、モデル中央と平面との一番近い点から求めた各 TweakVert の方向ベクトルを適当な距離だけ倍してやると綺麗に放射状に広がる引っ張り位置を求めることが出来ます。

要は、TweakVert の引っ張り位置を放射状におきたいわけです。
各自いいと思うアイディアでやってみてください。

あるけがやった方法は答えではありません。一種の提案です。
無論、これより結果がいい方法があるでしょう。




さて、出来上がったツールはというと、、、。

pelt0.jpg


pelt1.jpg



こんな感じになりました。

これは球体をペルト(展開)している様をあらわしています。


ってことで、後はUIとかを作ればツールとして完成だけど、面倒だから一旦放置w


次はなにやろうかなー(・∀・)y‐┛~~



blog_rank

ペルトマッピングあれこれ その1

2006年09月08日 23:24

プライベートのことで色々あったせいか、ちょっとブログ書く元気がありません。。

google とかから検索で飛んできてくれた人やこんなページでもブックマークしてくれている奇特な人に大変申し訳ないです( ;Д⊂ヽ


ってことで、ブログを閉鎖します!


・・・・っていうのはウソですw


書く元気がないからって何もやってないわけじゃないですよ(・∀・)
あ、けど Closure のことはもうちょっと元気が出た時にでも書きますね。
あれは説明するのも労力が必要なので( ;Д⊂ヽ


今日は今やってることなんかをちょっとまとめるために書きます。


今は PeltMapping っていう処理をやっています。
これは、モデルデータのUVを「アジのヒラキ」みたいに展開(Unfold)する処理です。
3D Studio Max 8 の UV Pelt Mapping 機能ですね。


2000年ぐらいの Siggraph に出てた論文を元に作っています。
・・・っていうけど、原理は恐ろしいほど強引で簡単なんですけどねw

とりあえず、参考になるURLを上げておきます。
http://www.cgl.uwaterloo.ca/~krmoule/talks/pelt/intro.html

ちなみに、Model Pelting のプラグインは Maya のはフリーでありますし、 XSI のも多分あるでしょう。確かめてないけどw

なので、デザイナーさんが使う分にはメインストリームのソフトウェアでサポートされているので1から作る必要はそんなにないわけです。


ならなぜ作るのか?
もちろん自分が作ったツールでUVを気にしないでモデルに対して陰影のテクスチャを張ったりペイントしたりをやりたいからです!
テキストエディターを作ったのと同じ理由ですね(´ω`)



さて、原理はというと、上でも「簡単」で「強引」と書きましたが、物凄い簡単で物凄い強引です。
まず、「アジのヒラキ」を作る場合やらないといけないことはなんでしょうか?

切れ目を入れることですね。

PeltMapping も同じです。
UVを展開する時に展開元となる切れ目のエッジを選択します。

その後何をするかというと、、、
物理演算を行いながら実際に皮を剥ぐようにメッシュを展開していくんですねw
強引過ぎますw 考えた人単純すぎw
けど、理にかなってます。


やり方は、、、
 1:UVの展開を行うために切れ目を作成します。
 2:メッシュの頂点間を結ぶ線(エッジ)を全てばねモデルにします。。
 3:1の切れ目を引っ張ります
 4:後は布シミュレーションを行うようにばねモデルを解決しちゃえばOKです。

こう書くと簡単そうですね~。


実際は結構面倒な処理があります( ;Д⊂ヽ



まずはエッジの生成処理。

maya とかじゃ線を選択して切断コマンドしちゃえば出来ちゃいますが、自力でやる場合案外面倒です。


何が面倒なのか?
エッジでメッシュを切断した場合、当たり前ですけど切断部分の点が出来ます。
これはエッジに沿って切った場合も同様です。


切断後に出来た点をメッシュが崩れないように切断部分のポリゴンを再連結する処理がまず面倒でした。

あるけ は最近頭がなまってるせいか、あまりいい考えが思い浮かばず強引にやることにしました。
まず、切断点の頂点(これはメッシュが保持している既存の頂点になっています)のインデックスを使って、カットするエッジを用意します。
(用意の仕方はとりあえず置いておきます。まだ実装してないので( ;Д⊂ヽ
多分GUIを用意することになるでしょう。2点間の最短エッジを結ぶとかメッシュを調査して自動的にカットする部分を見つけるとか)


次に一筆書きの順番になるように切断する点を整列します。

このとき、往復で考えるところがミソです。
たとえば、
 1->2, 2->3, 3->4
と切断点が指定された場合
 1->2, 2->1, 2->3, 3->2, 3->4, 4->3
と往復でカットするエッジを配列に入れておくわけです。

この例を一筆書きの順番に並べてみると
 1->2, 2->3, 3->4, 4->3, 3->2, 2->1
になります。


こんなの簡単じゃんとか思う人いると思います。
それじゃカットするエッジが十字にクロスしてた場合を考えて見ましょう。

   4
   |
 3-2-5
   |
   1

こんな感じにですね。

この場合、一筆書きで書こうとすると、(CCW でいきます)
 1->2, 2->5, 5->2, 2->4, 4->2, 2->3, 3->2, 2->1
となります。


これを生成するところまでが第一関門。


まぁ、単純に考えて直線の為す角度を利用すればいいわけです。
為す角度っていうと内積。
けど、ただ内積を取るだけじゃ CW 方向なのか CCW 方向なのかわかりません。
そこで、あるけ はエッジに座標系を持たせました。

この座標系、一意に求まらないとダメです。
ここでもちょっと躓きました。
なんでかっていうと、1->2 のベクトルをたとえばZ軸として座標系を作成しようとしても
それは 1->2 のベクトルの周りに無限に存在することになっちゃいます。
これじゃ、使い物になりません。
Z軸だけが固定だけじゃX軸だと思って使ってても、Y軸が反転すればX軸も反転しちゃうので・・・。


それじゃどうするのか?
1->2 ベクトルを使うポリゴンを全て列挙して、そのポリゴンの面法線を加算して単位化して使いました。

こうすれば2つ(1->2 ベクトルと面法線の平均のベクトル)のベクトル(しかも必ず交差する)により座標系を固定できるんです。


後は為す角度を求めるだけです。
求め方を上記の例(クロスさせたやつ)をもとに説明しましょう。
1->2 ベクトル(以降Aベクトル)と 2->5 ベクトル(以降Bベクトル)を例にとって見ますと、
まず内積で余弦が出るので、acos( A・B ) で角度が求まります。
次に A ベクトルの座標系 M のX軸ベクトル(これをxベクトルとします)と B ベクトルで内積を取ります。
内積の方向でどっち周りなのかがわかるわけですね。
これで acos( A・B ) が CW なのか CCW なのかがわかるわけです。


さて、後は単純に角度を調べて、CCW に一番近いエッジを選んでいけばOKです。
・・・ってワケにも実は行きませんw
A ベクトルと B ベクトルの内積の結果と x ベクトルとの内積の符号、場合により x ベクトルの内積の大きさと座標系 M のY軸ベクトル y との内積の符号も使わないと実際の方向は出ないわけです。

たとえば、A・B == A・C となる C ベクトルがあったとします。
そうすると、C != B となるベクトルであった時、必ずどちらかが先に処理されるように求めなければいけません。
X ベクトルとの内積の符号だけみれば回転方向はわかりますが、回転方向が同じで A・B == A・C だった場合、先に処理された方が隣接辺と判定されてしまうでしょう。

そこで、X ベクトルとの内積の大きさと Y ベクトルとの内積の符号も使用します。
実際、A・B == A・C の時だけ判定すればいいのであまり無いかもしれません。
A・B == A・C の場合に X ベクトルとの内積と、Y・B と Y・C の符号を使って為す角度を求めます。
ちなみに、 A・B == A・C かつ X・B == X・C の場合 B == C となるので、この場合はエッジの指定方法またはポリゴン事態がおかしなデータになります。

これで一筆書きのカットするエッジリストが手に入ります。



とりあえず今日はここまで。
また気が向いたら続きを書きますw




blog_rank

浮動小数点の標準宣言

2006年09月01日 16:38

今日はプログラムの話題だけど、自分のためのメモメモ。

浮動小数点のリミットや EPSILON などの標準的な宣言。
多分 ANSI 準拠。
float.h に記述されています。


float 型用
 FLT_DIG      6         -- 仮数部?
 FLT_MANT_DIG   24         -- 整数部?
 FLT_EPSILON    1.192092896e-07F  -- 1+EPSILON でおおよそ1とみなしていい最小表現値(誤差含む)
 FLT_MAX      3.402823466e+38F  -- とりえる最大値
 FLT_MIN      1.175494351e-38F  -- とりえる最小値
 FLT_MAX_10_EXP  38         -- 10進数表示時の最大桁数?
 FLT_MIN_10_EXP  (-37)       -- 10進数表示時の最小桁数?
 FLT_MAX_EXP    128        -- 2進数表示時の最大桁数?
 FLT_MIN_EXP    (-125)       -- 2進数表示時の最小桁数?
 FLT_RADIX     2         -- 計算時の基数



同じようなのが double にももちろん定義されています。

double 型用
 DBL_DIG     15            -- 仮数部?
 DBL_MANT_DIG  53            -- 整数部?
 DBL_EPSILON   2.2204460492503131e-016 -- 1+EPSILON でおおよそ1とみなしていい最小表現値(誤差含む)
 DBL_MAX     1.7976931348623158e+308 -- とりえる最大値
 DBL_MIN     2.2250738585072014e-308 -- とりえる最小値
 DBL_MAX_10_EXP 308           -- 10進数表示時の最大桁数?
 DBL_MIN_10_EXP (-307)          -- 10進数表示時の最小桁数?
 DBL_MAX_EXP   1024           -- 2進数表示時の最大桁数?
 DBL_MIN_EXP   (-1021)         -- 2進数表示時の最小桁数?



結構知ってるだけで便利ですよね。
特に EPSILON や MAX なんかは良く使うのでは?

blog_rank


最近の記事


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。