D3D9およびOpenGL頂点データ管理のためのより良い抽象化レイヤーとは何ですか?


8

私のレンダリングコードは常にOpenGLでした。OpenGLがないプラットフォームをサポートする必要があるため、OpenGLとDirect3D 9をラップする抽象化レイヤーを追加する必要があります。Direct3D11は後でサポートします。

TL; DR: OpenGLとDirect3Dの違いにより、プログラマーに冗長性が生じ、データレイアウトが不安定に感じられます。

今のところ、私のAPIは少しこのように動作します。これがシェーダーの作成方法です。

Shader *shader = Shader::Create(
    " ... GLSL vertex shader ... ", " ... GLSL pixel shader ... ",
    " ... HLSL vertex shader ... ", " ... HLSL pixel shader ... ");
ShaderAttrib a1 = shader->GetAttribLocation("Point", VertexUsage::Position, 0);
ShaderAttrib a2 = shader->GetAttribLocation("TexCoord", VertexUsage::TexCoord, 0);
ShaderAttrib a3 = shader->GetAttribLocation("Data", VertexUsage::TexCoord, 1);
ShaderUniform u1 = shader->GetUniformLocation("WorldMatrix");
ShaderUniform u2 = shader->GetUniformLocation("Zoom");

ここにはすでに問題があります。Direct3Dシェーダーがコンパイルされると、名前で入力属性をクエリする方法がなくなります。どうやら意味論だけが意味を保っています。これが、GetAttribLocationこれらの追加の引数があり、それらがに隠されている理由ShaderAttribです。

これが頂点宣言と2つの頂点バッファを作成する方法です。

VertexDeclaration *decl = VertexDeclaration::Create(
        VertexStream<vec3,vec2>(VertexUsage::Position, 0,
                                VertexUsage::TexCoord, 0),
        VertexStream<vec4>(VertexUsage::TexCoord, 1));

