Post

URP Light2D + Parallax 청크 시스템: 3D 프로젝트에 값싼 2D 라이팅 적용기

3D 씬에 URP Light2D를 청크 기반 Parallax + 오브젝트 풀과 결합해 Directional/Light Probe 대비 저비용 라이팅을 구현한 실전 가이드. 렌더 순서, 라이트 타입·Blending·Normal Maps, ScriptableObject 데이터·SRDebugger 연동, 성능 팁과 코드까지.

URP Light2D + Parallax 청크 시스템: 3D 프로젝트에 값싼 2D 라이팅 적용기

Hits


왜 3D 씬에 Light2D를? (문제정의 & 목표)

  • 문제정의: 대규모 3D 씬에서 Directional + Light Probe + 실시간 섀도우는 GPU/CPU/메모리 비용이 높음. 모바일/저사양에서 프레임 낙폭.
  • 핵심 목표
    • 시각 품질 유지: 글로벌 톤, 메인 포커스, 캐릭터 주변 라이트, 햇살(Shaft) 연출.
    • 낮은 비용: 2D 전용 라이트 패스 + 청크 트리거 + 오브젝트 풀로 스폰/업데이트/반납 비용 최소화.
    • 운영 친화: ScriptableObject + SRDebugger로 디자이너 및 기획측에서 런타임으로 라이트 등을 튜닝 가능하도록 툴 제공.

URP 2D Light 렌더링 순서 (개념 정리)

  1. 카메라 설정: 뷰포트/정렬(Sorting Layer & Order)
  2. 씬 오브젝트 렌더링: 스프라이트/메시 등 기하 렌더
  3. Light2D 렌더 패스: color, intensity, falloff, blend, mask기하 결과에 합성

핵심: 여러 Light2D가 겹치면 Blend StyleOverlap Operation(Additive / Alpha Blending)에 따라 최종 결과가 달라짐.


Light2D 타입·핵심 프로퍼티 요약

Light Type 요약

  • Global: 전역 톤(앰비언트)
  • Spot: 부채꼴/원뿔(Inner/Outer 각도 + Radius)
  • Freeform: 폴리곤으로 영역 지정
  • Sprite: 스프라이트 알파를 쿠키처럼 사용 (모양 기반 빛)

공통 프로퍼티

  • Color: 색. 어두운 색은 Intensity가 높아도 덜 밝아 보일 수 있음 → 색·세기 동시 조정
  • Intensity: 밝기(세기). 보통 0–1, 연출상 1 초과도 가능(겹침 과포화 주의)
  • Radius: 최대 범위(Spot/Point 중요). 너무 크면 오버드로/합성 비용 증가
  • Falloff Strength: 중심→테두리 감쇄 속도. 높을수록 가장자리로 빠르게 어두워짐

Blending & Overlap — 라이트 중첩 규칙

Blend Style (Renderer2DData에서 정의)

  1. Default: 일반적 합성—자연스러움
  2. Additive: 밝기/색을 덧셈 누적—네온/글로우
  3. Multiply with Mask (R): 마스크(R) 곱—검은 영역 약화
  4. Additive with Mask (R): 마스크(R) + 덧셈—국부 글로우·하이라이트


Overlap Operation

  • Additive: 겹칠수록 밝아짐—시선 유도/효과 강조
  • Alpha Blending: 알파 반영 부드러운 섞임—Light 순서 영향

프로젝트 표준 라이트 구성 (3종 프리셋)

1) Ambient(Global) — 전역 분위기

  • Light Type: Global
  • Color/Intensity: 씬 톤 + Intensity ≈ 0.25
  • Blend Style: Default
  • Overlap: Additive

2) Main Light(Spot) — 카메라 뷰포트 중심 노출

  • Type: Spot
  • Radius: 0~50
  • Falloff Strength: 0 (뷰포트 강조)
  • Normal Maps: Quality=Accurate, Distance≈Radius
  • Blend/Overlap: Default / Additive

