book/18-urp-lit-compatible-pass-template.md

18. URP 완전 호환 ShaderLab Pass 세트 템플릿

Unity 6.3(6000.3) / URP 17.3.0 기준으로 Forward/ShadowCaster/DepthOnly/DepthNormals/Meta + (옵션) GBuffer/MotionVectors/XR/2D까지 포함한 ‘완전 호환’ Pass 세트 템플릿을 제공한다

18. URP 완전 호환 ShaderLab Pass 세트 템플릿

이 챕터는 “URP 내장 Lit와 최대한 같은 환경에서 동작하는” 커스텀 셰이더의 실전 템플릿을 제공합니다.

목표:

  • URP가 기대하는 LightMode Pass들을 빠짐없이 제공
  • SRP Batcher 호환을 깨지 않는 CBUFFER 구성
  • Forward+/추가 라이트 키워드까지 포함하는 기본 틀 제공

선행:

매우 중요(현실적인 경고)
“완전 호환”은 프로젝트 설정(Forward+/Deferred, SSAO, Decal, Rendering Layers, XR 등)에 따라 요구가 늘어납니다.
이 템플릿은 가장 흔한 URP Lit 호환 세트를 기준으로 하며, 프로젝트 기능에 따라 키워드/패스를 추가해야 합니다.

18.0 정확 레퍼런스(Generated, URP 17.3.0)

이 챕터는 “추상 템플릿”이 아니라, 로컬 URP 17.3.0 소스에서 자동 추출한 레퍼런스와 함께 사용하도록 설계했습니다.

18.1 템플릿 구성 파일

이 책 저장소에는 아래 샘플 파일을 함께 제공합니다.

  • samples/urp/URP_LitCompatibleTemplate.shader
  • samples/urp/URP_LitCompatibleTemplate.hlsl

문서에서는 핵심 부분을 설명하고, 전체 코드는 샘플 파일을 참고하는 방식으로 운영합니다.

18.1.1 템플릿을 “내 커스텀 Lit”로 바꾸는 가장 안전한 전략

URP 내장 Lit을 완전히 재구현하려고 하면, 생각보다 많은 기능(데칼, GI, 라이트 레이어, 렌더링 레이어, 포그, 라이트맵, 프루브 볼륨, 디버그 디스플레이 등)이 얽혀 있습니다.

따라서 추천 전략은 다음 2단계입니다.

  1. 1단계(호환 껍데기 고정): Pass/Tags/CBUFFER/키워드를 URP Lit과 최대한 같게 맞춘다.
  2. 2단계(교체 지점 최소화): 먼저 “표면 모델만” 교체하거나(예: 알베도/노말/러프니스 처리), 그 다음 “BRDF만” 교체한다.

이렇게 하면 “왜 깨졌는지”의 원인이 좁혀져 디버깅이 쉬워집니다.

18.2 Pass 세트의 의미(왜 이게 필요하나?)

URP의 Render Pass(C#)는 특정 단계에서 다음을 수행합니다.

  • “이 단계에서 사용할 ShaderLab Pass는 무엇인가?”를 LightMode로 결정

따라서 커스텀 셰이더가 URP의 각 단계에 참여하려면:

  • 그 단계가 찾는 LightMode Pass를 제공해야 하고
  • 그 Pass가 요구하는 입력(알파 클립/노말/뎁스 등)을 맞춰야 합니다.

18.3 Pass별 체크리스트(실전)

18.3.1 UniversalForward(컬러 패스)

  • 메인/추가 라이트, 그림자, 포그, GI, 데칼 등 “프로젝트에서 켜진 기능”을 지원하는 키워드 포함
  • Forward+를 쓰면 _CLUSTER_LIGHT_LOOP 포함
  • 머티리얼 프로퍼티는 UnityPerMaterial CBUFFER에 고정(모든 pass 동일)

18.3.2 ShadowCaster

  • 알파 클립(컷아웃) 처리
  • 그림자 바이어스/노말 오프셋 등 URP 방식과 호환

18.3.3 DepthOnly

  • 알파 클립(컷아웃) 처리
  • DepthTexture 생성이 필요할 때 참여

18.3.4 DepthNormals

  • 스크린 스페이스 이펙트(Outline/SSAO 등)에서 핵심
  • 노말이 어떤 공간(WS/VS)으로 저장되는지 URP 구현에 맞춰야 함

18.3.5 Meta

  • 라이트맵 베이킹용
  • 알베도/에미션만 필요한 경우가 많음

18.3.6 UniversalGBuffer(옵션, Deferred)

  • 프로젝트가 Deferred 렌더링을 사용한다면 사실상 필수급
  • LightMode = UniversalGBuffer
  • ForwardLit과 “같은 머티리얼”이 Deferred에서도 동작하려면, GBuffer 출력 계약(알베도/노말/머티리얼 파라미터)을 충족해야 함

18.3.7 MotionVectors(옵션, TAA/모션블러/리프로젝션)

  • TAA/모션블러/리프로젝션 계열 기능은 모션 벡터 패스를 요구할 수 있음
  • LightMode = MotionVectors
  • URP Lit은 ObjectMotionVectors.hlsl#include_with_pragmas로 포함해 엔트리를 구성(pragma 누락 주의)

18.3.8 XRMotionVectors(옵션, XR)

  • XR에서 모션 벡터가 필요하면 별도 계약이 요구될 수 있음(예: 스텐실)
  • LightMode = XRMotionVectors
  • URP Lit 기준으로는 MotionVectors와 유사하되, XR 전용 define/Stencil 블록이 추가될 수 있음

18.3.9 Universal2D(옵션, 2D Renderer)

  • 2D Renderer 호환이 필요하면 추가 패스가 필요할 수 있음
  • LightMode = Universal2D
  • 2D 전용 엔트리/Include(Utils/Universal2D.hlsl) 계약을 따라야 함

18.4 “URP Lit 호환” 템플릿의 핵심 규칙 7개

  1. Tags { "RenderPipeline"="UniversalPipeline" }를 반드시 포함
  2. LightMode Pass를 프로젝트 요구에 맞춰 제공(기본: Forward/ShadowCaster/DepthOnly/DepthNormals/Meta + 옵션: GBuffer/MotionVectors/XR/2D)
  3. UnityPerMaterial CBUFFER를 모든 pass에서 동일하게 유지(SRP Batcher)
  4. URP Core include를 기준으로 좌표/스크린/깊이/조명 접근을 통일
  5. Forward+를 쓰면 _CLUSTER_LIGHT_LOOP 경로를 고려
  6. Depth/Normals 같은 “파이프라인 입력”은 Requirements(생성 여부)까지 연결해서 검증
  7. 변형(variant) 폭발을 막기 위해 “필요한 키워드만” 단계적으로 추가

18.4.1 키워드 세트를 기능별로 관리하라(변형 폭발 방지)

URP Lit 호환을 맞추려다 보면 #pragma multi_compile이 폭발합니다. 이걸 통제하려면 “기능 묶음”으로 관리하는 것이 좋습니다.

A) 라이트/그림자

  • Additional Lights: _ADDITIONAL_LIGHTS_VERTEX, _ADDITIONAL_LIGHTS
  • Additional Light Shadows: _ADDITIONAL_LIGHT_SHADOWS
  • Main light shadows/cascade: _MAIN_LIGHT_SHADOWS, _MAIN_LIGHT_SHADOWS_CASCADE
  • Forward+: _CLUSTER_LIGHT_LOOP

