
エフェクト(14) 2.5Dモーションブラー★ 1.はじめに
今更ですが,ロストプラネットで使われているという2.5Dモーションブラーが気になったので作ってみました。
![]() ★ 2. 2.5Dモーションブラーとは?
2.5Dモーションブラーについては,西川さんの記事に詳細が載っています。またDirectXをお使いの方はもんしょさんがすでに説明されていたり,Imagireさんが書かれている"DirectX 9 シェーダープログラミングブック"の467ページあたりにも説明がのっていますので,そちらのほうを参考にしてください。
2.5Dモーションブラーは2003年のGame Developers ConferenceでNVIDIAのSimon Greenさんが発表した"NVIDIA Stupid OpenGL Shader Tricks"が基です。 基本的な考え方は,基となる物体を描画して,それらを法線ベクトルの方向に引き伸ばし,速度マップを描画します。最後に速度マップに基づいてブラー処理を行います。 まず,シーンをテクスチャに描画します。これは問題ないでしょう。いつもどおりに描画するだけです。 次に頂点シェーダーを使って,各ピクセルに対して速度を計算し,速度マップを作成します。 ![]() まず,モデルを引き伸ばす方法ですが,移動軌跡ボリュームという方法を使います。 現在の位置座標P(t)と,1フレーム前の位置座標P(t-1)がわかれば,各頂点ごとの速度dP/dtを求めることができます。そこで,速度と法線ベクトルの向きの比較を行い,速度と法線ベクトルのむか等しければ,現在の位置座標を,向きが反対であれば1フレーム前の位置座標を使用するメッシュを作成します。向きの判定は速度ベクトルと法線ベクトルの内積を計算すれば判定ができます。 あとは,この処理を行った結果を速度マップとして出力します。実際にはつぎのフラグメントシェーダの処理で,すぐ使うのでまともにテクスチャに保存する必要はありません。 あとは,求めた速度を用いてフラグメントシェーダ(ピクセルシェーダ)で,速度ベクトルの方向に沿って,オブジェクトが書かれたテクスチャを何度もサンプリングします。最終的に出力する色はサンプリングしたテクセルを平均した色になります。 ★ シェーダの処理
上で説明したことをあとはコードに記述していきます。
まずは頂点シェーダのほうからです。
00102: //------------------------------------------------------------------------------------------------
00103: // Name : BlurVS()
00104: // Desc : 速度マップの描画
00105: //------------------------------------------------------------------------------------------------
00106: VertexOutput BlurVS(VertexInput In)
00107: {
00108: VertexOutput Out;
00109:
00110: // 座標変換
00111: float4 P1 = mul(In.Pos, modelView); // 現在のビュー座標
00112: float4 P0 = mul(In.Pos, prevModelView); // 1つ前のビュー座標
00113: float3 V = P1.xyz - P0.xyz; // 速度
00114: float3 N = mul(In.Normal, (float3x3)modelView); // ビュー座標系での法線ベクトル
00115:
00116: // 射影空間に変換
00117: P1 = mul(In.Pos, modelViewProj);
00118: P0 = mul(In.Pos, prevModelViewProj);
00119:
00120: float flag = dot(V, N) >= 0; // 速度の向きかどうか
00121: float4 Ps = flag ? P1 : P0; // 向いてる方向によって位置を決定
00122: Out.Pos = Ps;
00123:
00124: // 射影空間からテクスチャ空間に変換する
00125: Out.Tex.xy = Out.Pos.xy + Out.Pos.w;
00126: Out.Tex.w = 2.0f * Out.Pos.w;
00127:
00128: // テクスチャ座標での速度を求める
00129: P1.xyz /= P1.w;
00130: P0.xyz /= P0.w;
00131: Out.Velocity = (P1.xyz - P0.xyz)*0.5f;
00132:
00133: return Out;
00134: }
00135:
それほど難しくはありませんね。つづいて,フラグメントシェーダの処理です。 移動軌跡ボリュームでのスクリーン座標の位置(Tex.xy/Tex.w)から速度方向(Velocity)に少しずつずらしながらモデルをレンダリングしたテクスチャをサンプリングしていきます。最後に,合計した結果をサンプリングした回数で割って,色の平均を求めます。
00137: //------------------------------------------------------------------------------------------------
00138: // Name : BlurPS()
00139: // Desc : 速度マップからブラーを描画
00140: //------------------------------------------------------------------------------------------------
00141: float4 BlurPS(
00142: float4 Tex : TEXCOORD0,
00143: float3 Velocity : TEXCOORD1
00144: ) : COLOR
00145: {
00146: float4 Out = 0;
00147: const float samples = 16;
00148: float2 tex = Tex.xy/Tex.w;
00149: float2 velocity = Velocity.xy * blurScale;
00150:
00151: // サンプリング
00152: for ( float i=0; i<samples; i++)
00153: {
00154: float t = (i+1)/samples;
00155: Out += tex2D(sceneTex, tex + t *velocity);
00156: }
00157:
00158: // 平均を求める
00159: Out /= samples;
00160:
00161: return Out;
00162: }
00163:
こんな感じで一応2.5Dモーションブラーができます。 しかし,西川さんの記事にも書かれてあるとおり,ストレッチが破綻する場合やジオメトリコストが増加するなどといった問題点があり,このままの方法ではいろいろと問題があるので注意してください。 ★ Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。 プログラムの作成にはMicrosoft Visual Studio 2005 SP1 Professional, Cg Toolkit 2.0 Decemberを用いています。 |