要するに、記事の本質は次のように説明できます。

以下は、即興手段によるゲームでの照明の実装の小さな歴史です。
ご存知のように、彼らは服で迎えられ、チームがそのアートディレクターやアーティストさえもいない場合、普通のプログラマーはさまざまな方法で回避しなければなりません。
ゲームが下の写真のようになった瞬間、視覚的に、より多様で活気のある写真を作成し、同時にプログラマーのスキルを活用するものを追加する必要があることが明らかになりました。

作業開始時の技術的条件は次のとおりです。
-2012年
-XNA Framework 4.0の更新。 シェーダーの使用を許可しないリッチプロファイル。
-Windows Phone 7ベースの携帯電話:Nokia Lumia 800(2011)
-すべてが電話で60fpsを提供し、ゲームロジックの残り(AI、物理学、音楽)に十分なマージンを残す必要があります。
容量が限られているのは私なので、可能な限り保存する必要がありました。
行こう!
0日目。ゲーム内の照明のプロトタイプまず、アイデア自体をテストするために、手で照明を描くことにしました。 これは最小限の作業です。
- 地図を撮影し、ペイントで手動で光と影を描きます
- 結果のテクスチャをいわゆるライトマップとして使用します
- 適切な混合モードを選択します。
誰もが興味を持っている場合、私は次のパラメータでシンプルな、露出オーバーでないブレンドモードを使用しましたColorSourceBlend = Blend.Zero、
AlphaSourceBlend = Blend.Zero、
ColorDestinationBlend = Blend.SourceColor、
AlphaDestinationBlend = Blend.SourceColor、
ColorBlendFunction = BlendFunction.Add、
AlphaBlendFunction = BlendFunction.Add、
出力は次のようになります。

この画面ではそれほどはっきりしていませんが、それでも見た目が楽しくなりました。 それで決定しました、私たちは照明をやっています。
1日目:単純な静的シャドウゲームは基本的に2Dであり、カメラはほとんど常に同じ角度を向いているため、最も単純で静的な照明を作成します。

レベルをロードすると、レベルの上に描画されるライティングテクスチャが生成されます。ゲームは「ほぼ」2dであるため、ジオメトリをスキャンする必要はありません。 3Dジオメトリはすべて静的であるため、その照明は頂点の色で「焼き付け」られます。
ライトテクスチャバッファの生成は非常に簡単です。
光源ごとに:
- 一時バッファをクリアします
- 照明のテクスチャを一時バッファー(光源のカラーブレンドを使用した通常のグラデーションサークル)に描画し、照明領域に落ちる障害物に完全に黒い影を適用します
- 結果の一時バッファーは、一般的な照明バッファーと混合されます(通常の添加剤ブレンドを使用)
結果は、厳しいものではありますが興味深いものです。
2日目:ペナンブラを追加通常、光源は点光源ではありません。これは、光源からの影がはっきりしていないことを意味し、さらに光源からの距離が長くなるにつれて、ますますぼやける傾向があります。
ここでは、優れたFEARゲームがアイデアを刺激し、各光源について、わずかなオフセットで、より正確には光源に対して回転することにより、ライトマップが数回描画されます。
3日目:滑らかな影ここでは、ライティングの原因となるテクスチャを少しぼかしました。
ゲームはモバイルであり、シェーダーは非常に限られているため、ハードウェア補間機能を使用することが決定されました。
少し滑らかな影を作成するには:
- 元のテクスチャを小さなスケール(1 / 2、1 / 4など)で異なるバッファーに数回描画します
- 加算ブレンドモードとアルファ1 / Nを使用して、これらすべてのバッファーを適切なスケール(テクスチャ½サイズの場合は2、テクスチャ¼サイズの場合は4など)と混合します(Nはバッファーの数)

アイデアは、最初の影のシャープさと部分的な影のぼかしをさらに強調するために、より「知的に」混合することでした。 しかし、最後の段落からの単純なミキシング+部分的なシェードの結果でさえ十分であると思われ、このオプションに決めました。
4日目:オクルージョンシャドウ壁のセルフシャドーイングのような錯覚を作成するために、距離マップを生成するのに役立つ別のテクスチャを使用する必要がありました(低解像度のため)。最も近い壁までの距離が各セルに記録されるマップ。
たとえば、壁が赤で表示されているレベルの物理マップは次のとおりです。

レベルマップ+距離グリッド(青-壁が近い、白-壁が遠い):

マップ+オクルージョンシャドウ:

このテクスチャでは、ピクセルの色は単純なルールに従って選択されました。最も近い壁までの距離が特定のしきい値を超える場合-透明色、そうでない場合は黒。 テクスチャは小さいため(再生セル1つにつき1ピクセル〜1.5m)、テクスチャが増加すると、ハードウェア補間によって色間の滑らかな遷移が提供されます(約50倍に伸びます)。 また、ゲーム内のすべての壁は正方形で、厳密にグリッド上に配置されているため、テクスチャのサイズが小さくても視覚的なアーティファクトは発生しません。

またはゲーム内:

ご覧のとおり、違いは目立ちませんが、写真は深みを増しているようです。
5日目。ダイナミックシャドウ静的な影は優れており、動的な影は優れています。 それは、多くのリソースを費やしているだけで、彼ら自身もマシンの欲望もそうではありませんでした。 アイデアは、ダイナミックシャドウ1つにつき1〜2個のスプライトを使用し、オブジェクトと光源の相対的な位置に応じて、角度とスケールのみを変更することでした。 そして、すべてのゲームオブジェクトが長方形であるという事実により、このすべての計算はそれほど複雑ではありません。 光線のサイズを追跡する必要はありません。 影はおおよそのものであるため、光源からオブジェクトの中心までのビームに垂直な軸に、長方形全体の投影に等しい幅の長方形の影を描画するだけで十分です(
下のスクリーンショットで赤で強調表示されています)。