B) 표면 기능

  • Alpha test: _ALPHATEST_ON
  • Normal map: _NORMALMAP
  • Parallax: _PARALLAXMAP

C) 환경

  • Fog: #pragma multi_compile_fog
  • Lightmap/SH: LIGHTMAP_ON 등(프로젝트 요구에 따라)

권장 운영

  • 템플릿에서는 “최소 세트”만 넣고,
  • 프로젝트 기능을 켤 때마다 필요한 키워드만 추가한 뒤,
  • Frame Debugger/RenderDoc로 정확하게 확인하세요.

18.5 샘플 코드(요약): ShaderLab 골격

전체 코드는 samples/urp/URP_LitCompatibleTemplate.shader를 참고하세요.

ShaderLab
SubShader
{
    Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Opaque" "Queue"="Geometry" }

    Pass { Tags { "LightMode"="UniversalForward" } }
    Pass { Tags { "LightMode"="ShadowCaster" } }
    Pass { Tags { "LightMode"="DepthOnly" } }
    Pass { Tags { "LightMode"="DepthNormals" } }
    Pass { Tags { "LightMode"="Meta" } }

    // (옵션) Deferred
    Pass { Tags { "LightMode"="UniversalGBuffer" } }

    // (옵션) Motion Vectors
    Pass { Tags { "LightMode"="MotionVectors" } }
    Pass { Tags { "LightMode"="XRMotionVectors" } }

    // (옵션) 2D Renderer
    Pass { Tags { "LightMode"="Universal2D" } }
}

18.6 샘플 코드(요약): CBUFFER 고정(SRP Batcher)

핵심은 “모든 pass에서 동일한 레이아웃”입니다.

HLSL
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float4 _BaseMap_ST;
float _Cutoff;
CBUFFER_END

18.7 템플릿을 프로젝트에 적용하는 순서(권장)

  1. Forward+ OFF에서 먼저 Lit(Forward)만 정상 동작하게 만들기
  2. ShadowCaster/DepthOnly를 붙여 “그림자/깊이” 깨짐 해결
  3. DepthNormals를 붙여 “화면 기반 이펙트” 호환 확보
  4. Forward+ ON 후 _CLUSTER_LIGHT_LOOP 경로 검증
  5. Deferred 프로젝트면 UniversalGBuffer 패스를 추가/검증
  6. TAA/모션블러/리프로젝션이 필요하면 MotionVectors(및 XR이면 XRMotionVectors) 패스 추가/검증
  7. 2D Renderer 호환이 필요하면 Universal2D 패스 추가/검증
  8. 필요 기능(Decal/Rendering Layers/SSR 등)을 하나씩 추가

18.8 검증 루틴(필수): “URP가 내 Pass를 쓰고 있나?”

템플릿을 프로젝트에 넣었다면, 다음을 반드시 확인하세요.

  1. Frame Debugger로 렌더링 이벤트를 보고, 오브젝트가 어떤 pass로 그려지는지 확인
  2. ForwardLit/ShadowCaster/DepthOnly/DepthNormals/Meta가 기대대로 호출되는지 확인
  3. Deferred 프로젝트라면 GBuffer(UniversalGBuffer)가 실제로 사용되는지 확인
  4. TAA/모션블러/XR이 관여한다면 MotionVectors/XRMotionVectors가 실제로 사용되는지 확인
  5. RenderGraph Viewer에서 Depth/Normals/History 리소스가 언제 생성되는지 확인

이 검증이 끝나면, 커스텀화(표면 모델/BRDF/특수 효과)를 시작해도 됩니다.

18.8 다음 읽기