Unity3D:计算着色器

推荐:将NSDT场景编辑器加入你的3D工具链
3D工具集:NSDT简石数字孪生

计算着色器

计算着色器是在正常渲染管道之外的 GPU 上运行的着色器程序。

它们可用于大规模并行的GPGPU算法,或加速部分游戏渲染。为了有效地使用它们,通常需要对 GPU 架构和并行算法有深入的了解;以及DirectCompute,OpenGL Compute,CUDA或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 来查询计算着色器支持情况。

计算着色器资源

与着色器资源类似,计算着色器资源是项目中的文件。文件扩展名为 .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 FillWithRedcompute kernel#pragma directive#pragma kernel

使用多个 #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 会创建一个具有线性过滤模式和钳制包裹模式的采样器。

有关更多信息,请参阅采样器状态文档。

跨平台支持

与常规着色器一样,Unity 能够将计算着色器从 HLSL 转换为其他着色器语言。因此,对于最简单的跨平台生成,应在 HLSL 中编写计算着色器。但是,执行此操作时需要考虑一些因素。

跨平台最佳实践

DirectX 11 (DX11) 支持在其他平台(如 Metal 或 OpenGL ES)上不支持的许多操作。因此,应始终确保着色器在提供更少支持的平台(而不是仅在 DX11 上)上具有良好定义的行为。以下是要考虑的一些事项:

  • 越界内存访问是错误的。DX11 在读取时可能始终返回零,并且在读取某些写入时没有问题,但提供较少支持的平台可能会在执行此操作时导致 GPU 崩溃。密切注意特定于 DX11 的破解问题,与线程组大小倍数不匹配的缓冲区大小,试图从缓冲区的开头或结尾读取相邻的数据元素,以及类似的不兼容性。
  • 初始化您的资源。新缓冲区和纹理的内容是未定义的。有些平台可能会提供全零,但在其他平台上,可能会有某种内容(包括非数字)。
  • 绑定计算着色器声明的所有资源。即使您确定着色器在当前状态下由于分支而没有使用资源,仍必须确保有资源与其绑定。

平台特定差异

  • Metal(适用于 iOS 和 tvOS 平台)不支持对纹理的原子操作。Metal 也不支持对缓冲区的 GetDimensions 查询。如果需要,请将缓冲区大小信息作为常量传递给着色器。
  • OpenGL ES 3.1(适用于 Android、iOS、tvOS 平台)仅保证一次支持 4 个计算缓冲区。实际的实现通常支持更多数量,但在一般情况下,如果为 OpenGL ES 进行开发,应考虑在结构中对相关数据分组,而不是将每个数据项放在自己的缓冲区中。
  • OpenGL (ES) 和 Vulkan 需要一个图像格式限定符,因为这不是只写的。
    Unity 从尖括号中的类型 T 派生此限定符。格式限定符需要与绑定到 RWTexture 的 RenderTexture 的 GraphicsFormat/RenderTextureFormat 匹配。下表将 Unity 渲染纹理图形格式和渲染纹理格式映射到其相应的 HLSL 类型和图像格式限定符:RWTextures<T>
GraphicsFormatRenderTextureFormatHLSL typeGLSL image format qualifier
R32G32B32A32_SFloatARGBFloatfloat4rgba32f
R16G16B16A16_SFloatARGBHalfmin16float4/half4rgba16f
R32G32_SFloatRGFloatfloat2rg32f
R16G16_SFloatRGHalfmin16float2/half2rg16f
B10G11R11_UFloatPack32RGB111110Floatmin10float3r11f_g11g_b10f
R32_SFloatRFloat浮点精度r32f
R16_SFloatRHalfmin16float/halfr16f
R16G16B16A16_UNormARGB64unorm min16float4/half4rgba16
A2B10G10R10_UNormPack32ARGB2101010unorm min10float4rgb10_a2
R8G8B8A8_UNormARGB32unorm float4rgba8
R16G16_UNormRG32unorm min16float2/half2rg16
R8G8_UNormRG16unorm float2rg8
R16_UNormR16unorm min16float/halfr16
R8_UNormR8unorm floatr8
R16G16B16A16_SNormunsupportedsnorm min16float4/half4rgba16_snorm
R8G8B8A8_SNormunsupportedsnorm float4rgba8_snorm
R16G16_SNormunsupportedsnorm min16float2/half2rg16_snorm
R8G8_SNormunsupportedsnorm float2rg8_snorm
R16_SNormunsupportedsnorm min16float/halfr16_snorm
R8_SNormunsupportedsnorm floatr8_snorm
R32G32B32A32_SIntARGBIntint4rgba32i
R16G16B16A16_SIntunsupportedmin16int4rgba16i
R8G8B8A8_SIntunsupportedmin12int4rgba8i
R32G32_SIntRGIntint2rg32i
R16G16_SIntunsupportedmin16int2rg16i
R8G8_SIntunsupportedmin12int2rg8i
R32_SIntRIntintr32i
R16_SIntunsupportedmin16intr16i
R8_SIntunsupportedmin12intr8i
R32G32B32A32_UIntunsupporteduint4rgba32i
R16G16B16A16_UIntRGBAUShortmin16uint4rgba16ui
R8G8B8A8_UIntunsupportedunsupportedrgba8ui
R32G32_UIntunsupporteduint2rg32ui
R16G16_UIntunsupportedmin16uint2rg16ui
R8G8_UIntunsupportedunsupportedrg8ui
R32_UIntunsupporteduintr32ui
R16_UIntunsupportedmin16uintr16ui
R8_UIntunsupportedunsupportedr8ui
A2B10G10R10_UIntPack32unsupportedunsupportedrgb10_a2ui

仅限 HLSL 或仅限 GLSL 的计算着色器

通常情况下会以 HLSL 编写计算着色器文件,并自动将这些文件编译或转换到所有需要的平台中。但是,可以阻止转换为其他语言(即仅保留 HLSL 平台)或者手动编写 GLSL 计算代码。

以下信息仅适用于仅限 HLSL 或仅限 GLSL 的计算着色器,而不适用于跨平台版本。这是因为此信息可能导致计算着色器源代码被排除在某些平台之外。

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

变体和关键字

可以使用关键字生成计算着色器的多个变体,与图形着色器相同。

有关变体的一般信息,请参阅着色器变体。有关如何在计算着色器中实现这些功能的信息,请参阅在 HLSL 中声明和使用着色器关键字和计算着色器 API 文档。

3D建模学习工作室整理翻译,转载请注明出处!

上一篇:Unity3D:在运行时替换着色器 (mvrlink.com)

下一篇:Unity3D:错误和加载着色器 (mvrlink.com)

NSDT场景编辑器 | NSDT 数字孪生 | GLTF在线编辑器 | 3D模型在线转换 | UnrealSynth虚幻合成数据生成器 | 3D模型自动纹理化工具
2023 power by nsdt©鄂ICP备2023000829号