유니티 URP(Universal Render Pipeline) 커스텀 셰이더 기초 및 렌더링 최적화 노하우

유니티 URP(Universal Render Pipeline) 커스텀 셰이더 기초 및 렌더링 최적화 노하우

유니티(Unity)의 URP(Universal Render Pipeline)는 모바일, PC, 콘솔 등 다양한 하드웨어 플랫폼에서 최적의 그래픽 성능과 시각적 품질을 동시에 확보할 수 있도록 설계된 차세대 스크립터블 렌더 파이프라인(SRP)입니다. 과거의 빌트인 렌더 파이프라인(Built-in Render Pipeline)과 달리 URP는 단일 패스 포워드 렌더링(Single-pass Forward Rendering)을 기본 구조로 채택하여 드로우콜(Draw Call)과 오버드로우(Overdraw)를 혁신적으로 줄였습니다. 본 가이드에서는 URP 환경에서 HLSL(High-Level Shading Language)을 활용한 커스텀 셰이더의 기본 구조를 이해하고, SRP 배처(SRP Batcher) 호환성을 유지하면서 렌더링 효율을 극대화하는 실무 최적화 노하우를 상세히 전달해 드립니다.

유니티 URP 렌더 파이프라인 및 커스텀 셰이더 아키텍처 다이어그램
▲ 유니티 URP(Universal Render Pipeline) 기반 셰이더 렌더링 아키텍처

1. URP 셰이더의 기본 아키텍처와 HLSL 구조

빌트인 파이프라인에서는 CGPROGRAM과 #pragma surface 기반의 서피스 셰이더(Surface Shader)가 널리 쓰였으나, URP에서는 완전히 HLSL 표준인 HLSLPROGRAM 블록과 정점/프래그먼트(Vertex/Fragment) 셰이더 방식을 직접 작성하는 구조로 변경되었습니다. 이는 파이프라인의 불필요한 추상화를 제거하여 GPU 연산 제어권을 개발자에게 부여하고 성능 오버헤드를 줄이기 위함입니다.

1.1 필수 인클루드 파일과 네임스페이스

URP 셰이더가 정상적으로 작동하고 조명 연산 및 카메라 변환을 수행하려면 유니티 코어 라이브러리를 반드시 포함해야 합니다.

  • Core.hlsl: #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" — 좌표계 변환 함수(TransformObjectToWorldSpace 등)와 기본 매크로 제공
  • Lighting.hlsl: #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" — 유니티 주 광원(Main Light) 및 추가 광원(Additional Lights) 연산 함수 제공

1.2 정점 구조체(Attributes & Varyings)의 설계 원칙

버텍스 셰이더에서 프래그먼트 셰이더로 데이터를 넘길 때 사용되는 Varyings 구조체는 레지스터 사용량을 최소화하도록 설계해야 합니다. 모바일 기기의 버텍스 보간기(Interpolator) 대역폭은 한정되어 있으므로, 불필요한 중간 데이터 전달을 지양하고 필요한 경우 팩킹(Packing) 기법을 활용하여 float4 레지스터에 서로 다른 데이터를 결합하여 전달하는 것이 바람직합니다.


2. SRP 배처(SRP Batcher) 호환성 확보: 렌더링 성능의 핵심

URP 환경에서 드로우콜 CPU 오버헤드를 줄이는 가장 강력한 무기는 SRP 배처(SRP Batcher)입니다. 기존의 동적 배치(Dynamic Batching)가 매 프레임 CPU에서 버텍스를 합치는 방식이었다면, SRP 배처는 GPU 메모리의 CBuffer(Constant Buffer) 영역에 머티리얼 프로퍼티를 캐싱하여 렌더 상태 변경 비용을 거의 0으로 만듭니다.

GPU 상수 버퍼 CBuffer와 SRP 배처 메모리 구조
▲ CBuffer 상수 버퍼 구조를 통한 SRP 배처 드로우콜 최적화 메커니즘

2.1 CBUFFER_START(UnityPerMaterial) 선언 규칙

작성한 커스텀 셰이더가 SRP 배처와 호환되려면 셰이더 내의 모든 Properties 변수를 반드시 CBUFFER_START(UnityPerMaterial) 매크로 블록 안에 선언해야 합니다.

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

