ポストフィルタな話
応用の仕方は色々
Write: 06/11/25 UpDate: --/--/--

掲示板に質問があったので、集中線と波紋のエフェクトを紹介します。
両方ともやっていることは大差なく、一度テクスチャにレンダリングした画像を変形させながら再レンダリングしているだけです。
集中線は画像サイズを微妙に小さく(もしくは大きく)しながら複数回、半透明でレンダリングします。
固定頂点で実装するなら、いわゆるフィードバックブラーの一種になるかと思います。
ここでは手抜きしてピクセルシェーダでループを回しているため、
厳密にはフィードバックしていないのですが、まぁ似たような絵にはなります。
最後に漫画っぽく実線の集中線を書き込むとよりそれっぽく見えるかもしれません。
波紋は画素毎の変位量をあらかじめ別テクスチャに書き込んでおいて、それをピクセルシェーダで適用しています。
シェーダによる計算のみの実装にしないで、一度テクスチャに変位量を書き込む方式にすると、
波紋以外にも様々な2D変位が簡単に実現できるので便利です。
代わりに毎フレーム、テクスチャをロックしてCPUからデータを書き込むことになるので、
多少描画コストは高くなるかもしれません。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
なお、PS2.0以上必須です。

グレアフィルタな話
きらきらです
Write: 06/03/25 UpDate: --/--/--

最近、3D業界ではHDR処理が流行っています。
これをまともに扱うには浮動小数点バッファが必要です。
D3DFMT_A16B16G16R16F、D3DFMT_A32B32G32R32Fといったフォーマットですね。
ところが、このフォーマットDirectX9世代ではろくにサポートされていません。
Geforce6シリーズより前では使うことすらできないですし、
RadeonシリーズでもX1000より前のものではアルファブレンドやフィルタリングができないという困った制約があります。
GDC2006の速報によるとDirectX10世代では、DirectX10準拠を名乗るためには、
すべてのフィーチャーをサポートしなければならないようなので、
現在のようなカード依存の状況が緩和されると思われます。
とはいえ、世代交代にあと数年はかかりそうです。
今回のネタは、整数バッファでアルファチャンネルを使わずにグレア処理をやってみましょうという話です。
DOUBLE-S.T.E.A.Lですっかり有名になったこの処理ですが、高輝度部の抽出に一工夫いります。
まずはこちらの資料を眺めてください。
浮動小数点バッファを使わない場合、アルファチャンネルを使って、HDR処理をかける手が定番です。
しかし、アルファチャンネルを使ってしまうと、アルファブレンドができなくなります。
当たり前とはいえ、ちょっと不便です。
そこで、グレア処理程度なら大した精度は要らないので、
高輝度部分は0,1のバイナリマスクで抽出できればいいや、と割り切ってみました。
このマスクはBチャンネルの輝度Max近辺に保存します。
輝度を[0:1]の範囲とすると、0.95(この値自体は適当なので、以下BMAXとする)までは普通に使って、
そこで値をクリップします。ただしHDR処理によって1を遥かに超える高輝度部が発生した場合は、
初めて輝度をBMAX以上の値にします。
要は、BチャンネルにBMAX以上の値が入っている部分が高輝度部となります。
BMAX〜1の間で明るさが変化したところで、ほとんど知覚できないので、
そこにマスクを仕込んでしまえということです。
ちなみにBチャンネルを使うのは、人間の目が青色に対して感度が悪いからという理由です。
まぁ気休めですね。
レンダリングのパスとしては、まずこのBMAXを境界にした高輝度部マスク付の画像を生成します。
次に高輝度部のみを抽出した白黒のバイナリイメージを生成します。
この結果にローパスフィルタをかけてぼかし、ぼかした結果を十字型に引き伸ばします。
十字型になったぼけ画像(グレア)の結果は、毎フレーム半透明合成されます。
この処理は簡易的なモーションブラーと同じです。
グレアの元ネタがバイナリイメージなので、さすがに高輝度部の抽出精度が悪くなっています。
ポリゴンオブジェクトが動いた場合にこの影響が見えてくるので、
時間方向のローパスであるモーションブラーをかけて、より自然に見せています。
最後に、グレアをBMAXを境界にした画像に加算合成すれば完成です。
基本的には前出の資料と同じやり方です。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
なお、PS2.0以上必須です。

