multi_compile도 지시어도 shader_feature 지시어 처럼 토글(체크박스)로 셰이더의 기능을
On/Off 할수 있습니다.
두 지시어의 차이점에 대해 간단하게 알아 보겠습니다.
shader_feature vs multi_compile:
shader_feature:
- 메테리얼에서 쓰지 않는 변종은 빌드 시 포함하지 않습니다. (용량 절약, 보통 재질 설정에 사용)
- 용도 : 메테리얼 설정 (개별 기능 On/Off)
- 빌드 최적화 : 사용 중인 조합만 빌드에 포함
- 런타임 제어 : 어려움 (빌드 시 빠질 수 있음)
multi_compile:
- 사용 여부와 관계없이 모든 변종을 빌드합니다. (주로 그림자, 안개 등 시스템 설정에 사용)
- 용도 : 전역(Global) 설정 (안개, 그림자 등)
- 빌드 최적화 : 모든 가능한 조합을 빌드에 포함
- 런타임 제어 : 모두 포함, 자유로움 (언제든 끄고 켜기 가능)
런타임에 코드로 자유롭게 끄고 켜야 하는 기능이라면 shader_feature 대신 multi_compile을 사용하는 것이
안전합니다.
다음 예제는 multi_compile을 이용하여 텍스쳐를 적용 할지, 안할지 토글하여 선택 할수 있습니다.
셰이더를 초기화 상태로 되돌리려면 Reset을 해야 합니다.
실질적으로는 shader_feature 키워드에서 multi_compile로 키워드 한줄만 바뀌었습니다.
1. 퍼로퍼티 추가
[Toggle] 속성을 사용하여 인스펙터에 체크박스를 생성합니다.
괄호 안의 _USE_TEX_ON은 실제 셰이더 키워드 이름이 됩니다.
Properties
{
[Toggle(_USE_TEX_ON)]
_UseTex ("Use Texture?", Float) = 0.0
_BaseMap("BaseMap", 2D) =
"white" {}
_BaseColor("Base Color
(Fallback)", Color) = (1, 1, 1, 1)
}
2. 셰이더 키워드 선언 (텍스처 사용 여부 분기)
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ _USE_TEX_ON
multi_compile의 장점과 단점
- 장점: 게임 실행 중에 언제든지 EnableKeyword를 통해 기능을 켜도 즉시 대응할 수 있습니다.
셰이더가 빌드에서 누락되어 화면이 핑크색으로 변할 걱정이 없습니다.
- 단점: 키워드가 많아지면 빌드 용량이 커지고 셰이더 컴파일 시간이 길어집니다.
3. 체크박스가 켜져 있을 때만 텍스처를 샘플링합니다.
half3 frag(Varyings IN) : SV_Target
{
......
#if defined(_USE_TEX_ON)
albedo *= SAMPLE_TEXTURE2D(_BaseMap,
sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap)).rgb;
#endif
return albedo * (directLight + ambient);
}
주의할 점: 셰이더 변종 폭발 (Variant Explosion)
multi_compile을 남발하면 안 되는 이유입니다.
만약 다음과 같이 선언하면 어떻게 될까요?
- #pragma multi_compile A B (2개)
- #pragma multi_compile C D (2개)
- #pragma multi_compile E F (2개)
유니티는 이들의 모든 조합인 2 X 2 X 2 = 8개의 셰이더를 만듭니다.
키워드가 10개만 되어도 수천 개의 셰이더가 생성되어 빌드 시간이 몇 시간씩 걸릴 수 있습니다.
요약
- multi_compile은 어떤 상황에서도 해당 기능이 작동하도록 모든 셰이더 버전을 빌드에 포함시키는
지시어입니다.
- 런타임에 스크립트로 기능을 끄고 켜야 한다면 multi_compile이 정답입니다.
- 반면, 메테리얼 설정으로 고정해서 쓸 기능이라면 shader_feature가 최적화에 유리합니다
MyUnlitBasic_multi_cimpile.shader 파일
Shader "Custom/UnlitShaderBasic_MultiCompile"
{
Properties
{
// 인스펙터 토글. 런타임에
EnableKeyword/DisableKeyword로 제어 가능합니다.
[Toggle(_USE_TEX_ON)]
_UseTex ("Use Texture?", Float) = 0.0
_BaseMap("BaseMap", 2D) =
"white" {}
_BaseColor("Base Color
(Fallback)", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType" =
"Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
// shader_feature 대신 multi_compile을
사용하여
// 빌드 시 모든 경우의 수(On/Off)를 포함시킵니다.
// '_'는 키워드가 없는 상태(Off)를 의미합니다.
#pragma multi_compile _ _USE_TEX_ON
#include
"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include
"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2
uv :
TEXCOORD0;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2
uv :
TEXCOORD0;
float3 normalWS : NORMAL;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
CBUFFER_END
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = IN.uv;
OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
return OUT;
}
half3 frag(Varyings IN) : SV_Target
{
Light light = GetMainLight();
float3 lightDir = normalize(light.direction);
float3 normalWS = normalize(IN.normalWS);
float NdotL = saturate(dot(normalWS, lightDir));
float3 directLight = NdotL * light.color;
half3 ambient = unity_AmbientSky.rgb;
// 기본 색상 설정
half3 albedo = _BaseColor.rgb;
// multi_compile로 생성된 키워드 분기
#if defined(_USE_TEX_ON)
albedo *= SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap,
TRANSFORM_TEX(IN.uv, _BaseMap)).rgb;
#endif
return albedo * (directLight + ambient);
}
ENDHLSL
}
}
}
"Use Texture"를 토글 하여, 텍스쳐를 적용 할수 있습니다.
4. 셰이더 최종 정리
multi_compile _ _USE_TEX_ON 처럼 언더바는 항상 포함 시켜 줍니다.
체크의 초기 값은 다음과 같이 1.0과 0.0으로 해주는게 가독성에 좋습니다.
- [Toggle(_USE_TEX_ON)] _UseTex ("Use Texture?", Float) = 1.0
- [Toggle(_USE_TEX_ON)] _UseTex ("Use Texture?", Float) = 0.0
Properties
{
// 1.0이면 생성 시
자동 체크, 0.0이면 체크 해제 상태
[Toggle(_USE_TEX_ON)] _UseTex ("Use Texture?",
Float) = 1.0
_BaseMap("BaseMap", 2D) = "white" {}
}
// ...
HLSLPROGRAM
// _ 는 '아무것도 없음(Off)',
_USE_TEX_ON은 '켜짐(On)'
#pragma multi_compile _ _USE_TEX_ON
// ...
#if defined(_USE_TEX_ON)
// 텍스처 연산 수행
#endif
5. C#에서 제어 하는 방법
_USE_TEX_ON로 활성화, 비활성화 하면 됩니다.
using UnityEngine;
public class TextureToggleController : MonoBehaviour
{
private Material targetMat;
private bool isTexOn = true;
void Start()
{
// 렌더러에서 메테리얼 인스턴스를 가져옵니다.
targetMat =
GetComponent<Renderer>().material;
}
void Update()
{
// 스페이스바를 누를 때마다 토글
if
(Input.GetKeyDown(KeyCode.Space))
{
isTexOn = !isTexOn;
if
(isTexOn)
targetMat.EnableKeyword("_USE_TEX_ON");
else
targetMat.DisableKeyword("_USE_TEX_ON");
Debug.Log($"Texture is now: {(isTexOn ? "ON" : "OFF")}");
}
}
}
|