만약 머티리얼 프로퍼티 중 단 하나라도 UnityPerMaterial 버퍼 외부에 단독 선언되거나 셰이더 패스 간 불일치가 발생하면, 해당 셰이더는 SRP 배처 호환 목록에서 제외되어 일반적인 고비용 드로우콜로 폴백(Fallback)됩니다.


3. URP 렌더링 파이프라인 최적화 실무 지침

커스텀 셰이더를 작성할 때 연산 비용을 줄이고 모바일 기기 발열을 억제하기 위한 핵심 실무 지침은 다음과 같습니다.

최적화 영역 권장 실무 가이드라인 기대 효과
정밀도(Precision) 제어 위치 연산(Position)은 float(32비트), 색상 및 노멀/UV 좌표는 half(16비트)로 분리 모바일 GPU 레지스터 점유율 50% 절감 및 처리량 2배 향상
분기문(Branching) 제거 동적 if/else 문을 지양하고 step(), lerp(), saturate() 내장 함수 대체 스레드 분기(Divergence) 방지 및 연산 파이프라인 일관성 유지
텍스처 샘플링 축소 채널 패킹(R=Roughness, G=Metallic, B=AO)을 통해 단일 텍스처로 통합 메모리 대역폭 점유율 급감 및 텍스처 캐시 미스 억제
추가 광원(Lights) 제어 URP 설정에서 추가 광원을 'Per Pixel' 대신 'Per Vertex'로 변경하거나 제한 프래그먼트 연산 부하 급감 및 배터리 소모율 개선

4. 셰이더 그래프(Shader Graph) vs 코드 셰이더(HLSL) 실무 비교

유니티는 노드 기반의 셰이더 그래프(Shader Graph)를 기본 지원합니다. 프로토타이핑과 시각적 효과 검증에는 셰이더 그래프가 매우 유용하지만, 실무 상용화 프로젝트에서는 다음과 같은 기준에 따라 선택하여 개발해야 합니다.

4.1 셰이더 그래프의 적합 분야

환경 이펙트, 물(Water) 셰이더, UI 글리치 효과, 디졸브(Dissolve) 애니메이션 등 아티스트가 직접 직관적으로 파라미터를 조절해야 하는 특수 효과에 최적입니다. 셰이더 그래프 역시 URP 기본 설정 시 SRP 배처와 호환되도록 컴파일됩니다.

4.2 순수 코드 셰이더(HLSL)가 필수적인 경우

군중 캐릭터(Crowd Rendering), 복잡한 버텍스 애니메이션(Vertex Animation Texture, VAT), 커스텀 섀도우 캐스터(Shadow Caster) 패스 최적화 등 불필요한 연산 코드를 1바이트 단위까지 제거해야 하는 핵심 렌더링 모듈은 수동 작성된 HLSL 코드 셰이더가 압도적으로 높은 최적화 유연성을 제공합니다.


5. 결론 및 요약

유니티 URP 환경에서의 셰이더 개발은 단순한 비주얼 연출을 넘어, 플랫폼의 하드웨어 특성을 고려한 아키텍처 설계의 일환입니다. CBUFFER_START(UnityPerMaterial) 선언을 통한 SRP 배처 호환성 유지, 부동소수점 정밀도의 효율적 분배, 그리고 채널 패킹을 통한 텍스처 대역폭 관리는 모바일 기기에서도 부드럽고 안정적인 60FPS를 달성하는 핵심 원동력입니다.

  • HLSL 표준 전환: 빌트인 서피스 셰이더 대신 명시적인 정점/프래그먼트 구조를 채택하여 연산 제어권을 확보합니다.
  • SRP 배처 필수 유지: 모든 노출 프로퍼티를 UnityPerMaterial 상수 버퍼에 배치하여 드로우콜 상태 변경 오버헤드를 제거합니다.
  • 모바일 정밀도 최적화: 색상 및 벡터 연산에 half 정밀도를 적극 활용하고, 조건 분기를 수학적 내장 함수로 대체하여 GPU 효율을 극대화합니다.

Post a Comment

다음 이전