エンジンレンダリングパイプライン:シェーダーを汎用にする


10

OpenGL ES 2.0(現在のところiOS)を使用して2Dゲームエンジンを作成しようとしています。私は、Objective Cでアプリケーション層を記述し、C ++で別の自己完結型のRendererGLES20を記述しました。レンダラーの外部ではGL固有の呼び出しは行われません。完全に機能しています。

しかし、シェーダーを使用する際にいくつかの設計上の問題があります。各シェーダーには、メインの描画呼び出しの直前に設定する必要のある独自の固有の属性とユニフォームがあります(この場合はglDrawArrays)。たとえば、いくつかのジオメトリを描画するには、次のようにします。

void RendererGLES20::render(Model * model)
{
    // Set a bunch of uniforms
    glUniformMatrix4fv(.......);
    // Enable specific attributes, can be many
    glEnableVertexAttribArray(......);
    // Set a bunch of vertex attribute pointers:
    glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, m->pCoords);

    // Now actually Draw the geometry
    glDrawArrays(GL_TRIANGLES, 0, m->vertexCount);

    // After drawing, disable any vertex attributes:
    glDisableVertexAttribArray(.......);
}

ご覧のとおり、このコードは非常に厳格です。別のシェーダー、たとえばリップルエフェクトを使用する場合、追加のユニフォーム、頂点属性などを渡す必要があります。つまり、新しいシェーダーを組み込むためだけに、RendererGLES20レンダーソースコードを変更する必要があります。

シェーダーオブジェクトを完全に汎用化する方法はありますか?シェーダーオブジェクトを変更したいだけで、ゲームソースの再コンパイルを心配しない場合はどうでしょうか。レンダラーにユニフォームや属性などを認識させない方法はありますか?データをユニフォームに渡す必要がある場合でも、それを行うのに最適な場所はどこですか?モデルクラス?モデルクラスはシェーダー固有のユニフォームと属性を認識していますか?

以下は、Actorクラスを示しています。

class Actor : public ISceneNode
{
    ModelController * model;
    AIController * AI;
};

モデルコントローラークラス:クラスModelController {クラスIShader *シェーダー; int textureId; vec4ティント。浮動小数点アルファ; struct Vertex * vertexArray; };

Shaderクラスには、シェーダーオブジェクト、サブルーチンのコンパイルとリンクなどが含まれます。

Game Logicクラスでは、実際にオブジェクトをレンダリングしています。

void GameLogic::update(float dt)
{
    IRenderer * renderer = g_application->GetRenderer();

    Actor * a = GetActor(id);
    renderer->render(a->model);
}

ActorはISceneNodeを拡張していますが、まだSceneGraphの実装を開始していないことに注意してください。この問題を解決したらすぐにそれを行います。

これを改善する方法はありますか?関連デザインパターンなど?

質問を読んでいただきありがとうございます。



それが完全に重複しているかどうかはわかりませんが、それはあなたがやろうとしていることの本質に到達すると思います。
Sean Middleditch 2013

回答:


12

シェーダーシステムをよりデータ駆動型にすることは可能であり、ユニフォームや頂点フォーマットを配置するシェーダー固有のコードはそれほど多くなく、シェーダーにアタッチされたメタデータに基づいてプログラムで設定することができます。

まず免責事項:データ駆動型システムでは、新しいシェーダーを簡単に追加できますが、一方で、システムの複雑さが増すという点でコストがかかるため、保守とデバッグが困難になります。したがって、データ主導性がどれだけ役立つかを慎重に検討し(小規模なプロジェクトの場合は、答えが「なし」になる可能性がある)、過度に一般化されたシステムを構築しようとしないでください。

さて、まず頂点フォーマット(属性)について話しましょう。渡すglVertexAttribPointer属性を含む構造体(単一の属性のインデックス、タイプ、サイズなど)を作成し、頂点フォーマット全体を表すこれらの構造体の配列を作成することにより、データ記述を作成できます。この情報があれば、頂点属性に関連するすべてのGL状態をプログラムで設定できます。

この説明を入力するデータはどこから取得されますか?概念的には、最もクリーンな方法は、シェーダーに属させることです。メッシュの頂点データを作成する場合、メッシュで使用されているシェーダーを調べ、そのシェーダーに必要な頂点フォーマットを見つけ、それに応じて頂点バッファーを作成します。各シェーダーが期待する頂点フォーマットを指定する方法が必要です。

これにはさまざまな方法があります。たとえば、シェーダー内の属性の名前の標準セット( "attrPosition"、 "attrNormal"など)に加えて、 "position is 3 floats"などのハードコードされたルールを設定できます。次に、glGetAttribLocationまたはを使用して、シェーダーが使用する属性を照会し、ルールを適用して頂点フォーマットを構築します。別の方法は、フォーマットを定義するXMLスニペットを、シェーダーソースのコメントに埋め込み、ツールによって抽出する、またはそれらの行に沿って何かを行うことです。

ユニフォームの場合、OpenGL 3.1以降を使用できる場合は、ユニフォームバッファーオブジェクト(D3Dの定数バッファーに相当するOpenGL相当)を使用することをお勧めします。残念ながら、GL ES 2.0にはこれらがないため、ユニフォームは個別に処理する必要があります。これを行う1つの方法は、設定する各パラメーター(カメラマトリックス、スペキュラーパワー、ワールドマトリックスなど)の均一な位置を含む構造体を作成することです。サンプラーの位置もここにあります。このアプローチは、すべてのシェーダー間で共有されるパラメーターの標準セットがあることに依存しています。すべてのシェーダーがすべてのパラメーターを使用する必要はありませんが、すべてのパラメーターがこの構造体に含まれている必要があります。

各シェーダーにはこの構造体のインスタンスがあり、シェーダーをロードするときにglGetUniformLocation、標準化された名前を使用して、すべてのパラメーターの場所を照会します。その後、コードからユニフォームを設定する必要があるときはいつでも、それがそのシェーダーに存在するかどうかを確認し、その場所を調べて設定することができます。

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