コンピュートシェーダーは通常のレンダリングパイプラインの外で GPU 上で実行されるシェーダープログラムです。
これは超並列 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 をサポートしません。
最近のコンソール
コンピュートシェーダーのサポートの有無はランタイムに 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 を含めどんな値になることもありえます。
コンピュートシェーダーが宣言するすべてのリソースをバインドします。シェーダーが分岐のために現在の状態でリソースを使用しないことが確実にわかっていても、リソースがバインドされていることを確認する必要があります。
GetDimensions
クエリもサポートしません。必要な場合は、バッファーサイズの情報を定数としてシェーダーに渡します。RWTextures<T>
に画像形式の修飾子が必要です。GraphicsFormat | RenderTextureFormat | HLSL 型 | GLSL 画像形式修飾子 |
---|---|---|---|
R32G32B32A32_SFloat | ARGBFloat | float4 | rgba32f |
R16G16B16A16_SFloat | ARGBHalf | min16float4/half4 | rgba16f |
R32G32_SFloat | RGFloat | float2 | rg32f |
R16G16_SFloat | RGHalf | min16float2/half2 | rg16f |
B10G11R11_UFloatPack32 | RGB111110Float | min10float3 | r11f_g11g_b10f |
R32_SFloat | RFloat | float | r32f |
R16_SFloat | RHalf | min16float/half | r16f |
R16G16B16A16_UNorm | ARGB64 | unorm min16float4/half4 | rgba16 |
A2B10G10R10_UNormPack32 | ARGB2101010 | unorm min10float4 | rgb10_a2 |
R8G8B8A8_UNorm | ARGB32 | unorm float4 | rgba8 |
R16G16_UNorm | RG32 | unorm min16float2/half2 | rg16 |
R8G8_UNorm | RG16 | unorm float2 | rg8 |
R16_UNorm | R16 | unorm min16float/half | r16 |
R8_UNorm | R8 | unorm float | r8 |
R16G16B16A16_SNorm | unsupported | snorm min16float4/half4 | rgba16_snorm |
R8G8B8A8_SNorm | unsupported | snorm float4 | rgba8_snorm |
R16G16_SNorm | unsupported | snorm min16float2/half2 | rg16_snorm |
R8G8_SNorm | unsupported | snorm float2 | rg8_snorm |
R16_SNorm | unsupported | snorm min16float/half | r16_snorm |
R8_SNorm | unsupported | snorm float | r8_snorm |
R32G32B32A32_SInt | ARGBInt | int4 | rgba32i |
R16G16B16A16_SInt | unsupported | min16int4 | rgba16i |
R8G8B8A8_SInt | unsupported | min12int4 | rgba8i |
R32G32_SInt | RGInt | int2 | rg32i |
R16G16_SInt | unsupported | min16int2 | rg16i |
R8G8_SInt | unsupported | min12int2 | rg8i |
R32_SInt | RInt | int | r32i |
R16_SInt | unsupported | min16int | r16i |
R8_SInt | unsupported | min12int | r8i |
R32G32B32A32_UInt | unsupported | uint4 | rgba32i |
R16G16B16A16_UInt | RGBAUShort | min16uint4 | rgba16ui |
R8G8B8A8_UInt | unsupported | unsupported | rgba8ui |
R32G32_UInt | unsupported | uint2 | rg32ui |
R16G16_UInt | unsupported | min16uint2 | rg16ui |
R8G8_UInt | unsupported | unsupported | rg8ui |
R32_UInt | unsupported | uint | r32ui |
R16_UInt | unsupported | min16uint | r16ui |
R8_UInt | unsupported | unsupported | r8ui |
A2B10G10R10_UIntPack32 | unsupported | unsupported | rgb10_a2ui |
通常、コンピュートシェーダーファイルは HLSL で記述され、自動的に、必要なすべてのプラットフォームにコンパイルまたは変換されます。ただし、他の言語への変換を避けたり (つまり、HLSL プラットフォームのみを維持する)、GLSL コンピュートコードを記述することも可能です。
以下の情報は、HLSL 専用、または、GLSL 専用のコンピュートシェーダーにのみ適用され、クロスプラットフォームビルドには適用されません。これは、この情報によってコンピュートシェーダーソースが一部のプラットフォームから除外される可能性があるためです。
CGPROGRAM
キーワードと ENDCG
キーワードで囲まれたコンピュートシェーダーソースは、非 HLSL プラットフォームでは処理されません。
GLSLPROGRAM
キーワードと ENDGLSL
キーワードで囲まれたコンピュートシェーダーソースは、GLSL ソースとして扱われ、そのまま出力されます。これは、OpenGL または GLSL プラットフォームをターゲットとする場合にのみ機能します。また、自動的に変換されたシェーダーはバッファーの HLSL データレイアウトに従いますが、記述された GLSL シェーダーは GLSL レイアウトルールに従うということにも注意する必要があります。
グラフィックスシェーダーと同じように、キーワードを使ってコンピュートシェーダの複数のバリアントを作ることができます。
バリアントに関する一般的な情報については、シェーダーバリアント を参照してください。これらの機能をコンピュートシェーダーに実装する方法については、HLSL のシェーダーキーワードの宣言と使用 と ComputeShader のスクリプトリファレンス を参照してください。