Version: 2021.1
언어: 한국어
빌트인 셰이더 변수
셰이더 데이터 타입 및 정밀도

셰이더 배리언트 및 키워드

공통 코드를 공유하는 셰이더 스니핏을 작성할 수 있지만, 특정 키워드가 활성화되거나 비활성화되면 다른 기능을 갖습니다. Unity가 이러한 셰이더 스니핏을 컴파일하면 활성화되거나 비활성화된 키워드의 다양한 조합에 대해 별도의 셰이더 프로그램을 생성합니다. 이러한 개별 셰이더 프로그램을 셰이더 배리언트라고 부릅니다.

셰이더 배리언트는 프로젝트 워크플로에 유용할 수 있습니다. 동일한 셰이더를 다른 머티리얼에 할당할 수 있지만, 키워드는 각각에 대해 다르게 설정할 수 있습니다. 즉 한 곳에서 셰이더 코드를 작성하고 유지하며, 프로젝트에서 더 적은 셰이더 에셋을 사용합니다. 또한 셰이더 배리언트를 사용하면 키워드를 활성화하거나 비활성화하여 런타임 시점에 셰이더 동작을 변경할 수 있습니다.

매우 많은 배리언트가 있는 셰이더는 “메가 셰이더” 또는 “우버 셰이더”라고 부릅니다. Unity의 스탠다드 셰이더는 그러한 셰이더의 한 예입니다.

셰이더 배리언트 및 키워드 사용

셰이더 배리언트 생성

셰이더 배리언트를 생성하려면 다음 pragma 지시문 중 하나를 사용합니다.

  • #pragma multi_compile
  • #pragma multi_compile_local
  • #pragma shader_feature
  • #pragma shader_feature_local

모든 셰이더 소스 파일(표면 셰이더 포함)과 컴퓨트 셰이더에 이러한 지시문을 사용할 수 있습니다.

키워드가 단일 셰이더 단계에만 영향을 미치는 경우 이러한 지시문에 접미사를 추가하여 중복되는 셰이더 컴파일 작업을 줄일 수 있습니다. 자세한 내용은 단계별 키워드 지시문을 참조하십시오.

그런 다음 Unity는 다양한 프리 프로세서 지시문을 사용하여 동일한 셰이더 코드를 여러 번 컴파일합니다.

셰이더 키워드 활성화 및 비활성화

셰이더 키워드를 활성화하거나 비활성화하려면 다음 API를 사용하십시오.

키워드를 활성화하거나 비활성화하면 Unity는 그에 적합한 배리언트를 사용합니다.

빌드에서 셰이더 배리언트 스트리핑

셰이더 배리언트가 필요하지 않다고 판단되면 빌드에 포함되지 않도록 만들 수 있습니다. 이렇게 하면 빌드 시간과 파일 크기를 줄일 수 있습니다.

그렇게 하려면 다음 API를 사용하십시오.

이 주제에 대한 자세한 내용은 Unity 블로그 포스트 스크립터블 셰이더 배리언트 스트리핑을 참조하십시오.

multi\_compile 작동 방식

예제 지시문:

# pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON

이 예제 지시문은 두 가지 셰이더 배리언트를 생성합니다. 하나는 FANCY_STUFF_OFF가 정의되고 다른 하나는 FANCY_STUFF_ON이 정의되어 있습니다. Unity는 머티리얼 또는 전역 셰이더 키워드에 따라 런타임 시점에 둘 중 하나를 활성화합니다. 두 키워드가 모두 활성화되지 않는 경우 Unity는 전자(이 예제에서는 FANCY_STUFF_OFF)를 사용합니다.

multi_compile 줄에 두 개 이상의 키워드를 추가할 수 있습니다. 예를 들면 다음과 같습니다.

# pragma multi_compile SIMPLE_SHADING BETTER_SHADING GOOD_SHADING BEST_SHADING

이 예제 지시문은 네 가지 셰이더 배리언트, 즉 SIMPLE_SHADING, BETTER_SHADING, GOOD_SHADING, 및 BEST_SHADING을 생성합니다.

아무 전처리기 매크로도 정의되지 않은 셰이더 배리언트를 생성하려면 밑줄(__)만 포함하는 이름을 추가하십시오. 프로젝트에서 사용할 수 있는 키워드 개수에는 제한이 있으며, 이렇게 하면 두 개의 키워드를 사용하는 것을 피할 수 있습니다(키워드 제한의 이후 섹션 참조). 예를 들면 다음과 같습니다.

