ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

メタボールを作る

[1] 主筆嬢 06/07/29 14:06

おもむろにメタボールについて考えてみた。

[2] nabiki_t 06/07/29 14:06

メタボールって、このサイトの趣旨・志向とは大幅に異なるじゃねぇか。

[3] 主筆嬢 06/07/29 14:07

そんなことはどうでもいいじゃん。

ネタが増えればそれでいいじゃん。

[4] nabiki_t 06/07/29 14:07

まぁそうだがな。

[5] 名無しさん 06/07/29 14:08

それでいいのかよ

[6] 主筆嬢 06/07/29 14:09

とりあえず話を簡単にするために、二つのボールを融合させることを考えてみる。

でもって、融合した形状をポリゴンで表現することにする。

レイ・トレーシングとかそういった奴を使った方が綺麗な絵を表現できるんだけど、今はまだ実用的じゃないと思う。ポリゴンで表現するようにすれば、OpenGLとかDirectXとかが使えるしね。

[7] 主筆嬢 06/07/29 14:10

いきなりやるといっても、なかなかできないものだから、とりあえず参考文献を当たることにする。

いろいろと検索してたら、こんなページが出てきたから、参考にしてみた。

[8] 主筆嬢 06/07/29 14:11

>>7のファイルは、良くありがちな数学チックな表現でどうにも判りにくい。

とりあえず、自分なりに解釈した結果を書いておいてみる。

[9] nabiki_t 06/07/29 14:12

本当に理解できているのかどうかは怪しいがな。

[10] 主筆嬢 06/07/29 14:12

うるさい。

[11] 主筆嬢 06/07/29 14:13

バカは放っておいて、>>7のファイルに書いてあることを考えてみる。

まずここで、生成されるメタボールとはどういう物なのかを定義してみる。

[12] 名無しさん 06/07/29 14:14

へったくそな絵だな、おい。

[13] 主筆嬢 06/07/29 14:014

まず、融合したいオブジェクトが三次元空間中に存在するものとして考えてみる。>>11の絵でいうところの青い奴がそれ。

次に、オブジェクトの表面で1、有効範囲球の表面で0になる濃度分布関数を考える。

で、空間中に定義された「濃度」が、ある一定以上の大きさになるところの点を結んで、ポリゴンに仕立て上げたのがメタボールと言うことになる。

書き方を変えるとこんな感じ。

上の絵は、濃度分布関数により与えられる「濃度」を、色の濃さで表してみた。まぁ、イメージだけど。

で、融合されたオブジェクトを作るには、上の絵の、ある一定以上の濃さのところをつなぎ合わせて、一つの形状にしてあげればいい。

[14] 主筆嬢 06/07/29 14:15

次に、濃度分布関数とは何かを考えてみる。

オブジェクトの中心点(O)からオブジェクトの表面までの距離をts、有効範囲球の表面までの距離をte、tsとteの間の任意の一点をtmとする。

また、オブジェクト表面上での濃度をCとする。

そして、tmでの濃度Cmは下記の式で求める。

ついでに言うと、この式をグラフにするとこんな感じになる。

これは、C=1、ts=1、te=3で、X軸にtm、Y軸にCmをとってグラフ化している。

[15] 主筆嬢 06/07/29 14:16

もし、空間中にオブジェクトが複数あったら、それぞれのオブジェクトに由来する濃度を加算することになる。

上図の交点の濃度は、「オブジェクト1のCm」+「オブジェクト2のCm」になる。

[16] 広告 06/07/29 14:18

[17] nabiki_t 06/07/29 14:19

まぁ、ここまでは特に問題はない。

でも、最大の難問は「どうやって、ある一定の濃度の部分を繋いでポリゴンにするか」だな。

これが解けなきゃ、どうしようもない。

[18] 主筆嬢 06/07/29 14:20

やり方はいろいろあるだろうけど、ここでは素直に>>7のやり方をやってみる。

[19] 主筆嬢 06/07/29 14:23

