빌트인 셰이더 변수
GLSL 셰이더 프로그램

여러 셰이더 프로그램 배리언트 만들기

셰이더 코드 조각 대부분을 고정시키되 약간 다른 셰이더 “배리언트”가 생성될 수 있게 하면 편리합니다. 일반적으로 “메가 셰이더” 또는 “우버 셰이더”라고 하며, 각 경우마다 다른 프리프로세서 명령으로 셰이더 코드를 여러 번 컴파일할 수 있습니다.

Unity에서는 #pragma multi_compile 또는 #pragma shader_feature 지시문을 셰이더 스니핏에 추가하여 이렇게 할 수 있습니다. 표면 셰이더에서도 가능합니다.

런타임 시점에 Unity는 머티리얼 키워드(Material.EnableKeywordShader.DisableKeyword) 또는 전역 셰이더 키워드(Shader.EnableKeywordShader.DisableKeyword)에서 적절한 셰이더 배리언트를 선택합니다.

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

로컬 키워드

shader_featuremulti_compile 의 주요 단점으로는 이 둘에서 정의된 모든 키워드가 Unity의 전역 키워드 개수 제한(256개 전역 키워드 + 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");
}

빌트인 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가 포함된 모든 배리언트를 건너뜁니다.

셰이더 하드웨어 배리언트

셰이더 하드웨어 배리언트를 사용하면 서로 다른 수준의 하드웨어 성능에 따라 별도로 최적화된 배리언트 집합을 제공할 수 있습니다. 단일 타겟 플랫폼(예: OpenGL ES) 내에서 고사양 및 저사양 하드웨어 모두에서 효율적으로 실행할 수 있는 간소화된 셰이더를 만들 수 있습니다.

셰이더 하드웨어 배리언트 생성을 활성화하려면 #pragma hardware_tier_variants renderer를 추가합니다. 여기서 renderer셰이더 프로그램 pragma에 사용 가능한 렌더링 플랫폼 중 하나입니다. 이 #pragma를 통해 Unity는 다른 키워드에 관계없이 셰이더당 세 개의 배리언트를 생성합니다. 각 배리언트에는 다음 중 하나가 정의됩니다.

UNITY_HARDWARE_TIER1
UNITY_HARDWARE_TIER2
UNITY_HARDWARE_TIER3

위 정의를 사용하여 저사양 또는 고사양 하드웨어용 추가 기능이나 조건부 폴백을 작성할 수 있습니다. Unity 에디터에서 각 티어 간에 전환할 수 있는 Graphics Emulation 메뉴를 사용하여 티어를 테스트할 수 있습니다.

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

로드하는 동안 Unity는 사용하는 GPU를 검사하고 티어 값을 자동으로 감지합니다. GPU를 자동으로 감지하지 못하면 가장 높은 티어로 기본 설정됩니다. Shader.globalShaderHardwareTier를 사용하여 이 티어 값을 오버라이드할 수 있지만, 변경할 셰이더를 Unity가 로드하기 전에 이 작업을 수행해야 합니다. 메인 씬을 로드하기 전에 사전 로드 씬에서 설정하는 것이 가장 좋습니다.

이런 셰이더 하드웨어 티어는 플레이어의 Quality 설정과 관련이 없으며, 플레이어를 실행하는 GPU의 상대적인 성능을 통해 인식됩니다.

플랫폼 셰이더 설정

다양한 하드웨어 티어에 따라 셰이더 코드를 미세 조정하는 작업과 별도로 Unity 내부 #defines를 미세 조정해야 할 수 있습니다. 예를 들어 모바일에서 캐스케이디드 섀도우 맵을 적용해야 할 수 있습니다. 이에 대한 자세한 내용은 UnityEditor.Rendering.PlatformShaderSettings 문서에서 확인할 수 있습니다. 이 문서에는 티어별 오버라이드에 대해 현재 지원되는 기능 리스트가 있습니다.

UnityEditor.Rendering.EditorGraphicsSettings.SetShaderSettingsForPlatform을 사용하여 플랫폼 셰이더 설정을 플랫폼과 티어별로 미세 조정할 수 있습니다.

다른 티어로 설정된 PlatformShaderSettings가 동일하지 않으면, #pragma hardware_tier_variants가 없어도 Unity가 셰이더에 대한 티어 배리언트를 생성합니다.

참고 항목

빌트인 셰이더 변수
GLSL 셰이더 프로그램