被写界深度のポストプロセスを作成しようとしていますが、どこから始めればよいのかわかりません(現在のレンダリング深度マップを除く)。そのためのすべてのチュートリアルは、XNA3.1用であるか、実際には説明を提供しないか、または本の一部です。
では、DOFがどのようにレンダリングされるかについて、詳細なステップバイステップのプロセスを実行できますか?
これは、Reachプロファイル内でXNAが提供する「すぐに使える」機能を使用して基本的な近似を実現する方法についての説明です。
組み込みのものを使用してC#でそれを行う方法を理解したら、HLSLでそれを達成することがもう少し明白になることを願っています。
また、Windows Phone 7用のゲームを作成したい場合は、どこから始めればよいでしょう(Windows Phone 7は現時点ではカスタムシェーダーをサポートしていないため)。
最初に、外観を生成するために必要なビットとピースを保持するためのインスタンスレベル変数を定義します。
BasicEffect effect;
List<Matrix> projections;
List<RenderTarget2D> renderTargets;
SpriteBatch spriteBatch;
次に、LoadContent()メソッドで、それらのロードを開始します。最終シーンのレンダリングに使用するSpriteBatchから始めます。
spriteBatch = new SpriteBatch(GraphicsDevice);
BasicEffectのインスタンスが続きます:
effect = new BasicEffect(GraphicsDevice);
effect.EnableDefaultLighting();
effect.DiffuseColor = Color.White.ToVector3();
effect.View = Matrix.CreateLookAt(
Vector3.Backward * 9 + Vector3.Up * 9,
Vector3.Zero,
Vector3.Up);
effect.World = Matrix.Identity;
effect.Texture = Content.Load<Texture2D>("block");
effect.TextureEnabled = true;
effect.EnableDefaultLighting();
ここでは、基本効果の構成方法の詳細は重要ではありません。レンダリングする効果があるというだけです。
次は、いくつかの射影行列が必要になります。
projections = new List<Matrix>() {
Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(60f),
GraphicsDevice.Viewport.AspectRatio,
9f,
200f),
Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(60f),
GraphicsDevice.Viewport.AspectRatio,
7f,
10f),
Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(60f),
GraphicsDevice.Viewport.AspectRatio,
0.2f,
8f)};
各投影の最後の2つのパラメータを調べると、ここで効果的に行っているのは、世界を「チャンク」に分割し、各チャンクがカメラからのさまざまな距離をカバーしていることに気付くでしょう。
たとえば、9ユニットを超えるすべてのもの、カメラから7ユニットから10ユニットまでのすべて、そして最後に8ユニットより近いもの。
(シーンに応じてこれらの距離を微調整する必要があります。わずかなオーバーラップに注意してください)
次に、いくつかのレンダリングターゲットを作成します。
var pp = GraphicsDevice.PresentationParameters;
renderTargets = new List<RenderTarget2D>()
{
new RenderTarget2D(GraphicsDevice,
GraphicsDevice.Viewport.Width / 8,
GraphicsDevice.Viewport.Height / 8,
false, pp.BackBufferFormat, pp.DepthStencilFormat),
new RenderTarget2D(GraphicsDevice,
GraphicsDevice.Viewport.Width / 4,
GraphicsDevice.Viewport.Height / 4,
false, pp.BackBufferFormat, pp.DepthStencilFormat),
new RenderTarget2D(GraphicsDevice,
GraphicsDevice.Viewport.Width,
GraphicsDevice.Viewport.Height,
false, pp.BackBufferFormat, pp.DepthStencilFormat),
};
各レンダーターゲットは、前述の「チャンク」に対応します。非常に単純なブラー効果を実現するために、各レンダリングターゲットは異なる解像度に設定され、「最も遠い」チャンクは低解像度で、最も近いチャンクは高解像度です。
Draw()メソッドにジャンプすると、シーンチャンクをレンダリングできます:(各チャンクの背景をレンダリングしないように注意してください)
effect.Projection = projections[0];
GraphicsDevice.SetRenderTarget(renderTargets[0]);
GraphicsDevice.Clear(Color.Transparent);
// render scene here
effect.Projection = projections[1];
GraphicsDevice.SetRenderTarget(renderTargets[1]);
GraphicsDevice.Clear(Color.Transparent);
// render scene here
effect.Projection = projections[2];
GraphicsDevice.SetRenderTarget(renderTargets[2]);
GraphicsDevice.Clear(Color.Transparent);
// render scene here
GraphicsDevice.SetRenderTarget(null);
これで、シーンが分割され、距離によってぼやけてしまいました。あとは、シーンを再結合して最終的な画像を作成するだけです。
最初のステップでは、(素晴らしい)背景をレンダリングします。
GraphicsDevice.Clear(Color.CornflowerBlue);
次に、各チャンクをさらに近いものから最も近いものへとレンダリングします。
spriteBatch.Begin(
SpriteSortMode.Deferred,
BlendState.AlphaBlend,
SamplerState.AnisotropicClamp,
null, null);
spriteBatch.Draw(renderTargets[0], GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.Draw(renderTargets[1], GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.Draw(renderTargets[2], GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.End();
そしてビオラ!ことわざの端の周りは少し荒いですが、被写界深度の近似値があります。
これで、Reachプロファイルの範囲内にとどまる予定の場合は、各チャンクを複数の解像度でレンダリングし、Additive BlendStateなどを使用して結果の画像を結合することにより、ぼかし効果を向上させることができます。
一方、HiDefプロファイルでカスタムシェーダーを作成することを計画している場合、概念はほぼ同じですが、実行方法が変更されます。
たとえば、低解像度のレンダリングをより本格的なガウススタイルのブラーに交換する...または...チャンクのコースグレインのアイデアを捨てて、深度マップに基づいた比較的細かいグレインのブラー方法に移行します。