基本的な戦術としては、オブジェクトの中心から放射状に(仮想的な)線を引いて、その線上で「濃度」が閾値になるところを求めて、そこをポリゴンの頂点として使用する、という方法をとる。

[20] 主筆嬢 06/07/29 14:25

中心から放射状に線を引くと行っても、ただ闇雲にやっても仕方がない。

後でポリゴンにしなければならないのだから、この時点でポリゴンとして生成しやすいような形にしておいてあげる必要がある。

だから私は、まず初めに有効範囲球を表すポリゴンを生成しておいて、その個々のポリゴンの頂点の方向に向かって線を引くことにした。

で、頂点を決定したら、元の有効範囲球を表すポリゴンと一対一に対応するように、頂点を繋いでポリゴンにしてあげればいい。

ついでに言っておくと、球を表すポリゴンの生成方法については、私は「OpenGL プログラミング・ガイド」に書かれてるやり方を使っておいた。

[21] 主筆嬢 06/07/29 14:27

次に、濃度が閾値になるところをどうやって決めるか、について考えてみる。

[22] 主筆嬢 06/07/29 14:28

空間中にオブジェクトが一つしかないのであれば、>>14の式の逆関数を求めれば、濃度から位置を直接算出することができる。

でも、オブジェクトは当然の事ながら複数あると考えられるし、おまけに有効範囲球は互いに重なり合っている可能性もある。

だから、位置は簡単には決まらない。

[23] 主筆嬢 06/07/29 14:30

仕方がないから、中心の方から外に向かって、小刻みに濃度を算出していって、位置を決定するしかない。

このとき、まかり間違っても外から中心に向かって計算していってはいけない。

>>14のグラフでは、濃度は単調減少に見える。つまり、中心の濃度が高くて外に向かって薄くなっていっているように見える。

でもこれは、オブジェクトが一つしかない場合であって、複数ある場合には、こう単純にはいかない。

もしかしたら、下記のグラフのような変化をするかも知れない。

この場合、ts側から辿っていった場合と、te側から辿っていった場合では、閾値を下回る点が異なってしまう。よく考えてもらえば分かると思うけど、閾値になる点が複数個ある場合は、オブジェクトの中心に最も近い点を採用する必要がある。

だから、中心から外に向かって行くことになる。

[24] 主筆嬢 06/07/29 14:32

それからもう一つ、中心から外に向かって辿って濃度を求めていっても、結局閾値を下回る点が見つからない場合がある。

例えばこういう場合。

オブジェクトの中心から有効範囲球までの線分が、融合されたオブジェクトの内部で始終するとき。こういう場合は、この矢印上には濃度が閾値を下回る点は存在しない。

[25] 主筆嬢 06/07/29 14:35

この場合は、更に二つのパターンに分けて考える。

まず一つは、ポリゴンを形成する全ての頂点について、閾値を下回る点が見つからなかった場合。もう一つは、一部の頂点についてだけ、閾値を下回る点が見つからなかった場合。

前者の場合は、ポリゴン全体が「融合されたオブジェクト」に内包されることになるわけだから、このポリゴンは生成しなくても良いことになる。

後者の場合は、ポリゴンの一部が表に現れることになるため、省略するわけにはいかない。仕方がないから、閾値を下回る点が見つからなかった奴については、頂点を有効範囲球の表面に設定する。

[26] nabiki_t 06/07/29 14:37

とりあえずこれで、ポリゴンは生成できるようになったな。

だがしかし、まだ法線が設定できてないぜ。

[27] 主筆嬢 06/07/29 14:40

言われなくても分かってる。

[28] 主筆嬢 06/07/29 14:42

とりあえず、ポリゴンが決定できれば、ポリゴンの垂直方向を向く法線を求めることができる。

[29] nabiki_t 06/07/29 14:44

それだと、フラットシェーディングにしかならねぇじゃねぇか。

ちゃんと頂点ごとの法線を求めて、スムースシェーディングができるようにしろよ。

[30] 広告 06/07/29 14:45