アルファチャンネルを使っていないので、アルファブレンドが可能です。
画面上でくるくる回っているオブジェクトの中心部にある円盤は、
四角のポリゴンにアルファチャンネル付のテクスチャを張って、丸くくりぬいています。
過渡期的なアイデアですが、DirectX9世代でグレアを使う一手ということで。

アナグリフ式立体視な話
飛び出せ!
Write: 06/03/08 UpDate: --/--/--

以前ゲームを作ったときに使った、アナグリフ式立体視のお話です。
アナグリフとか言うと難しく聞こえるので、赤青メガネ方式といった方が分かりやすいでしょう。
白い紙に赤い絵と青い絵を描いて、赤青メガネで覗くと、
赤いレンズ越しには、赤い絵が見えず、青いレンズ越しには青い絵が見えません。
立体視の基本は、右目と左目の視差なので、視差のついた2つの絵を、
赤い絵と青い絵で表示すれば、眼鏡越しには立体画像として見えます。
世間的には、赤青メガネといっていますが、良く見かけるのは左目が赤、右目がシアンの組み合わせです。
なぜシアンなのかというのは、こことかここのサイトをご覧ください。
赤とシアンは補色の関係にあるからなんですね。
プログラム上は、注視点を固定し位置を平行移動させたカメラを使って、2枚の画像をテクスチャにレンダリングします。
片方の画像はRチャンネルだけ残し、GとBチャンネルは白く塗りつぶします。
もう一方の画像はG,Bチャンネルを残して、Rチャンネルを白く塗りつぶします。
この処理は、ピクセルシェーダで書くのが手っ取り早いです。
固定頂点の場合は、画面全体をシアン一色で加算合成すればRチャンネルが残り、
赤一色で加算合成すればG,Bチャンネルが残ります。
最後に2枚のテクスチャを加算合成してレンダリングすれば終了です。
実に簡単ですね。
どれだけカメラを動かせば良いのかということに関しては、
ディスプレイの大きさ、目とディスプレイの距離、右目と左目の距離、等々に依存するので、
一意には決められません。
適当に決めてしまいましょう。
コンフィグで設定できるようにしておく方が無難かと思います。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
なお、固定頂点での実装です。

現在米国滞在中につき、赤青メガネが手元にありません。
そんな訳でちゃんと立体に見えるのか不安一杯なので、パラメータの調節は各自行ってください。

トゥーンシェードな話(補)
絵心ありますか?
Write: 06/03/06 UpDate: --/--/--

前回紹介したトゥーンシェードは、一般的な実装です。
プログラマ的にはこれで問題ありませんが、野村XXは”一応”絵描きの端くれなので、あの絵は正直許せません。
なんというか、生気に欠ける感じがしませんか?
そんな訳で、ひと手間加えて、萌え具合を上げて見ましょう。
先に、結果を出しておきます。