3) Character Spot(Spot) — 등불/휴대 광원

  • Type: Spot
  • Radius: 0~10
  • Falloff Strength: ≈0.5 (자연스러운 감쇄)
  • Intensity: Main보다 조금 높게
  • Normal Maps: Accurate, Distance≈Radius
  • Blend/Overlap: Default / Additive

Normal Maps (2D 표면에 라이팅 질감 부여)

  • 정의: RGB에 픽셀 단위 법선 벡터 저장 → 하이라이트/음영을 2D에 구현
  • Quality: Accurate (정확)/ Fast (가성비)
  • Distance: 라이트가 노멀에 영향 주는 최대 거리 → Radius와 유사/약간 크게
  • 예외: 바닥 구름(Floor) 라이트는 Normal Maps 비활성

데이터 파이프라인 & 씬 구조

  • 배치 위치: GameScene / Main Camera / Lights
  • 에셋 경로: Assets/ExternalAssets/TileMap/Light/Dusk.asset
  • 노출 항목: Color, Intensity, Enable Spot Light, LightShaft Enabled
  • 생성/복제: Create → Cocone → Light2DSetting 또는 Cmd/Ctrl + D
  • SRDebugger 연동: 새 데이터는 자동 등록, 런타임 변경 가능
  • 헤더 일본어 설명: 「色 / 明るさ(強度) / スポットライト有効 / サンシャフト有効 はランタイムで安全に変更できます。」

햇살(Light Shaft) 제작 가이드 (Sprite Light)

  • Light Type: Sprite 사용, 프리팹(LightShaft)으로 만들어 맵 오브젝트 배치
  • 리소스(임시)
    • Assets/ExternalAssets/TileMap/Sprite/LightShafts/gradient light.png
    • Sprite_SunSpots_2 (바닥 구름 그림자), shadow2 (햇살 패턴)
  • 자동화 난점: Sprite Light 수/패턴이 많아 수작업 배치가 현실적
  • 주의: Floor(바닥) 구름 라이트는 Normal Maps Off

Parallax + Chunk + Light2D: 시스템 아키텍처

핵심 아이디어

  • 청크 트리거 + 오브젝트 풀스폰/반납 비용 최소화
  • 플레이어/카메라 이동에 따라 필요 청크만 활성, 멀어진 청크 즉시 반납

구성 요소

  • ParallaxSystemManager: 타깃 추적, triggerChunkSize, RefreshAllLayers, per-frame OnUpdate
  • IParallaxLayerHandler: Initialize/RefreshChunks/OnUpdate/CleanUp/SetEnabled/CollectRuntimeTags
  • StaticEffectLayerHandler / MovingEffectLayerHandler: 정적/이동 이펙트 샘플링·풀링·카링
  • ParallaxLightRuntimeTag: Light2D 캐시 + BaseScale 보관(이중 스케일 방지), Static/Moving 구분
  • EffectLayerSetting (SO): OverrideLight2D, LightIntensity/Color/ScaleMultiplier, 샘플링(gridCount, density), 카링(maxDistance), Overlap(minSeparation, rejectionTry), Changed 이벤트

코드 하이라이트 (발췌)

ParallaxSystemManager — 청크 전환 & 업데이트

```csharp private void Update() { if (!_isInitialized) return;

1
2
3
4
5
6
7
8
9
Vector2Int newTriggerChunk = GetTriggerChunk(targetTransform.position);
if (newTriggerChunk != lastTriggerChunk)
{
    RefreshAllLayers(false);   // 청크 교체 시 스폰/반납
    lastTriggerChunk = newTriggerChunk;
}

foreach (var h in layerHandlers)
    h.OnUpdate(targetTransform.position); }

주의 : UniRx가 R3로 업데이트 되면서 R3에서 MessageBroker는 MessagePipe로 변경되었음.

This post is licensed under CC BY 4.0 by the author.