そして、円錐を取得するには、オブジェクトの角度サイズに基づいて回転する2つのスプライトを描画します。

シャドウスプライトには、グラデーション付きの4x4ピクセルテクスチャが使用されました(赤い点が回転の中心です)。

その結果、次のような結果が得られます。

テクスチャの勾配により、部分的な陰影が得られ、通常のアルファブレンディングで2つが描画されるため、中央に飽和した影ができます。

そして、例として、静的シャドウと動的シャドウを比較します:

小さなトリック:
1.影は単純化されており、壁を考慮していないため、壁を「貫通」しないように注意する必要があります。 この場合も、距離グリッドが役立ちました。 各オブジェクトの最大シャドウ長は、距離グリッドの値+最小壁サイズによって制限されていました。 もちろん、これは壁の近くのこれらの影の正確な振る舞いを導きませんが、この効果は種の人工物よりも目立ちません。

2.光源から少し離れると、角度サイズが大きくなりすぎて、2つのレンダリングされたテクスチャが「影をシミュレート」して壊れることがありません。 2つのオプションがあります。レンダリングの数を増やすか、特定の角度を超えた場合、影が完全に消えるまで透明にします。 リソースの観点からより経済的な2番目のオプションを選択しました。
3.光源から遠い距離では、レンダリング時に2つのシャドウテクスチャが実際にマージされるため、オブジェクトの特定の角度サイズを超える場合は、x2アルファの1つの描画で十分です。
4.ご覧のとおり、この影の実装は1つの光源でしか適切に機能しないため、さらに光源がある場合は.... 影を見せないでください。
5. 4の帰結そのような影の光源は常に1つなので、影が急激に変化するという不快な効果は、影が変化する(または消える)ときに発生します。 これを取り除くには、スムーズなトランジションを追加するだけで十分です。つまり、古いシャドウはしばらくの間透明になり、新しい(必要に応じて)反対に、完全な透明から表示されます。 ゲームは動的であるため、このような遷移はほとんどの場合、不自然さで特に注目されません。
6日目。汚れたレンズの影響最後の仕上げは、フルスクリーンの「ダーティレンズ」効果の追加でした。
これは、シェーダーへのフルアクセスがなく、パフォーマンスを維持する必要があるため、それほど単純ではないことがわかりました。
方法#1は迅速かつ簡単です。
汚れたガラスの質感を取り入れ、明るい領域に現れるブレンドモードを使用しました。
ブレンドファッションの例ColorSourceBlend = Blend.DestinationColor、
AlphaSourceBlend = Blend.DestinationColor、
ColorDestinationBlend = Blend.One、
AlphaDestinationBlend = Blend.One、
ColorBlendFunction = BlendFunction.Add、
AlphaBlendFunction = BlendFunction.Add、
また、この方法は高速で、状況によっては希望する画像を取得できますが:

多くの場合、結果は悲しいものでした:

この動作の理由は理解できます。エリアの実際の照明を考慮せず、ピクセルの色を使用するだけです。 したがって、コントラスト照明のカードでは、すべてが多かれ少なかれ見えますが、明るい照明ではすべてがひどいです。
方法2は遅いが美しい。
カメラ投影ですべての光源(より小さく、影を考慮しない)からのすべての光点をバッファーに描画し、方法#1のブレンドモードでダーティガラステクスチャを描画します。 その後、受信バッファをすでに使用しています。

ただし、フレームごとに追加のバッファーをレンダリングするのは高速とはほど遠いです。 個別のバッファの利点は、フレームごとに更新することはできませんが、カメラが移動したときにのみ更新できますが、そのような最適化を行った後でも、モバイルデバイスのパフォーマンスは望みにはほど遠いものでした。
方法#3-高速で美しい
シェーダーへの完全なアクセス権はありませんでしたが、1つのプリインストールされたデュアルテクスチャシェーダーへのアクセス権がありました。 乗算(より正確には、Modulate2Xブレンドモード
blogs.msdn.com/b/shawnhar/archive/2010/08/04/dualtextureeffect.aspx )により、テクスチャ座標を考慮して2つのテクスチャを混合します。 最初のテクスチャは、私たちにとって興味のあるすべての光点を含む準備されたテクスチャでした(ゲームは本質的に2dであるため、レベルのために一度準備すれば十分です)、2番目は汚れたガラスです。 フレームごとに更新する必要があるのは、最初のテクスチャのテクスチャ座標だけです。 これらは、画面をテクスチャ1の座標に投影することで計算されます(これらはスケール付きのワールド座標です)。

実際、最終結果は方法2と変わりませんが、バッファへの追加の描画は必要ありません。
合計:したがって、最終フレームには次のものが必要でした。
- A)カードの開始時に
- 静的照明マップを計算する
- オクルージョンシャドウのシェーディングマップを計算する
- 汚れたレンズの影響のために明るい点のバッファーを準備する
- ダイナミックシャドウのすべてのポイントに最も近い光源のキャッシュを準備します
- B)フレームごと
- 静的な照明マップを描く
- 動的な影の角度と幅を計算し、オブジェクトに1-2のスプライトを描画します
- 汚れたレンズの影響のために明るい点のバッファーを準備する
- 4点をワールド座標に投影し、テクスチャ座標を更新し、ダーティレンズエフェクト用のデュアルテクスチャシェーダで1つのテクスチャを描画します