左側が今回の結果、右側が前回の結果になります。
如何でしょうか?
陰の色が変わったことと、ハイライトが入ったことで、ずいぶん印象が変わったと思います。
陰の色については、こちらのサイトさんが非常に面白い記事を出しているので、ご覧下さい。
説明がHSVの色空間でされているので、なじみのない方には分かりづらいかもしれません。
googleさんにでも聞きながら読んでみてください。
前回の実装のように、シェーディングの成分を乗算すると、大雑把には色度は同じで輝度が落ちた絵になります。
先の記事によると、色度も合わせて変化させなければなりません。
ある色の物体に対して、どのような陰の色を与えれば、見栄えが良くなるのかということに関しては
主観が入るので一概には言えません。
この辺りは、絵描きのセンスですね。
そこで、プログラム側としては陰の色は手動で設定してもらうことにします。
シェーディング用テクスチャのU座標は濃淡に使っているので、V座標をマテリアル毎に使い分けます。
肌色用の陰、髪の毛用の陰、服用の陰、etc...、と別々に陰の色を指定します。
ソースに同梱のテクスチャを見ていただけるば、一目瞭然だと思います。
マテリアル色をテクスチャにする、やっつけプログラムはこちらです。
このプログラムで出力したbmpをベースに濃淡をつけると多少は作業がはかどるでしょう。
さらに、ハイライトを追加します。
ハイライト自体は固定頂点で言う所の、Phongモデルのスペキュラーをそのまま使っています。
スペキュラーは鏡面反射のモデルなので、あまり強く使うと肌がテカテカになってしまいます。
この辺も、要サジ加減ですね。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
なお、ピクセルシェーダ1.1必須です。

ベースの色 + 陰2色 + ハイライトという構成は、いわゆる一枚絵のアニメ塗りでは定番です。
一方、動画だと、ベースの色 + 陰1色 + 少量のハイライトというケースがほとんどです。
今回のようにモデルが動いているときに陰に2色使うと、結構くどい感じになりますね。

トゥーンシェードな話
この手のシェーディングが必要になることもあります
Write: 06/03/04 UpDate: --/--/--

今回は、定番のトゥーンシェードです。
いろんなサイトで既に実装例が出ているので、いまさら感がありますね。
たまたま、DirectX9 + HLSLベースでモデルのテクスチャとマテリアルがちゃんと反映されているものが
見当たらなかったので、ソースを公開しておきます。
基本的なアイデアは、Microsoftのものと一緒です。
2パスのレンダリングで3階調程度に塗り分けられたシェーディングと、輪郭線の表示を行います。
使うのは頂点シェーダのみです。
参考までにシェーディングの部分にはピクセルシェーダの実装も入れてあります。
2006年現在、ピクセルシェーダを全面的に使うのはまだ厳しいので、
素直に固定頂点機能を使っておく方が幸せでしょう。
シェーディングの原理は、あらかじめ3階調程度に塗ったテクスチャを用意し、
モデルの法線ベクトルとライトの方向ベクトルの内積の値をテクスチャ座標にして、
シェーディングの濃さを決めます。
法線とライトの逆方向がなす角度が0度に近づくほどシェーディングは明るくなれば良いので、
内積が1になるほど明るくなれば良いわけです。
これをあらかじめu方向に濃淡を付けたテクスチャのu座標に適用すればOKです。
シェーディング用に一枚テクスチャを使うので、
モデルに張ってあるテクスチャを生かすためにはマルチテクスチャの設定が必要です。
固定頂点ならSetTextureStageStateで設定し、ピクセルシェーダーならsamplerを2つ使います。
マルチテクスチャについてはこちらが詳しいです。
輪郭線を描く方法は、面をひっくり返したモデルを法線方向に拡大して黒塗りで
表示するという定番の方法です。
カクカクシカジカ(移転?)さんのアイデアをもらってきて、
輪郭線の太さがモデルの距離に依存しないようにしています。
これは単にモデルのwの値を拡大率にあらかじめ掛けておくだけです。
wの値はパース補正に使われるので、これをキャンセルするわけです。
大抵のアニメでは、輪郭線は均一の太さなのでこの方が自然に見えると思います。
輪郭線にはエイリアシングが出てちらつくので、
何らかのアンチエイリアシング処理を入れておく方が無難です。
ハードウェアでFSAAをかけてしまうのが簡単ですね。
対応していないグラフィックボードの場合は、解像度を上げるなり、
解像度の高いテクスチャに一旦レンダリングしてから、それを縮小表示するなり、
輪郭線の太さと色を変えて複数回レンダリングするなりすると良いでしょう。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。

 


戻る