[31] 主筆嬢 06/07/29 14:45

わがままな奴だ。

とりあえず一番簡単なのは、一つの頂点を共有するポリゴンの法線の平均をとる方法。

一応、これである程度は何とかなる。

[32] 主筆嬢 06/07/29 14:46

それともう一つ。幾何学的な根拠から法線を決定する事を考えてみる。

オブジェクトが一つしかない場合、あるいは、他のオブジェクトとは関係ないところにある頂点であれば、話は簡単。法線はオブジェクトの中心点の反対側を向くことになる。

[33] 主筆嬢 06/07/29 14:48

オブジェクトが複数関連してくる場合、つまり、オブジェクトが融合する境界面付近だった場合は、ちょっと複雑なことになる。

法線は融合されたオブジェクトの表面にたいして、垂直方向を向くように定義される。でも、複数のオブジェクトが融合している部分では、>>32の要領では求めることができない。

[34] 主筆嬢 06/07/29 14:50

この場合、下図の青い法線と緑の法線から、赤い法線を算出してやる。

緑のベクトルは、オブジェクト1の中心から反対方向を向くベクトルで、青いベクトルは、オブジェクト2の中心から反対方向を向いたベクトルを表している。

そして、私の妄想に従えば、この赤いベクトルは青いベクトルと緑のベクトルを、濃度の比率に合わせて足し合わせてあげれば算出できる。

ポリゴンの頂点にでの濃度をCm、オブジェクト1の濃度をCm1、オブジェクト2の濃度をCm2、オブジェクト1の中心の反対方向を向くベクトル(つまり緑の奴)をV1、オブジェクト2の中心から反対方向を向くベクトル(つまり青い奴)をV2とすると、赤いベクトルVは、下記のように求めることができる。

V1を正規化
V2を正規化
V1 = V1 * Cm1
V2 = V2 * Cm2
V = V1 + V2
Vを正規化

[35] nabiki_t 06/07/29 14:51

妄想って……

それでいいのかよ。

[36] 主筆嬢 06/07/29 14:53

いいんじゃない?

何となく、それっぽい絵が得られるから。

[37] 主筆嬢 06/07/29 14:54

そういうことで、今まで垂れてきた講釈を元に、プログラムを作ってみた。

meta.zipに含まれているのが、メタボールを生成するプログラム。gmview.zipの奴は、meta.zipの出力を受け取って画面に表示するプログラム。

一応、使い方を書いておく。

  1. meta.zipを展開する。
  2. 中に含まれているmeta.cppをコンパイルする。
  3. 生成された実行プログラムを実行する。結果は標準出力に出力されるため、それをファイルにリダイレクトする。
  4. gmview.zipを展開する。
  5. Visual C++ 6.0でコンパイルする。
  6. 生成された実行プログラムを実行する。
  7. メニューの「ファイル(F)」−「開く(O)...」から、3で取得したファイルを選択する。

ここまでできたら、こんな画面が表示されるはず。

ウインドウの上でマウスをグリグリと動かすと、表示されているオブジェクトも動くようになっているけど、それ以上の細かい制御は一切できない。そんな丁寧には作られてないから。

[38] nabiki_t 06/07/29 14:56

まぁプログラムは、あんまり当てにはしない方がいいような気がするがな。

きったねぇから。

[39] 主筆嬢 06/07/29 14:54

それと、>>37のプログラムで表示されるオブジェクトは、くびれ部分の法線がうまく設定できていないように見える。

これはどうやら、このページで講釈を垂れたやり方に依存する、本質的な問題みたい。法線を設定するアルゴリズムに>>31の方法を使っても、>>32の方法でやっても、同じ様な結果になる。

それに、>>7のファイルの2ページ目にある画像を見ても、同じような乱れが確認できる。

どうやら、このやり方だと綺麗な画像を生成するためにはポリゴン数を増やさなければならないらしい。

[40] nabiki_t 06/07/29 14:57

まぁ、いつの日にか暇があったら対処法でも考えてみるさ。