Version: 2021.3
在运行时替换着色器
Error and loading shaders

计算着色器

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 技术紧密配合。计算着色器适用的平台:

  • Windows 和 Windows 应用商店,使用 DirectX 11 或 DirectX 12 图形 API 和 Shader Model 5.0 GPU

  • macOS 和 iOS,使用 Metal 图形 API

  • Android、Linux 和 Windows 平台,Vulkan API

  • 现代 OpenGL 平台(Linux 或 Windows 上的 OpenGL 4.3;Android 上的 OpenGL ES 3.1)。请注意,Mac OS X 不支持 OpenGL 4.3

  • Modern consoles

在运行时可使用 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 指令的同一行上不允许 // text 样式的注释,如果使用,会导致编译错误。

可选择性地在 #pragma kernel 行后面添加要在编译该内核时定义的多个预处理器宏,例如:

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

调用计算着色器

在脚本中,应定义 ComputeShader 类型的变量并分配对资源的引用。如此便可使用 ComputeShader.Dispatch 函数来调用它们。请参阅关于 ComputeShader 类的 Unity 文档以了解更多详细信息。

与计算着色器密切相关的是 ComputeBuffer 类,该类将定义任意数据缓冲区(在 DX11 术语中称为“结构化缓冲区”)。如果已设置“随机访问”标志(在 DX11 中称为“无序访问视图”),也可从计算着色器中写入渲染纹理。请参阅 RenderTexture.enableRandomWrite 以了解与此相关的更多信息。

计算着色器中的纹理采样器

纹理和采样器不是 Unity 中的单独对象,因此要在计算着色器中使用它们,必须遵循以下 Unity 特定规则之一:

  • 使用与纹理名称相同的名称,以 sampler 开头(例如,Texture2D MyTexSamplerState samplerMyTex)。在此情况下,采样器将初始化为纹理的过滤/包裹/各向异性 (filter/wrap/aniso) 设置。

  • 使用预定义采样器。因此,该名称必须具有 LinearPoint(对于过滤模式)和 ClampRepeat(包裹模式)。例如,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 的破解问题,与线程组大小倍数不匹配的缓冲区大小,试图从缓冲区的开头或结尾读取相邻的数据元素,以及类似的不兼容性。

  • 初始化您的资源。新缓冲区和纹理的内容是未定义的。有些平台可能会提供全零,但在其他平台上,可能会有某种内容(包括非数字)。

  • 绑定计算着色器声明的所有资源。即使您确定着色器在当前状态下由于分支而没有使用资源,仍必须确保有资源与其绑定。

平台特定差异

  • Metal(适用于 iOS 和 tvOS 平台)不支持对纹理的原子操作。Metal 也不支持对缓冲区的 GetDimensions 查询。如果需要,请将缓冲区大小信息作为常量传递给着色器。
  • OpenGL ES 3.1(适用于 Android、iOS、tvOS 平台)仅保证一次支持 4 个计算缓冲区。实际的实现通常支持更多数量,但在一般情况下,如果为 OpenGL ES 进行开发,应考虑在结构中对相关数据分组,而不是将每个数据项放在自己的缓冲区中。
  • OpenGL (ES) and Vulkan require an image format qualifier for RWTextures<T> that are not write-only.
    Unity derives this qualifier from the type T in the angle-brackets. The format qualifier needs to match the GraphicsFormat/RenderTextureFormat of the RenderTexture that is bound to the RWTexture. The following table maps Unity RenderTexture GraphicsFormats and RenderTextureFormats to their corresponding HLSL type and image format qualifier:
GraphicsFormat RenderTextureFormat HLSL type GLSL image format qualifier
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 浮点精度 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 的计算着色器,而不适用于跨平台版本。这是因为此信息可能导致计算着色器源代码被排除在某些平台之外。

  • 对于非 HLSL 平台,不会处理 CGPROGRAMENDCG 关键字包围的计算着色器源代码。

  • GLSLPROGRAMENDGLSL 关键字包围的计算着色器源代码视为 GLSL 源代码,并逐字发出。这仅在目标平台为 OpenGL 或 GLSL 平台时才奏效。还应注意,虽然自动转换的着色器遵循缓冲区上的 HLSL 数据布局,但是手动编写的 GLSL 着色器将遵循 GLSL 布局规则。

变体和关键字

You can use keywords to produce multiple variants of compute shaders, the same as you can for graphics shaders.

For general information on variants, see Shader variants. For information on how to implement these features in compute shaders, see Declaring and using shader keywords in HLSL and the ComputeShader API documentation.

在运行时替换着色器
Error and loading shaders