Version: 2021.2
言語: 日本語
ランタイムのシェーダーの置き換え
ビルトインシェーダー

コンピュートシェーダー

Compute shaders are shader programs that run on the GPU, outside of the normal rendering pipeline.

They can be used for massively parallel GPGPU algorithms, or to accelerate parts of game rendering. In order to efficiently use them, an in-depth knowledge of GPU architectures and parallel algorithms is often needed; as well as knowledge of DirectCompute, OpenGL Compute, CUDA, or 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 を使って確認できます。

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

Similar to shader assets, compute shader assets are files in your project. with a .compute file extension. They are written in DirectX 11 style HLSL language, with a minimal number of #pragma compilation directives to indicate which functions to compile as compute shader kernels.

こちらは、コンピュートシェーダーファイルの基本例です。出力テクスチャを赤で塗りつぶします。

// 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);
}

The language is standard DX11 HLSL, with an additional #pragma kernel FillWithRed directive. One compute shader Asset file must contain at least onecompute kernel that can be invoked, and that function is indicated by the #pragma directive. There can be more kernels in the file; just add multiple #pragma kernel lines.

複数の#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 は、リニアフィルターモードとクランプラップモードを持つサンプラーを作成します。

詳細は、サンプラー状態 を参照してください。

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

As with regular shaders, Unity is capable of translating compute shaders from HLSL to other shader languages. Therefore, for the easiest cross-platform builds, you should write compute shaders in HLSL. However, there are some factors that need to be considered when doing this.

クロスプラットフォームでの最良の実践法

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 用に開発する場合は、それぞれのバッファーに各データを格納するよりも、関連するデータを構造体でグループ化することを検討する必要があります。

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

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

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

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

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

バリアントとキーワード

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

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

ランタイムのシェーダーの置き換え
ビルトインシェーダー