Version: 2021.3
言語: 日本語
ランタイムのシェーダーの置き換え
エラーシェーダーとローディングシェーダー

コンピュートシェーダー

コンピュートシェーダーは通常のレンダリングパイプラインの外で GPU 上で実行されるシェーダープログラムです。

これは超並列 GPGPU アルゴリズム、またはゲームレンダリングの一部を加速させるために使用されます。効果的に使用するためには、しばしば GPU アーキテクチャおよび並列アルゴリズム、さらには DirectComputeOpenGL ComputeCUDAOpenCL に関する深い知識が必要です。

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) は、他のプラットフォーム (MetalOpenGL ES など) ではサポートされていない多くの操作をサポートしています。 したがって、DX11 だけの環境を考えるより、サポートが少ないプラットフォームで、シェーダーの挙動を明確に定義する必要があります。 考慮すべき点は以下のとおりです。

  • アウトオブバンドのメモリアクセスが悪い点。DX11 は、読み込み時に常にゼロを返し、問題なくデータの一部を読み込むかもしれませんが、サポートが少ないプラットフォームでは、これを行う際に GPU がクラッシュする可能性があります。DX11 特有のハック、スレッドグループサイズの倍数と一致しないバッファーサイズ、バッファーの先頭や末尾から隣接するデータ要素を読み取ろうとすること、などの同様の非互換性に注意してください。

  • リソースを初期化する点。 新しいバッファーとテクスチャの内容は未定義です。 プラットフォームの中にはすべてゼロを示すものもありますが、そうでないものでは、NaN を含めどんな値になることもありえます。

  • コンピュートシェーダーが宣言するすべてのリソースをバインドします。シェーダーが分岐のために現在の状態でリソースを使用しないことが確実にわかっていても、リソースがバインドされていることを確認する必要があります。

プラットフォーム特有の相違

  • Metal (iOS と tvOS プラットフォーム用) は、テクスチャのアトミック操作をサポートしません。Metal は、バッファーの GetDimensions クエリもサポートしません。必要な場合は、バッファーサイズの情報を定数としてシェーダーに渡します。
  • OpenGL ES 3.1 (Android, iOS, tvOS プラットフォーム用) は、一度に 4 つのコンピュートバッファーしかサポートしません。実際の実装では、より多くをサポートすることもありますが、一般的には OpenGL ES 用に開発する場合は、それぞれのバッファーに各データを格納するよりも、関連するデータを構造体でグループ化することを検討する必要があります。
  • OpenGL (ES) および Vulkan では、書き込み専用ではない RWTextures<T> に画像形式の修飾子が必要です。
    Unity は、この修飾子を山かっこ内の型 T から派生させます。形式の修飾子は、RWTexture にバインドされている RenderTextureGraphicsFormat/RenderTextureFormat と一致する必要があります。以下の表は、Unity RenderTexture GraphicsFormats および RenderTextureFormats を、対応する HLSL 型と画像形式修飾子にマップします。
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 専用、または、GLSL 専用のコンピュートシェーダー

通常、コンピュートシェーダーファイルは HLSL で記述され、自動的に、必要なすべてのプラットフォームにコンパイルまたは変換されます。ただし、他の言語への変換を避けたり (つまり、HLSL プラットフォームのみを維持する)、GLSL コンピュートコードを記述することも可能です。

以下の情報は、HLSL 専用、または、GLSL 専用のコンピュートシェーダーにのみ適用され、クロスプラットフォームビルドには適用されません。これは、この情報によってコンピュートシェーダーソースが一部のプラットフォームから除外される可能性があるためです。

  • CGPROGRAM キーワードと ENDCG キーワードで囲まれたコンピュートシェーダーソースは、非 HLSL プラットフォームでは処理されません。

  • GLSLPROGRAM キーワードと ENDGLSL キーワードで囲まれたコンピュートシェーダーソースは、GLSL ソースとして扱われ、そのまま出力されます。これは、OpenGL または GLSL プラットフォームをターゲットとする場合にのみ機能します。また、自動的に変換されたシェーダーはバッファーの HLSL データレイアウトに従いますが、記述された GLSL シェーダーは GLSL レイアウトルールに従うということにも注意する必要があります。

バリアントとキーワード

グラフィックスシェーダーと同じように、キーワードを使ってコンピュートシェーダの複数のバリアントを作ることができます。

バリアントに関する一般的な情報については、シェーダーバリアント を参照してください。これらの機能をコンピュートシェーダーに実装する方法については、HLSL のシェーダーキーワードの宣言と使用ComputeShader のスクリプトリファレンス を参照してください。

ランタイムのシェーダーの置き換え
エラーシェーダーとローディングシェーダー