滑らかな2D照明効果を実現するにはどうすればよいですか?


18

XNAで2Dタイルベースのゲームを作成しています。

現在、私の雷はこのように見えます。

このように見えるようにするにはどうすればよいですか?

各ブロックに独自の色合いがある代わりに、滑らかなオーバーレイがあります。

ある種のシェーダーを想定しており、周囲のタイルの照明値をシェーダーに渡すことを想定していますが、私はシェーダーの初心者なので、よくわかりません。

私の現在の照明は光を計算し、次にそれをaに渡しSpriteBatch、色合いパラメーターで描画します。各タイルにはColor、色合いに使用される照明アルゴリズムで描画する前に計算されるがあります。

これが現在どのようにライティングを計算するかの例です(左、右、下からもこれを行いますが、このフレームごとに行うのは本当にうんざりしました...)

ここに画像の説明を入力してください

したがって、実際にこれまでに光を取得して描画することは問題ありません

霧の霧とグラデーション円形オーバーレイを使用して滑らかな稲妻を作成する無数のチュートリアルを見てきましたが、すでに各タイルに稲妻値を割り当てる素晴らしい方法があります。

レビューする

  • 照明の計算(完了)
  • タイルの描画(完了、シェーダー用に変更する必要があることはわかっています)
  • シェードタイル(値を渡し、「グラデーション」を適用する方法)

回答:


6

このようなものはどうですか?

タイルスプライトを着色して照明を描画しないでください。点灯しいないタイルをレンダーターゲットに描画し、タイルライトを2番目のレンダーターゲットに描画します。各タイルターゲットは、タイルの領域を覆うグレースケールの四角形として表されます。最終シーンをレンダーするには、シェーダーを使用して2つのレンダーターゲットを組み合わせ、2番目の値に応じて最初のピクセルを暗くします。

これはあなたが今持っているものを正確に生成します。それは役に立たないので、少し変更しましょう。

ライトマップレンダーターゲットの寸法を変更して、各タイルが単一の長方形の領域ではなくピクセルで。最終シーンを合成するときは、線形フィルタリングでサンプラーステートを使用します。それ以外の場合は、他のすべてを同じままにします。

シェーダーを正しく記述したと仮定すると、合成中にライトマップは効果的に「スケールアップ」されるはずです。これにより、グラフィックデバイスのテクスチャサンプラーを介して素敵なグラデーション効果が無料で得られます。

また、シェーダーを切り取って、「暗くする」BlendStateでより簡単にこれを行うこともできますが、詳細を説明する前に、実験する必要があります。

更新

今日は実際にこれをモックする時間がありました。上記の答えは、すべてに対する最初の答えとしてシェーダーを使用する私の習慣を反映していますが、この場合、それらは実際には必要ではなく、それらの使用は不必要に物事を複雑にします。

私が提案したように、カスタムBlendStateを使用してまったく同じ効果を達成できます。具体的には、このカスタムBlendState:

BlendState Multiply = new BlendState()
{
    AlphaSourceBlend = Blend.DestinationAlpha,
    AlphaDestinationBlend = Blend.Zero,
    AlphaBlendFunction = BlendFunction.Add,
    ColorSourceBlend = Blend.DestinationColor,
    ColorDestinationBlend = Blend.Zero,
    ColorBlendFunction = BlendFunction.Add
}; 

ブレンド方程式は

result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)

カスタムBlendStateを使用すると、

result = (lightmapColor * destinationColor) + (0)

つまり、純粋な白(1、1、1、1)のソースカラーは目的色を保持し、純粋な黒(0、0、0、1)のソースカラーは目的色を純粋な黒に暗くし、中間のグレーのシェードは、中間の量だけ目的の色を暗くします。

これを実行するには、まず、ライトマップを作成するために必要なことをすべて実行します。

var lightmap = GetLightmapRenderTarget();

次に、通常どおりバックライトに照明のないシーンを直接描画します。

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();

次に、カスタムBlendStateを使用してライトマップを描画します。

var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;

spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();

これにより、デスティネーションの色(点灯していないタイル)にソースの色(ライトマップ)が掛けられ、点灯していないタイルが適切に暗くなり、ライトマップテクスチャが必要なサイズに拡大された結果としてグラデーション効果が作成されます。


これは非常に簡単に思えますが、後で何ができるかがわかります。(私は他のすべての上にライトマップをレンダリングし、ブラーシェーダを使用し、私はそれは紫色のオーバーレイを持っていたが、私はそれがactuallタイル層に至るまで見たかったレンダーターゲットは「透明」で作るcouldntの)前に私が何かsimlarを試してみました
Cyral