# pragma multi_compile __ FOO_ON

이 지시문은 두 개의 셰이더 배리언트를 생성합니다. 하나는 아무것도 정의되어 있지 않고(__) 다른 하나는 FOO_ON이 정의되어 있습니다.

shader\feature와 multi\compile의 차이

shader_featuremulti_compile과 매우 유사합니다. 유일한 차이는 Unity가 shader_feature 셰이더의 미사용 배리언트를 최종 빌드에 포함하지 않는다는 점입니다. 이러한 이유로 머티리얼에서 설정된 키워드에는 shader_feature를 사용하고 코드에서 전역으로 설정된 키워드에는 multi_compile을 사용하는 것이 좋습니다.

또한 키워드가 하나뿐인 짧은 표기도 있습니다.

# pragma shader_feature FANCY_STUFF

이 표기는 #pragma shader_feature _ FANCY_STUFF의 단축키로, 두 셰이더 배리언트로 확장됩니다(첫 번째는 정의가 없고 두 번째는 정의가 있음).

여러 multi\_compile 줄 결합

multi_compile 줄을 제공하는 경우 Unity는 줄의 모든 가능한 조합에 대한 결과 셰이더를 컴파일합니다. 예를 들어 다음과 같습니다.

# pragma multi_compile A B C
#pragma multi_compile D E

첫 번째 줄에서 세 가지 배리언트가 생성되고 두 번째 줄에서 두 가지 배리언트가 생성되어 총 여섯 가지의 셰이더 배리언트(A+D, B+D, C+D, A+E, B+E, C+E)가 생성됩니다.

multi_compile 줄이 셰이더 “기능” 하나를 제어한다고 생각하십시오. 이 방법을 사용하면 전체 셰이더 배리언트 수가 매우 빠르게 증가합니다. 예를 들어 옵션이 각각 두 개인 multi_compile 기능이 열 개가 있으면 셰이더 배리언트가 총 1,024개 생성됩니다.

키워드 제한

셰이더 배리언트를 사용할 때는 Unity에서 키워드가 384개로 제한되고 이 중 약 60개가 내부적으로 사용되므로 실제로 사용 가능한 키워드는 더 적습니다. 또한 키워드는 Unity 프로젝트 전반에 걸쳐 전역으로 활성화되므로 여러 셰이더에 여러 키워드를 정의하는 경우에는 제한을 초과하지 않도록 주의해야 합니다.

로컬 키워드

shader_featuremulti_compile 의 주요 단점으로는 이 둘에서 정의된 모든 키워드가 Unity의 전역 키워드 개수 제한(384개 전역 키워드 + 64개 로컬)에 포함된다는 것을 들 수 있습니다. 이 문제를 피하려면 다른 셰이더 배리언트 지시문, 즉 shader_feature_localmulti_compile_local 을 사용할 수 있습니다.

  • shader_feature_local: shader_feature 와 유사하지만 열거된 키워드가 로컬입니다.
  • multi_compile_local: multi_compile 과 유사하지만 열거된 키워드가 로컬입니다.

로컬 지시문은 해당 지시문 아래에 정의된 키워드를 전체 프로젝트에 적용하는 대신, 해당 셰이더에 보관합니다. 이러한 이유로 전역 키워드가 아니라 로컬 키워드를 사용해야 합니다. 단, 전역 API를 통해 특정 키워드를 활성화하려는 경우는 예외입니다.

로컬 키워드 사용을 시작하면 성능상의 변화를 볼 수 있지만, 성능 차이는 프로젝트의 설정 방식에 따라 다릅니다. 셰이더당 로컬 및 전역 키워드의 총 개수는 성능에 영향을 미칩니다. 로컬 키워드는 많이 사용하고 전역 키워드는 적게 사용하여 셰이더당 총 키워드 개수를 줄이는 것이 이상적인 설정입니다.

동일한 이름의 전역 키워드와 로컬 키워드가 있는 경우 Unity는 로컬 키워드에 더 우선 순위를 둡니다.

