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

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

メタボールを作る

2006年8月2日公開

始めに

メタボールを作ることについて考えてみる。

とりあえず話を簡単にするために、2つのボールを融合させることを考えてみる。でもって、融合した形状をポリゴンで表現することにする。

定義

そもそも論として、メタボールとはどういうものなのかについて、考えてみる。

まず、融合したいオブジェクトの中心部分が三次元空間中に存在するものとして考えてみる。上図での青い部分がそれに該当する。また、青い部分を包含するように有効範囲球というものを定義する。

融合された、最終的な出力となるオブジェクトは、この青い部分の外側、有効範囲球の内側に存在することになる。

次に、青い部分の表面で1.0、有効範囲球の表面で0.0になる様な濃度分布関数を考える。でもって、空間中に定義された「濃度」がある一定の値になるところの点を結んで、ポリゴンに仕立て上げたものが、出力としてのメタボールということになる。

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

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

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

濃度分布関数

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

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

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

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

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

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

空間中の一点における濃度

空間中のとある一点における「濃度」は、空間中に定義された全てのオブジェクトに由来する「濃度」を合計することにより算出する。

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

ポリゴンの生成

ポリゴンを生成するには、marching cubes法などが有名だが、ここでは異なる手法を用いてポリゴンを生成することを考えてみる。

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

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

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

そのため、まず初めに有効範囲球を表すポリゴンを生成しておいて、その個々のポリゴンの頂点の方向に向かって線を引くということを行う。でもって、頂点を決定したら、元の有効範囲球を表すポリゴンと一対一に対応するように、頂点を繋いでポリゴンにしてやればいい。

余談だが、「球を表すポリゴン」を作るのは、これはこれで頭を使う必要がある。とりあえず俺は、素直に「OpenGL プログラミング・ガイド」に記載されている方法に従っておいた。

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

空間中にオブジェクトが一つしかないのであれば、濃度分布関数の逆関数を求めれば、濃度から位置を直接算出することができるはずである。しかし、オブジェクトは当然の事ながら複数あると考えられるし、おまけに有効範囲球は互いに重なり合っている可能性もある。

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

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

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

上にある、濃度分布関数のグラフは、中から外に向かって単調減少になっているように見える。しかし、これは、オブジェクトが1つしかない場合であって、複数ある場合には、こう単純なるとは限らない。

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

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

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

また逆に、中心から外に向かって辿って濃度を求めていっても、結局閾値を下回る点が見つからないという場合が考えられる。

例えばこういう場合である。

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

この場合は、更に2つのパターンに分けて考える必要がある。

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

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

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

上記の方策に従い、全てのオブジェクトの全てのポリゴンに対して、一つずつ順番に処理していってやれば、最終的に融合された形状を表すポリゴンを生成することが可能となる。

法線の設定

ポリゴンを設定したら、法線を設定したくなる。正しく法線を設定してやらないと、見てくれが悪いからだ。

もっとも単純な方法としては、ポリゴンから垂直方向を向く法線を算出するという方法である。これであれば、メタボールとかなんとか関係なく、単純な幾何学の問題として解くことができる。

とりあえず、フラットシェーディングであれば、これで事足りるはずである。

スムースシェーディングにしたい場合はどうすればいいか。

一番簡単なのは、1つの頂点を共有するポリゴン間で、法線の平均を取る方法である。初めから頂点配列によりポリゴンを生成するようにしていれば、ポリゴン間で頂点を共有しているか否かを判断することもできるし、そう難しい事でもないはずである。

しかし、ポリゴンを頂点配列で生成すること自体が、結構面倒だったりもする。

別の方法として、幾何学的な根拠から法線を決定する事を考えてみる。

オブジェクトが一つしかない場合、あるいは、他のオブジェクトとは関係ないところにある頂点であれば、話は簡単である

オブジェクトが複数関連する場合、すなわち、オブジェクトが融合する境界面付近だった場合は、事態は幾分複雑になる。

法線は融合されたオブジェクトの表面にたいして、垂直方向を向くように定義されなければならない。しかし、複数のオブジェクトが融合している部分では、「オブジェクトの中心から反対の方向」ということでは求めることができない。

そういった場合は、下図の青い法線と緑の法線から、赤い法線を算出するという事になる。

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

おそらく、俺の考えが正しければ、この赤いベクトルは青いベクトルと緑のベクトルを、濃度の比率に合わせて足し合わせてあげれば算出できるはずである。

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

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

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

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で取得したファイルを選択する。

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

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

なお、上記の図をよく見ると、くびれの部分に乱れが生じていることが判る。これは、このページに記述したアルゴリズム上の特性であり、致し方のないものであるようだ。

参考文献

このページに記載したアルゴリズムは、ネットをググッて見つけた「分布関数による多面体オブジェクト間の融合接続の一手法」という論文を元にしている。

しかし、結構前にリンクが切れてこのPDFファイルが見つからなくなってしまった。事故的に外部に露出していただけだったのだろうか?

それともう1つ。このページのやり方だと、上図の通り融合部分の品質があまりよろしくない。ポリゴン数を少なくしつつも、もう少しいい結果を出したいのなら、やっぱりマーチングキューブ法なんかを使うのが常套手段かもしれない。

 


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.