Version: 2023.2
언어: 한국어
런타임 시 셰이더 교체
오류 셰이더와 로딩 셰이더

컴퓨트 셰이더

컴퓨트 셰이더는 노멀 렌더링 파이프라인과 별도로 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를 사용하는 Mac OS 및 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);
}

언어는 추가 #pragma kernel FillWithRed 지시문이 포함된 스탠다드 DX11 HLSL입니다. 하나의 컴퓨트 셰이더 에셋 파일에는 호출 가능한 최소 한 개의 ’컴퓨트 커널(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의 “정렬되지 않은 액세스 뷰”) Render Textures도 컴퓨트 셰이더에서 쓰기 가능합니다. 자세한 내용은 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은 읽을 때 일관되게 0을 반환할 수 있으며 문제없이 일부 쓰기를 읽을 수 있지만 지원이 적은 플랫폼은 GPU를 충돌시킬 수 있습니다. DX11 특정 해킹, 스레드 그룹 크기의 배수와 일치하지 않는 버퍼 크기, 버퍼의 시작 또는 끝에서 이웃하는 데이터 요소를 읽으려는 시도 및 유사한 비호환성을 주의해야 합니다.

  • 리소스를 초기화해야 합니다. 새 버퍼 및 텍스처의 내용은 정의되지 않습니다. 일부 플랫폼은 모두 0을 제공하지만 다른 플랫폼에서는 NaN을 포함할 수 있습니다.

  • 컴퓨트 셰이더가 선언한 모든 리소스를 바인딩해야 합니다. 셰이더가 분기로 인해 현재 상태의 리소스를 사용하지 않는다는 사실을 알고 있어도 리소스가 바인딩되어 있는지 확인해야 합니다.

플랫폼별 차이점

  • Metal(iOS 및 tvOS 플랫폼용)은 텍스처의 원자 연산(atomic operation)을 지원하지 않습니다. 또한 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 미지원 snorm min16float4/half4 rgba16_snorm
R8G8B8A8_SNorm 미지원 snorm float4 rgba8_snorm
R16G16_SNorm 미지원 snorm min16float2/half2 rg16_snorm
R8G8_SNorm 미지원 snorm float2 rg8_snorm
R16_SNorm 미지원 snorm min16float/half r16_snorm
R8_SNorm 미지원 snorm float r8_snorm
R32G32B32A32_SInt ARGBInt int4 rgba32i
R16G16B16A16_SInt 미지원 min16int4 rgba16i
R8G8B8A8_SInt 미지원 min12int4 rgba8i
R32G32_SInt RGInt int2 rg32i
R16G16_SInt 미지원 min16int2 rg16i
R8G8_SInt 미지원 min12int2 rg8i
R32_SInt RInt int r32i
R16_SInt 미지원 min16int r16i
R8_SInt 미지원 min12int r8i
R32G32B32A32_UInt 미지원 uint4 rgba32i
R16G16B16A16_UInt RGBAUShort min16uint4 rgba16ui
R8G8B8A8_UInt 미지원 미지원 rgba8ui
R32G32_UInt 미지원 uint2 rg32ui
R16G16_UInt 미지원 min16uint2 rg16ui
R8G8_UInt 미지원 미지원 rg8ui
R32_UInt 미지원 uint r32ui
R16_UInt 미지원 min16uint r16ui
R8_UInt 미지원 미지원 r8ui
A2B10G10R10_UIntPack32 미지원 미지원 rgb10_a2ui

HLSL 전용 또는 GLSL 전용 컴퓨트 셰이더

일반적으로 컴퓨트 셰이더 파일은 HLSL로 작성되고 필요한 모든 플랫폼으로 자동 컴파일되거나 번역됩니다. 그러나 다른 언어로의 이동(즉, HLSL 플랫폼만 유지)을 방지하거나 GLSL 컴퓨트 코드를 수동으로 작성할 수 있습니다.

다음 정보는 크로스 플랫폼 빌드가 아닌 HLSL 전용 또는 GLSL 전용 컴퓨트 셰이더에만 적용됩니다. 이 정보로 인해 일부 플랫폼에서 컴퓨트 셰이더 소스가 제외될 수 있기 때문입니다.

  • CGPROGRAMENDCG 키워드로 둘러싸인 컴퓨트 셰이더 소스는 HLSL이 아닌 플랫폼에서 처리되지 않습니다.

  • GLSLPROGRAMENDGLSL 키워드로 둘러싸인 컴퓨트 셰이더 소스는 GLSL 소스로 취급되며 축약됩니다. 이는 OpenGL 또는 GLSL 플랫폼을 타겟으로 할 때만 적용됩니다. 자동으로 변환된 셰이더는 버퍼의 HLSL 데이터 레이아웃을 따르지만 수동으로 작성된 GLSL 셰이더는 GLSL 레이아웃 규칙을 따릅니다.

배리언트 및 키워드

키워드를 사용하여 그래픽스 셰이더와 마찬가지로 컴퓨트 셰이더의 여러 배리언트를 생성할 수 있습니다.

배리언트에 대한 일반적인 정보는 셰이더 배리언트를 참조하십시오. 컴퓨트 셰이더에서 해당 기능을 구현하는 방법에 대한 자세한 내용은 HLSL에서 셰이더 키워드 선언 및 사용ComputeShader API 문서를 참조하십시오.

ComputeShader

런타임 시 셰이더 교체
오류 셰이더와 로딩 셰이더