한계

  • 전역 키워드를 변경하는 API가 포함된 로컬 키워드(예: Shader.EnableKeyword 또는 CommandBuffer.EnableShaderKeyword)는 사용할 수 없습니다.

  • 셰이더당 최대 64개의 고유 로컬 키워드를 사용할 수 있습니다.

  • 머티리얼에 활성화된 로컬 키워드가 있고 해당 셰이더가 더 이상 선언되지 않는 셰이더로 변경되면 Unity는 새로운 전역 키워드를 생성합니다.

예제

# pragma multi_compile_local __ FOO_ON

이 지시문은 두 개의 셰이더 배리언트를 생성합니다. 하나는 아무것도 정의되어 있지 않고(__) 다른 하나는 FOO_ON이 로컬 키워드로 정의되어 있습니다.

로컬 키워드를 활성화하기 위한 프로세스는 전역 키워드와 동일합니다.

public Material mat;
Private void Start()
{
    mat.EnableKeyword("FOO_ON");
}

단계별 키워드 지시문

셰이더 배리언트를 생성할 때 Unity 에디터의 기본 동작은 모든 배리언트에 셰이더 프로그램의 모든 단계를 생성하는 것입니다. 예를 들어 셰이더 프로그램에 버텍스 단계와 프래그먼트 단계가 포함되어 있는 경우 Unity는 모든 키워드 조합에 대한 버텍스 단계와 프래그먼트 단계를 생성합니다.

키워드가 모든 단계에 영향을 미치지 않는 경우 이 기본 동작으로 인해 중복된 작업이 발생합니다. 예를 들어 키워드가 프래그먼트 단계에만 영향을 미치는 경우 에디터는 각 배리언트에 동일한 버텍스 단계를 생성합니다. Unity는 이후 중복 요소를 식별하고 제거하여 이 중복된 작업이 빌드 크기나 런타임 성능에 영향을 미치지 않습니다. 하지만 여러 단계 그리고/또는 배리언트가 있는 경우 셰이더 컴파일 동안 낭비되는 시간이 상당할 수 있습니다.

이러한 문제를 방지하기 위해 단계별 키워드 지시문을 사용할 수 있습니다. 단계별 키워드 지시문은 일반 키워드 지시문에 적용하는 접미사입니다. 특정 키워드가 영향을 미치는 셰이더 단계를 에디터에게 알려주어 지원되는 그래픽스 API용 셰이더를 빌드할 때 중복된 작업을 건너뛸 수 있습니다.

지원되는 그래픽스 API

모든 그래픽스 API에서 Unity는 단계별 키워드 지시문 사용을 일부만 지원합니다.

  • OpenGL과 Vulkan용 셰이더를 컴파일할 때 에디터는 모든 단계별 키워드 지시문을 일반 키워드 지시문으로 자동으로 되돌립니다.
  • Metal용 셰이더를 컴파일할 때 버텍스 단계를 타게팅하는 키워드는 테셀레이션 단계에도 영향을 미치며, 반대도 성립합니다.

단계별 키워드 지시문 사용

사용 가능한 접미사로는 _vertex, _fragment, _hull, _domain, _geometry, _raytracing이 있습니다. 예를 들어 multi_compile_fragment, 또는 shader_feature_local_vertex와 같이 키워드 지시문의 끝에 접미사를 적용합니다. 여러 셰이더 단계를 타게팅하려면 동일한 키워드를 선언하는 여러 단계별 키워드 지시문을 사용합니다.

참고: 키워드가 특정 셰이더 단계에서만 사용되도록 보장하는 것은 사용자의 책임입니다.

빌트인 multi\_compile 단축키

빌트인 렌더 파이프라인에서 여러 셰이더 배리언트를 컴파일하는 데 사용되는 “단축키” 표기가 몇 개 있으며, 주로 Unity의 다양한 광원, 그림자, 라이트맵 타입을 처리하는 데 사용됩니다. 자세한 내용은 렌더링 경로 및 셰이더 문서를 참조하십시오.

  • multi_compile_fwdbasePassType.ForwardBase에 필요한 모든 배리언트를 컴파일합니다. 배리언트는 다양한 라이트맵 타입을 처리하며, 주요 방향 광원의 섀도우를 활성화하거나 비활성화합니다.
  • multi_compile_fwdaddPassType.ForwardAdd에 대한 배리언트를 컴파일합니다. 방향 광원, 스폿 광원 또는 점 광원 타입과 쿠키 텍스처가 있는 각각의 배리언트를 처리하도록 배리언트를 컴파일합니다.
  • multi_compile_fwdadd_fullshadows - multi_compile_fwdadd와 동일하지만, 광원에 실시간 섀도우가 적용되는 기능도 포함합니다.
  • multi_compile_fog는 다양한 안개 타입(off/linear/exp/exp2)을 처리하는 여러 배리언트로 확장됩니다.