デバイスにレンダーターゲットを設定した後、device.Clear(Color.Transparent);を呼び出します。
コールキャンベル

ただし、この場合、ライトマップはおそらく白または黒にクリアする必要があることを指摘する価値がありますが、それらはそれぞれ完全な明るさと完全な暗さです。
コールキャンベル

前に試したことはそれだと思いますが、まだ紫色でした。明日、これに取り組む時間があるときに調べます。
Cyral

これはほとんどすべてうまくいきましたが、黒から透明ではなく黒から白にフェードします。シェーダーの部分は、私が混乱しているものです。元の照明のないシーンを新しいシーンに暗くするために1つ書く方法です。
Cyral

10

さて、簡単で滑らかな2D稲妻を作成し、3つのパスでレンダリングする非常に簡単な方法を次に示します。

  • パス1:雷のないゲームの世界。
  • パス2:世界のない雷
  • 画面に表示される1.および2.パスの組み合わせ。

パス1では、すべてのスプライトと地形を描画します。

パス2では、光源であるスプライトの2番目のグループを描画します。次のようになります。
ここに画像の説明を入力してください
なります。レンダーターゲットを黒で初期化し、それらのスプライトを最大または加算ブレンディングで描画します。

パス3では、前の2つのパスを組み合わせます。それらを組み合わせる方法は複数あります。ただし、最も単純で芸術的な方法は、Multiplyを使用してそれらをブレンドすることです。これは次のようになります。
ここに画像の説明を入力してください


事は、私はすでに照明システムを持っています、そして、いくつかのオブジェクトが通過するために光を必要とするものとそうでないために、ここでポイントライトは動作しません。
サイラル

2
まあ、それはもっとトリッキーです。シャドウを得るにはレイキャストする必要があります。Terariaは地形に大きなブロックを使用しているため、問題はありません。不正確なレイキャスティングを使用することもできますが、それはテラリアよりも良く見えず、グラフィックスをブロックしていない場合はさらに適合しない可能性があります。高度で見栄えの良い技術については、この記事があります:gamedev.net/page/resources
API-Beast

2

投稿した2番目のリンクは は、ある種の戦争霧のが、これに関する他の質問がいくつかあります

それは私には テクスチャのます。プレーヤーが前進するにつれて、黒いオーバーレイのビットを「消去」します(ピクセルのアルファを0に設定することにより)。

プレイヤーが探索した場所でブラシタイプの「消去」を使用したに違いないと思います。


1
それは戦争の霧ではなく、各フレームの稲妻を計算します。私が指摘したゲームはTerrariaに似ています。
サイラル

私は何を意味することは、戦争の霧に似て実装されるかもしれないです
bobobobo

2

タイルの代わりに明るい頂点(タイル間の角)。4つの頂点に基づいて、各タイル全体に照明をブレンドします。

頂点に視線テストを実行して、それがどの程度照明されているかを判断します(ブロックですべてのライトを停止するか、ライトを減らすことができます。たとえば、純粋なバイナリ可視/非可視テスト)。

タイルをレンダリングするときに、各頂点のライト値をGPUに送信します。フラグメントシェーダーを簡単に使用して、タイルのスプライトの各フラグメントで補間されたライト値を取得し、各ピクセルをスムーズに照らすことができます。GLSLに触れてから実際のコードサンプルを提供するのは気が進まないので十分に長いのですが、擬似コードでは次のように簡単になります。

texture t_Sprite
vec2 in_UV
vec4 in_Light // coudl be one float; assuming you might want colored lights though

fragment shader() {
  output = sample2d(t_Sprite, in_UV) * in_Light;
}

頂点シェーダーは、入力値をフラグメントシェーダーに渡すだけでよく、リモートでも複雑ではありません。

より多くの頂点でさらに滑らかな照明が得られます(たとえば、各タイルが4つのサブタイルとしてレンダリングされる場合)が、目的の品質によっては努力する価値がない場合があります。最初に簡単な方法で試してください。


1
line-of sight tests to each vertex?それは高価になります。
ashes999

ある程度の賢さで最適化できます。2Dゲームでは、厳密なグリッドに対して驚くほど多くの光線テストを非常に迅速に行うことができます。まっすぐなLOSテストの代わりに、レイテストも実行するのではなく、頂点上のライト値を「フロー」することができます(私は今のところ適切な用語を考えられません。ビールの夜)。
ショーンミドルディッチ

視線テストを使用すると、これがさらに複雑になります。照明はすでにわかっています。さて、4つの頂点をシェーダーに送信するとき、どうすればよいですか?実際のコードサンプルを提供するのは気が進まないことは承知していますが、もう少し情報が得られればいいと思います。また、質問の詳細を編集しました。
サイラル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.