OpenGL コア詳細
グラフィックスコマンドバッファ

コンピュートシェーダー

コンピュートシェーダーは、通常のレンダリングパイプラインと異なって、グラフィックスカード上で動作するプログラムです。これは超並列 GPGPU アルゴリズム、またはゲームレンダリングの一部を加速させるために使用できます。効果的に使用するためには、しばしば GPU アーキテクチャおよび並列アルゴリズム、さらには DirectComputeOpenCL、または CUDA に関する深い知識が必要です。

Unity のコンピュートシェーダーは DirectX 11 の DirectCompute テクノロジに非常に近いものです。コンピュートシェーダーを実行できるプラットホームは以下のとおりです。

  • DirectX 11 グラフィックス API と Shader Model 5.0 GPU を伴う Windows と Windows ストア
  • 現段階で一般的に使用されている OpenGL プラットフォーム (Linux か Windows 上の OpenGL 4.3、Android 上の OpenGL ES 3.1)。Mac OS X は OpenGL 4.3 に対応していないため、コンピュートシェーダーはまだ、利用することはできません。
  • 現段階で一般的に使用されているコンソール (Sony PS4、Microsoft XboxOne)。

コンピュートシェーダーアセット

シェーダーアセット と似て、コンピュートシェーダーはプロジェクト上で *.compute 拡張子のついたアセットファイルです。DirectX 11 スタイルの HLSL 言語で記述されていて、最小限の #pragma コンパイルディレクティブ用いてどの関数がコンピュートシェーダーカーネルを用いるか指定します。

コンピュートシェーダーを使用するための最低限のサンプルです。

// test.compute

#pragma kernel FillWithRed

RWTexture2D<float4> res;

[numthreads(1,1,1)]
void FillWithRed (uint3 dtid : SV_DispatchThreadID)
{
    res[dtid.xy] = float4(1,0,0,1);
}

上記のサンプルでは際立って面白いことはやっておらず、ただ出力テクスチャを赤で埋めます。

言語は標準的な DirectX 11 HLSL であり、例外として #pragma kernel FillWithRed ディレクティブを使用しています。ひとつのコンピュートシェーダーアセットは最低限ひとつの実行可能な “コンピュートカーネル” を含む必要があり、その関数は e #pragma ディレクティブにより指定します。ファイルの中に複数のカーネルを含むことが可能であり、複数の #pragma kernel 行を追加するのみです。

コメント行に #pragma kernel を複数記入していると、実際に #pragma kernel を使用したときにコンパイルエラーになってしまうので注意してください。

#pragma kernel 行はオプションとして、カーネルをコンパイルしている間に複数のプリプロセッサーマクロを後に続けて定義することが可能です。サンプルとしては、

#pragma kernel KernelOne SOME_DEFINE DEFINE_WITH_VALUE=1337
#pragma kernel KernelTwo OTHER_DEFINE
// ...

コンピュートシェーダーの実行

スクリプトの中で、ComputeShader 型の変数を定義して、アセットに対する参照を割り当てて、その後は ComputeShader.Dispatch 関数を用いて実行することができます。詳細についてはスクリプトリファレンスの ComputeShader を参照してください。

コンピュートシェーダーと密接に関連しているのが ComputeBuffer クラスであり、任意のデータバッファを定義します( DirectX 11 用語では “structured buffer”)。Render Texture も、“random access” フラグ (DirecX 11 用語では “unordered access view”) が設定されていれば、またコンピュートシェーダーから書き込むことができます。RenderTexture.enableRandomWrite を参照してください。

コンピュートシェーダーでのテクスチャサンプラ

テクスチャおよびサンプラは Unity で別オブジェクトではないため、コンピュートシェーダーで使用するためには Unity 特有のルールに従う必要があります。

  • ネーミングについて、テクスチャ名と同じ名前にするか、または “sampler” を名前の始めに付与した名前 (例えば Texture2D MyTex の場合、SamplerState samplerMyTex) のいずれかにします。
  • さらには、“定義済み” のサンプラを使用します。名前は、“Linear” または “Point” (フィルターモードとして)、かつ、“Clamp” または “Repeat” (Wrap モードとして)、をつける必要があります。例えば "SamplerState MyLinearClampSampler" - これによりフィルターとして Linear、かつ、Wrap モードとして Clamp が使用されます。

クロスプラットフォームのサポート

標準シェーダーのように、Unity は、HLSL から GLSL に コンピュートシェーダーを変換できます。したがって、最も簡単なクロスプラットフォームの構築については、HLSL でコンピュートシェーダーを記述することを推奨します。

OpenGL コンピュートと D3D コンピュートの違い

複数の異なるシェーダーをプラットフォーム上で動作させるために考慮すべき制限事項は以下のとおりです。

  • D3D および OpenGL は、異なるデータのレイアウト規則を有しています。自動翻訳される GLSL シェーダーは、コンピュートシェーダー上で std430 レイアウトを使用します。したがって、例えば float3 ベースの 構造化バッファーは、DX をタイトにパッキングできるようにし、OpenGL では float4 にパッディングを強制するので互換性の問題が発生します。スカラー、2成分ベクトルまたは4成分ベクトルは、そのまま使用しても安全です。構造体を構築する際は特に注意が必要です。
  • OpenGL ES 3.1では、4 simultaneous シェーダーストレージバッファーのみサポートが保障されています。実際の実装では、通常、もう少しサポートしていますが、概して各バッファーでそれぞれのデータ項目を所有するのとは異なり、構造体で関連データのグルーピングを行うことを検討してください。

HLSL または、GLSL のみのコンピュートシェーダー

一般的に、コンピュートシェーダーのファイルは HLSL で記述され、必要なプラットホームすべてに自動的にコンパイルまたは変換されます。ですが、GLSLプラットホームを省く(つまり、HLSLプラットホームだけを残す)か、GLSLのコードを手動で書くために、変換を制限することができます。

  • CGPROGRAMENDCG のキーワードに囲まれたコンピュートシェーダーソースは、OpenGL/GLSL プラットフォーム用には処理されません。
  • GLSLPROGRAMENDGLSL のキーワードに囲まれたコンピュートシェーダーソースは、GLSL ソースとして扱われ、出力されます。これは、OpenGL/GLSL プラットフォームをターゲットとしているときのみ有効です。

注意: クロスプラットフォームビルドには、上記のいずれも適していません。なぜなら、それらは、プラットフォームから実行するコンピュートシェーダーソースにかなり密接に結びついているからです。

OpenGL コア詳細
グラフィックスコマンドバッファ