レイヤーの深さを維持しながら2Dでステンシルを使用しようとしています


7

これは、参照フレームを取得できるように、現在行われていることの画面です。

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

私が遭遇している問題は、描画呼び出し中に実行しているテクスチャスワップの量が原因で、ゲームの速度が低下することです。タイルの描画ごとに、壁、キャラクター、床、およびすべてのオブジェクトがそれらのタイプを含むそれぞれのスプライトシート上にあるため、読み込まれたテクスチャは、適切にレイヤー化するためにスプライトを循環して描画するときに、3〜5回以上スワップします。

今、私はすべての一般的なオブジェクトをそれぞれのリストにまとめ、layerDepthを使用してそれらをそのように描画してみましたが、私が直面している新しい問題はドア/ウィンドウの方法に関係しています壁に描かれています。つまり、ステンシルを使用して、ドア/窓の形状で描画された壁のブロックを一掃し、壁が描画されるときにドア/窓のサイズの穴ができるようにしました。

これは、一般的なオブジェクトをグループ化するのではなく、タイルごとに壁に描画を設定する方法です。

  1. 最初に、ドア/窓がこの壁にあるかどうかを確認します。そうでない場合は、すべての手順をスキップして、通常どおりに描画します。さもないと
  2. 現在のspriteBatchを終了する
  3. すでに描画されたものを保持するために、透明色でバッファをクリアします
  4. ステンシル設定で新しいスプライトバッチを開始する
  5. ドアエリアを描く
  6. spriteBatchを終了する
  7. 以前に設定されたステンシルを考慮した新しいスプライトバッチを開始します
  8. 穴を開けて描かれる壁を描きます
  9. そのスプライトバッチを終了
  10. タイルの描画を続行するには、通常の設定で新しいスプライトバッチを開始します

タイルごとの描画では、どの描画を何の上に描画するかをlayerDepthを使用して整理していないため、深度/ステンシルバッファをクリアすることは重要ではありませんでした。

タイルごとではなく、一般的なオブジェクトのリストから描画しているので、描画呼び出しが大幅に高速化されましたが、ステンシルシステムがドアや窓の領域を隠すための方法を理解できないようです。壁に引き込まれます。問題の根本は、DepthStencilStateを変更するためにspriteBatchを終了すると、現在のRenderTargetがフラットになり、線の下に描画されたものの深度ソートがなくなるということです。つまり、ステンシルはドアや窓のある壁ごとに1回発生する必要があるため、ゲームの世界での奥行きや位置に関係なく、壁は常にすべての上に描画されます。

誰かがこれを回避する方法を知っていますか?簡単に言うと、特定のスプライトの一部をステンシル/マスキングしながら、レイヤーの深さでソートしたものを描画する方法が必要です。


実際の回答を投稿するのに十分なXNAについては知りませんが、これは疑わしいようです。「spriteBatchを終了すると、現在のRenderTargetがフラット化され、深度の並べ替えが行われなくなります」。スプライトが実際に深度バッファーに描画されている場合、これは発生しません(これが「layerDepth」が行うことを想定しています)。深度ステンシルの状態を変更しても、深度バッファーをクリアする必要はありません。したがって、その動作を変更する方法があるかどうかを調べる価値があるかもしれません。
Nathan Reed

「新しいspritebatchを開始する」と言っても、新しいSpriteBatchインスタンスを作成しているわけではありませんか。何かを描くたびに新しいバッチを作成すると、ゲームがかなり遅くなります。
Mike Cluck

回答:


1

適切な解決策を提供するのに十分なXNAを知りませんが、一般的な答えを与えることができます。

あなたが抱えている問題は、画家のアルゴリズムです。具体的には、描きたいものをすべて描く前に、描きたいものをすべて注文する必要があります。この問題を回避する最も一般的な方法は、深度バッファを使用することです。興味深いことに、これはZ値をカメラ空間に格納するため、Zバッファーとも呼ばれます。

ただし、Zがないため(2Dゲームを作成しているため)、この手法は使用できません。それともできますか?

次の点を考慮してください。正投影では、3Dポイントのポイントの遠近法は変わりません。常に同じようにしたいので、これはユーザーインターフェイス要素などに役立ちます。

私が提案しているのは、3Dマトリックスと頂点ごとの深度値を取るカスタムシェーダーを作成することです。この深度値は深度バッファリングに使用する必要がありますが、投影には使用しないでください。

疑似シェーダーコードでは:

[vertex shader]
pos_output = TransformOrthographic * TransformView * vec3(pos_vertex.x, pos_vertex.y, 0.0);
depth_output = depth_vertex;

[fragment shader]
depth_output = (depth_input - depth_min) / (depth_max - depth_min);

これを設定したら、オブジェクトが間違った順序でレンダリングされることを心配する必要がなくなり、代わりに深度値を設定できます。

これで、画面の一部をステンシルアウトすることを心配することなく、1回の描画呼び出しですべてをレンダリングできるはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.