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

셰이더 배리언트 및 키워드

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

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

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

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

셰이더 배리언트 생성

To create shader variants, you use one of the following pragma directives:

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

You can use these directives in all shader source files (including surface shaders) and compute shaders.

If a keyword affects only a single shader stage, you can add a suffix to these directives to reduce redundant shader compilation work. For more information, see Stage-specific keyword directives.

그런 다음 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개 생성됩니다.

키워드 제한

When using Shader variants, there is a limit of 384 keywords in Unity, and Unity uses around 60 of them internally (therefore lowering the available limit). The keywords are enabled globally across a Unity project, so be careful not to exceed the limit when you define multiple keywords in several different Shaders.

로컬 키워드

The main disadvantage of shader_feature and multi_compile is that all keywords defined in them contribute towards Unity’s global keyword count limit (384 global keywords, plus 64 local keywords). To avoid this issue, you can use different shader variant directives: shader_feature_local and multi_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");
}

Stage-specific keyword directives

When you create shader variants, the Unity Editor’s default behavior is to generate every stage of the shader program in every variant. For example, if your shader program contains a vertex stage and a fragment stage, Unity generates a vertex stage and a fragment stage for every keyword combination.

If a keyword does not affect all stages, this default behavior results in redundant work. For example, if a keyword affects only the fragment stage, the Editor generates an identical vertex stage for each variant. Unity identifies and removes duplicates afterwards, so this redundant work does not affect build sizes or runtime performance; however, if you have a lot of stages and/or variants, the time wasted during shader compilation can be significant.

To avoid this problem, you can use stage-specific keyword directives. These are suffixes that you apply to regular keyword directives. They tell the Editor which shader stage a given keyword affects, so it can skip the redundant work when building shaders for supported graphics APIs.

지원되는 그래픽스 API

Unity does not fully support the use of stage-specific keyword directives with all graphics APIs.

  • When compiling shaders for OpenGL and Vulkan, the Editor automatically reverts any stage-specific keyword directives to regular keyword directives.
  • When compiling shaders for Metal, any keyword targeting vertex stages will also affect tessellation stages, and vice versa.

Using stage-specific keyword directives

The available suffixes are _vertex, _fragment, _hull, _domain, _geometry, and _raytracing. You apply the suffix at the end of a keyword directive, for example: multi_compile_fragment, or shader_feature_local_vertex. To target multiple shader stages, you use multiple stage-specific keyword directives declaring the same keywords.

Note: you are responsible for ensuring that the keywords are only used in the specified shader stages.

빌트인 multi\_compile 단축키

In the Built-in Render Pipeline, ther are several “shortcut” notations for compiling multiple shader variants. These are mostly to deal with different light, shadow and lightmap types in Unity. See documentation on rendering paths and shaders for details.

  • 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) 또는 커스텀 스크립터블 렌더 파이프라인과는 호환되지 않습니다.

To enable this feature, add #pragma hardware_tier_variants renderer, where renderer is a valid graphics API, like this:

# 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를 추가하지 않더라도 마찬가지입니다.

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