コンピュートシェーダーは、通常のレンダリングパイプラインとは異なって、グラフィックスカード上で実行するプログラムです。これは超並列 GPGPU アルゴリズム、またはゲームレンダリングの一部を加速させるために使用できます。効果的に使用するためには、しばしば GPU アーキテクチャおよび並列アルゴリズム、さらには DirectCompute、OpenGL Compute、CUDA、OpenCL に関する深い知識が必要です。
Unity のコンピュートシェーダーは、DirectX 11 DirectCompute テクノロジーと良く似ています。コンピュートシェーダーが使用できるプラットフォームは以下の通りです。
DirectX 11 または DirectX 12 グラフィックス API と Shader Model 5.0 GPU を伴う Windows と Windows ストア
Metal グラフィックス API を使用する macOS と iOS
Vulkan API を伴う Android、Linux、Windows プラットフォーム
現段階で一般的に使用されている OpenGL プラットフォーム (Linux または Windows の OpenGL 4.3、Android の OpenGL ES 3.1)。Mac OS X は OpenGL 4.3 をサポートしません。
現段階で一般的に使用されている多くのコンソール (Sony PS4、Microsoft Xbox One)
コンピュートシェーダーのサポートの有無はランタイムに SystemInfo.supportsComputeShaders を使って確認できます。
通常のシェーダー と同様に、コンピュートシェーダーは、プロジェクトのアセットファイルで .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);
}
言語は標準の DX11 HLSL で、#pragma kernel FillWithRed
ディレクティブが追加されています。1つのコンピュートシェーダーアセットファイルには、呼び出し可能な1つ以上の コンピュートカーネル
が含まれていなければなりません。そして、その関数は #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 class を参照してください。
ComputeBuffer クラスはコンピュートシェーダーと密接に関係し、任意のデータバッファ (DX11 用語の 「構造化バッファー」) を定義します。レンダーテクスチャ は、「ランダムアクセス」フラグが設定されている場合 (DX11 の「unordered access view」)、コンピュートシェーダーからの書き込みも可能です。 詳細については、RenderTexture.enableRandomWrite を参照してください。
テクスチャとサンプラーは、Unity では別々のオブジェクトではありません。そのため、コンピュートシェーダでそれらを使用するには、以下の Unity 特有のルールに従う必要があります。
テクスチャ名と同じ名前を使用し、最初にsampler
と表記します (たとえば、Texture2D MyTex
; SamplerState samplerMyTex
)。 この場合、サンプラーはテクスチャの filter/wrap/aniso 設定に初期化されます。
あらかじめ定義されたサンプラーを使用してください。 このため、名前にはLinear
または Point
(フィルタモード用) と、Clamp
または Repeat
(ラップモード用) が必要です。 たとえば、SamplerState MyLinearClampSampler
は、リニアフィルターモードとクランプラップモードを持つサンプラーを作成します。
詳細は、サンプラー状態 を参照してください。
通常のシェーダーと同様に、Unity はコンピュートシェーダーを HLSL から他のシェーダー言語に翻訳することができます。したがって、最も簡単なクロスプラットフォームのビルドのためには、コンピュートシェーダーを HLSL で書くきます。ただし、これを行う場合には、いくつかの要素を考慮する必要があります。
DirectX 11 (DX11) は、他のプラットフォーム (Metal や OpenGL ES など) ではサポートされていない多くの操作をサポートしています。 したがって、DX11 だけの環境を考えるより、サポートが少ないプラットフォームで、シェーダーの挙動を明確に定義する必要があります。 考慮すべき点は以下のとおりです。
アウトオブバンドのメモリアクセスが悪い点。DX11 は、読み込み時に常にゼロを返し、問題なくデータの一部を読み込むかもしれませんが、サポートが少ないプラットフォームでは、これを行う際に GPU がクラッシュする可能性があります。DX11 特有のハック、スレッドグループサイズの倍数と一致しないバッファーサイズ、バッファーの先頭や末尾から隣接するデータ要素を読み取ろうとすること、などの同様の非互換性に注意してください。
リソースを初期化する点。 新しいバッファーとテクスチャの内容は未定義です。 プラットフォームの中にはすべてゼロを示すものもありますが、そうでないものでは、NaN を含めどんな値になることもありえます。
コンピュートシェーダーが宣言するすべてのリソースをバインドします。シェーダーが分岐のために現在の状態でリソースを使用しないことが確実にわかっていても、リソースがバインドされていることを確認する必要があります。
Metal (iOS と tvOS プラットフォーム用) は、テクスチャのアトミック操作をサポートしません。Metal は、バッファーの GetDimensions
クエリもサポートしません。必要な場合は、バッファーサイズの情報を定数としてシェーダーに渡します。
OpenGL ES 3.1 (Android, iOS, tvOS プラットフォーム用) は、一度に 4 つのコンピュートバッファーしかサポートしません。実際の実装では、より多くをサポートすることもありますが、一般的には OpenGL ES 用に開発する場合は、それぞれのバッファーに各データを格納するよりも、関連するデータを構造体でグループ化することを検討する必要があります。
通常、コンピュートシェーダーファイルは HLSL で記述され、自動的に、必要なすべてのプラットフォームにコンパイルまたは変換されます。ただし、他の言語への変換を避けたり (つまり、HLSL プラットフォームのみを維持する)、GLSL コンピュートコードを記述することも可能です。
以下の情報は、HLSL 専用、または、GLSL 専用のコンピュートシェーダーにのみ適用され、クロスプラットフォームビルドには適用されません。これは、この情報によってコンピュートシェーダーソースが一部のプラットフォームから除外される可能性があるためです。
CGPROGRAM
キーワードと ENDCG
キーワードで囲まれたコンピュートシェーダーソースは、非 HLSL プラットフォームでは処理されません。
GLSLPROGRAM
キーワードと ENDGLSL
キーワードで囲まれたコンピュートシェーダーソースは、GLSL ソースとして扱われ、そのまま出力されます。これは、OpenGL または GLSL プラットフォームをターゲットとする場合にのみ機能します。また、自動的に変換されたシェーダーはバッファーの HLSL データレイアウトに従いますが、記述された GLSL シェーダーは GLSL レイアウトルールに従うということにも注意する必要があります。
You can use the #pragma multi_compile
and #pragma multi_compile_local
directives to compile multiple variants of compute shaders, the same way you can for regular shaders. These directives affect all kernels in a given file.
Note that regular and compute shaders share global keywords. Enabling or disabling a global keyword affects all regular shaders and all compute shaders. Global keywords set in compute shaders count towards the global keyword limit.
To enable or disable global keywords on all regular shaders and compute shaders, use the Shader.EnableKeyword, Shader.DisableKeyword, CommandBuffer.EnableKeyword, and CommandBuffer.DisableKeyword APIs.
To enable or disable a local keyword on a compute shader, use the ComputeShader.EnableKeyword and ComputeShader.DisableKeyword APIs.
For more information, see Making multiple shader program variants and the ComputeShader API documentation.