VertexBuffer *vb1 = new VertexBuffer(NUM * (sizeof(vec3) + sizeof(vec2));
VertexBuffer *vb2 = new VertexBuffer(NUM * sizeof(vec4));

別の問題:情報VertexUsage::Position, 0はセマンティクスに関心がないため、OpenGL / GLSLバックエンドにはまったく役に立たない。

頂点バッファがデータで満たされるか、またはポイントされると、これはレンダリングコードです。

shader->Bind();
shader->SetUniform(u1, GetWorldMatrix());
shader->SetUniform(u2, blah);
decl->Bind();
decl->SetStream(vb1, a1, a2);
decl->SetStream(vb2, a3);
decl->DrawPrimitives(VertexPrimitive::Triangle, NUM / 3);
decl->Unbind();
shader->Unbind();

あなたはそれが見declもう少しだけD3D-のような頂点宣言、それはちょっとレンダリングの世話をするにもよります。これはまったく意味がありますか?よりきれいなデザインは何でしょうか?または良いインスピレーションの源?


どのバージョンのOpenGLをターゲットにしていますか?
Nicol Bolas

@NicolBolas現在、OpenGL 2.1およびOpenGL ES 2.0を使用しており、OpenGL 3.3または4.0をサポートする予定ですが、以前のバージョンのサポートを終了するかどうかはまだ決定していません。私の現在の問題は...私はまた、次善のではなく、便利であるPS3、上の古いのOpenGLのサブセットを使用することである
サム・ホスバー

あなたはおそらくこれを既に認識しているが、彼らはそれをやったかを確認するために鬼のソースをチェックアウトogre3d.org
Aralox

4
@Aralox:OGREはシングルトンに感染した混乱であり、私はこれまで、誰も彼らの設計に従うように助言することは決してありません。
DeadMG 2012

回答:


8

あなたは基本的に、NVIDIA Cgをそのような魅力的なソフトウェアにするような状況に陥っています(使用していると言っているGL | ESをサポートしていないという事実は別です)。

また、実際にはglGetAttribLocationを使用しないでください。その機能は、GLを担当する人々が実際に優れたシェーディング言語がどのように機能するかを悩み始める前のGLSLの初期の頃からの悪いjujuです。時々使用されるため非推奨ではありませんが、一般的には、glBindAttibLocationまたは明示的な属性のロケーション拡張(GL 3.3以降のコア)を優先します。

シェーダー言語の違いに対処することは、GLとD3D間でソフトウェアを移植する上で最も難しい部分です。3.30より前のGLSLバージョンは明示的な属性の場所をサポートしていないため(HLSLの属性セマンティクスと同様)、GLSLバージョンより前のGLSLバージョンは、シェーダー言語の問題と見なすこともできます。 4.10 iircは明示的な統一バインディングをサポートしていません。

「最良の」アプローチは、シェーダーパッケージをカプセル化する高レベルのシェーディング言語ライブラリとデータ形式を用意することです。生のGLSL / HLSLの束を単純なShaderクラスに単純にフィードしないでください。また、あらゆる種類の健全なAPIを思い付くことができると期待してください。

代わりに、シェーダーをファイルに入れます。それらを少しのメタデータにまとめます。XMLを使用して、次のようなシェーダーパッケージを作成できます。

<shader name="bloom">
  <profile type="glsl" version="1.30">
    <source type="vertex"><![CDATA[
      glsl vertex shader code goes here
    ]]></source>
    <source type="fragment"><![CDATA[
      glsl fragment shader code goes here
    ]]></source>
  </profile>
  <profile type="hlsl" version="sm3">
    <source type="fx"><![CDATA[
      hlsl effects code goes here
      you could also split up the source elements for hlsl
    ]]></source>
  </profile>
</shader>

そのための最小限のパーサーを作成するのは簡単です(たとえば、TinyXMLを使用するだけです)。シェーダーライブラリでそのパッケージをロードし、現在のターゲットレンダラーに適切なプロファイルを選択して、シェーダーをコンパイルします。

また、必要に応じて、シェーダー定義の外部にソースを保持できますが、ファイルは保持できます。ソースの代わりに、ソースの代わりにファイル名を入れてください。これは、たとえばシェーダーをプリコンパイルする予定がある場合に役立ちます。

もちろん、今や難しい部分はGLSLとその欠陥に対処することです。問題は、属性の場所をHLSLセマンティクスのようなものにバインドする必要があることです。これは、APIでこれらのセマンティクスを定義し、GLSLプロファイルをリンクする前にglBindAttribLocationを使用することで実行できます。シェーダーパッケージフレームワークはこれを明示的に処理でき、グラフィックAPIが詳細を公開する必要はまったくありません。

これを行うには、GLSLプロファイルのいくつかの新しい要素を使用して上記のXML形式を拡張し、属性の場所を明示的に指定します。

<shader name="bloom">
  <profile type="glsl" version="1.30">
    <attrib name="inPosition" semantic="POSITION"/>
    <attrib name="inColor" semantic="COLOR0"/>
    <source type="vertex"><![CDATA[
      #version 150
      in vec4 inPosition;
      in vec4 inColor;

      out vec4 vColor;

      void main() {
        vColor = inColor;
        gl_Position = position;
      }
    ]]></source>
  </profile>
</shader>

シェーダーパッケージコードは、XMLのすべてのattrib要素を読み取り、それらから名前とセマンティクスを取得し、各セマンティクスの事前定義された属性インデックスを検索して、シェーダーをリンクするときに自動的にglBindAttribLocationを呼び出します。

その結果、APIは以前のGLコードよりも見栄えがよくなり、D3D11が許可するよりも少しクリーンになります。

// simple example, easily improved
VertexLayout layout = api->createLayout();
layout.bind(gfx::POSITION, buffer0, gfx::FLOATx4, sizeof(Vertex), offsetof(Vertex, position));
layout.bind(gfx::COLOR0, buffer0, gfx::UBYTEx4, sizeof(Vertex), offsetof(Vertex, color));

また、シェーダーパッケージ形式は厳密には必要ありません。物事をシンプルに保ちたい場合は、GLモードでname.vsおよびname.fs GLSLファイルを自動的に取得し、それらをコンパイルしてリンクするloadShader(const char * name)種類の関数を自由に使用できます。ただし、その属性メタデータは絶対に必要です。単純なケースでは、次のような特別に解析しやすいコメントでGLSLコードを補強できます。

#version 150

/// ATTRIB(inPosition,POSITION)
in vec4 inPosition;
/// ATTRIB(inColor,COLOR0)
in vec4 inColor;

out vec4 vColor

void main() {
  vColor = inColor;
  gl_Position = inPosition;
}

コメントの解析に慣れているので、気に入るはずです。HLSLスタイルのセマンティック宣言を完全に追加するなど、少数の専門エンジンが構文解析して変更するマイナー言語拡張を作成することもあります。解析の知識がしっかりしている場合は、それらの拡張宣言を確実に見つけ、追加情報を抽出して、テキストをGLSL互換コードに置き換えることができます。

どのようにしても、ショートバージョンは、不足している属性のセマンティック情報でGLSLを補強し、シェーダーローダーの抽象化でglBindAttribLocationを呼び出して修正し、簡単で効率的な最新のGLSLバージョンやHLSLに似せるようにします。


非常に包括的な答えをありがとう。セマンティックコメントに関する追加の提案は単純ですが、非常に理にかなっています!
sam hocevar 2012

他の人が非常に役に立ってくれたとしても、私はついにあなたの答えを受け入れます。私は適切にそれを行う方法を熟考するのに長い時間を費やし、サポートされていないときに明示的な属性の場所をエミュレートするのに役立つ完全なGLSL / HLSLパーサーを作成することになりました。
sam hocevar 2013年

5

まず、VertexBuffer<T>型の安全性を向上させるためにを使用することをお勧めしますが、次に、このレベルでは2つのAPIの違いが大きすぎると思います。個人的には、頂点宣言やシェーダー属性の設定などを処理しないインターフェースの背後にあるレンダラーを完全にカプセル化します。


出向; あなたの抽象化レイヤーは現在、レベルが低すぎるため、APIの違いに実際に対処するには、より高いレベルにする必要があります。
Maximus Minimus

2

個人的には、属性インデックスの標準化された規則を確立(および適用)します。GLインデックス0が位置です。GLインデックス1は色です。インデックス2は法線で、接線と従法線(必要な場合)は3と4です。インデックス5〜7はテクスチャ座標です。たぶん8と9は骨の重さのためのものです。必要に応じて、10を2番目の色にすることができます。GL_ARB_explicit_attrib_locationまたはGL 3.3以降を使用できない場合は、標準化された属性の命名規則も確立する必要があります。

このように、D3Dには規則があり、OpenGLには規則があります。したがって、ユーザーは「位置」のインデックスが何であるかを尋ねる必要さえありません。彼らはそれが0であることを知っています。そして、あなたの抽象化は0がD3Dランドでを意味することを知っていますVertexUsage::Position

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