대부분의 빌트인 단축키는 많은 셰이더 배리언트를 생성합니다. 프로젝트에 필요하지 않은 경우에는 #pragma skip_variants를 사용하여 그중 일부에 대한 컴파일을 건너뛸 수 있습니다. 예를 들면 다음과 같습니다.

# pragma multi_compile_fwdadd
# pragma skip_variants POINT POINT_COOKIE

이 지시문은 POINT 또는 POINT_COOKIE가 포함된 모든 배리언트를 건너뜁니다.

그래픽스 티어 및 셰이더 배리언트

런타임 시점에 Unity는 GPU의 기능을 검사하고 해당하는 그래픽스 티어를 결정합니다. 빌트인 렌더 파이프라인에서는 각 그래픽스 티어에 대한 셰이더 배리언트 집합을 자동으로 생성할 수 있습니다. 이렇게 하려면 #pragma hardware_tier_variants 지시문을 사용하십시오.

이 기능은 빌트인 렌더 파이프라인과만 호환됩니다. 유니버설 렌더 파이프라인(URP), 고해상도 렌더 파이프라인(HDRP) 또는 커스텀 스크립터블 렌더 파이프라인과는 호환되지 않습니다.

이 기능을 활성화하려면 다음과 같이 #pragma hardware_tier_variants renderer를 추가하며, 여기서 renderer는 유효한 그래픽스 API입니다.

# pragma hardware_tier_variants gles3

Unity는 다른 키워드 외에도 각 셰이더에 대해 세 가지의 셰이더 배리언트를 생성합니다. 생성된 각 배리언트에는 다음 정의 중 하나가 있으며, GraphicsTier 열거형의 동일한 번호가 매겨진 값에 해당합니다.

UNITY_HARDWARE_TIER1
UNITY_HARDWARE_TIER2
UNITY_HARDWARE_TIER3

이를 사용하여 저사양 또는 고사양 하드웨어에 대한 조건부 폴백 또는 추가 기능을 작성할 수 있습니다.

Unity는 애플리케이션을 처음 로드할 때 GraphicsTier를 감지하고 결과를 Graphics.activeTier에 저장합니다. Graphics.activeTier 값을 오버라이드하려면 직접 설정하십시오. 단, 변경하려는 셰이더를 Unity가 로드하기 전에 이 작업을 수행해야 합니다. 이 값을 설정하기에 적합한 지점은 메인 씬을 로드하기 전의 사전 로드 씬입니다.

이러한 배리언트의 영향을 가능한 한 최소한으로 유지하기 위해 Unity는 하나의 셰이더 집합만 플레이어에 로드합니다. 또한, TIER1에 대해서만 특수 버전을 작성하고 나머지는 모두 동일하게 한 경우처럼 결과적으로 동일한 셰이더는 디스크 공간을 추가로 사용하지 않습니다.

Unity 에디터에서 티어를 테스트하려면 Edit > Graphics tier로 이동한 후 Unity 에디터가 사용할 티어를 선택하십시오.

그래픽스 티어는 품질 설정과는 관계가 없으며, 이러한 설정에 추가됩니다.

플랫폼별 셰이더 정의 설정 및 그래픽스 티어 배리언트

빌트인 렌더 파이프라인에서 EditorGraphicsSettings.SetShaderSettingsForPlatform API를 사용하여 특정 BuildTargetGraphicsTier에 대한 Unity의 내부 #defines를 오버라이드할 수 있습니다.

이 기능은 빌트인 렌더 파이프라인과만 호환됩니다. 유니버설 렌더 파이프라인(URP), 고해상도 렌더 파이프라인(HDRP) 또는 커스텀 스크립터블 렌더 파이프라인과는 호환되지 않습니다.

특정 BuildTarget의 다른 GraphicsTier에 대해 다른 TierSettings 값을 제공하는 경우 Unity는 해당 셰이더에 대한 배리언트를 생성합니다. 이는 셰이더 코드에 #pragma hardware_tier_variants를 추가하지 않더라도 마찬가지입니다.

빌트인 셰이더 변수
셰이더 데이터 타입 및 정밀도