フラグメント シェーダーに値のリストを送信します。これはおそらく大きな (数千項目の長さの) 単精度浮動小数点のリストです。フラグメント シェーダーはこのリストにランダムにアクセスする必要があり、各フレームで CPU から値を更新したいと考えています。
これをどのように実行できるかについて、いくつかの選択肢を検討しています。
配列型のユニフォーム変数として ("uniform float x[10];")。ただし、ここには制限があるようです。私の GPU では、数百を超える値を送信すると非常に遅くなり、実行時に上限を変更したい場合は、シェーダーで上限をハードコードする必要があります。
高さ 1、リストの幅を持つテクスチャとして、glCopyTexSubImage2D を使用してデータを更新します。
他の方法はありますか? 最近 GL 仕様のすべての変更についていけていないのですが、おそらくこの目的のために特別に設計された他の方法があるのでしょうか?
ベストアンサー1
現在、これを行う方法は 4 つあります: 標準 1D テクスチャ、バッファ テクスチャ、ユニフォーム バッファ、およびシェーダ ストレージ バッファです。
1Dテクスチャ
この方法では、glTex(Sub)Image1D
1Dテクスチャをデータで埋めるために使用します。データは単なるfloatの配列なので、画像フォーマットである必要がありますGL_R32F
。次に、シェーダー内で単純なtexelFetch
呼び出しでアクセスします。はtexelFetch
テクセル座標 (名前の由来) を受け取り、すべてのフィルタリングをオフにします。したがって、正確に 1 つのテクセルが取得されます。
注:texelFetch
は 3.0 以上です。以前の GL バージョンを使用する場合は、サイズをシェーダーに渡し、テクスチャ座標を手動で正規化する必要があります。
ここでの主な利点は、互換性とコンパクトさです。これはGL 2.1ハードウェアで動作します(表記法を使用)。そして、持っている形式を使用するには、半浮動小数点数GL_R32F
を使用できます。または、データが正規化されたバイトに適している場合。サイズは全体的なパフォーマンスに大きな影響を与える可能性があります。GL_R16F
GL_R8
主な欠点は、サイズ制限です。 1D テクスチャは最大テクスチャ サイズに制限されます。 GL 3.x クラスのハードウェアでは、これは約 8,192 になりますが、4,096 未満にならないことが保証されます。
ユニフォームバッファオブジェクト
これを動作させるには、シェーダーでユニフォーム ブロックを宣言します。
layout(std140) uniform MyBlock
{
float myDataArray[size];
};
次に、配列と同じようにシェーダー内のそのデータにアクセスします。
C/C++ などのコードに戻ると、バッファ オブジェクトを作成し、浮動小数点データを入力します。次に、そのバッファ オブジェクトをユニフォームMyBlock
ブロックに関連付けることができます。詳細は、こちらをご覧ください。
この手法の主な利点は、速度とセマンティクスです。速度は、実装がテクスチャと比較してユニフォーム バッファをどのように扱うかによって決まります。テクスチャ フェッチはグローバル メモリ アクセスです。ユニフォーム バッファ アクセスは一般的にグローバル メモリ アクセスではありません。ユニフォーム バッファ データは通常、レンダリングでの使用時にシェーダが初期化されるときにシェーダにロードされます。そこからはローカル アクセスとなり、はるかに高速になります。
意味的には、これは単なるフラットな配列ではないので、より優れています。特定のニーズでは、 だけが必要な場合float[]
、それは問題ではありません。ただし、より複雑なデータ構造がある場合は、意味が重要になることがあります。たとえば、ライトの配列を考えてみましょう。ライトには位置と色があります。テクスチャを使用する場合、特定のライトの位置と色を取得するコードは次のようになります。
vec4 position = texelFetch(myDataArray, 2*index);
vec4 color = texelFetch(myDataArray, 2*index + 1);
position
ユニフォーム バッファーを使用すると、他のユニフォーム アクセスとまったく同じように見えます。 および を呼び出すことができる名前付きメンバーがありますcolor
。したがって、すべてのセマンティック情報がそこにあり、何が起こっているのかを理解しやすくなります。
これにもサイズ制限があります。OpenGLでは、実装がユニフォームブロックの最大サイズとして少なくとも16,384バイトを提供する必要があります。つまり、float配列の場合、4,096要素しか得られません。これは、最小実装から要求されるものはありません。ハードウェアによっては、はるかに大きなバッファを提供するものもあります。たとえば、AMD は DX10 クラスのハードウェアで 65,536 を提供します。
バッファテクスチャ
これらは一種の「スーパー1Dテクスチャ」です。テクスチャユニットからバッファオブジェクトにアクセスするこれらは 1 次元ですが、1D テクスチャではありません。
これらは GL 3.0 以上でのみ使用できます。また、texelFetch
関数経由でのみアクセスできます。
ここでの主な利点はサイズです。バッファテクスチャは一般的に非常に巨大になります。仕様は一般的に控えめで、バッファテクスチャには少なくとも65,536バイトを義務付けていますが、ほとんどのGL実装では、メガバイトのサイズです。実際、通常、最大サイズはハードウェアの制限ではなく、使用可能な GPU メモリによって制限されます。
また、バッファテクスチャは、1Dテクスチャのような不透明なテクスチャオブジェクトではなく、バッファオブジェクトに保存されます。つまり、バッファオブジェクトストリーミング技術更新します。
ここでの主な欠点は、1D テクスチャの場合と同様にパフォーマンスです。バッファ テクスチャは 1D テクスチャより遅くなることはないかもしれませんが、UBO ほど高速でもありません。バッファ テクスチャから 1 つの float を引き出すだけであれば、問題にはなりません。ただし、バッファ テクスチャから大量のデータを引き出す場合は、代わりに UBO の使用を検討してください。
シェーダーストレージバッファオブジェクト
OpenGL 4.3 では、これを処理する別の方法が提供されています。シェーダストレージバッファこれらはユニフォーム バッファによく似ています。ユニフォーム ブロックとほぼ同じ構文を使用して指定します。主な違いは、これらに書き込みができることです。明らかに、これはあなたのニーズには役立ちませんが、他にも違いがあります。
シェーダストレージバッファは、概念的にはバッファテクスチャの代替形式です。したがって、シェーダストレージバッファのサイズ制限は、多くユニフォームバッファよりも大きい。OpenGLの最大UBOサイズの最小値は16KBである。OpenGLの最大SSBOサイズの最小値は16MBしたがって、ハードウェアをお持ちの場合は、UBO の興味深い代替手段となります。
readonly
これらに書き込むわけではないので、必ず として宣言してください。
ここでの潜在的な欠点は、UBOと比較した場合のパフォーマンスです。SSBOは、画像の読み込み/保存操作バッファ テクスチャを介して。基本的に、これはimageBuffer
イメージ タイプに関する (非常に優れた) 構文糖です。そのため、これらからの読み取りは、 からの読み取りと同じ速度で実行される可能性がありますreadonly imageBuffer
。
バッファイメージを介したイメージのロード/ストアによる読み取りがバッファテクスチャよりも速いか遅いかは、現時点では不明です。
もう一つの潜在的な問題は、非同期メモリアクセスこれらは複雑なので、簡単につまずいてしまう可能性があります。