Frame Debugger로 진단한 lilToon 런타임 투명화 문제
프로젝트 요약
캐릭터의 lilToon 멀티 머티리얼을 런타임에서 불투명과 투명 상태로 전환하는 기능을 구현했습니다.
Alpha와 Blend 값은 GPU에 정상적으로 전달됐지만 불투명 셰이더 Variant가 계속 실행되는 문제를 Frame Debugger로 분리해 확인했습니다. lilToon 내부 키워드 매핑을 추적한 뒤 렌더링 상태와 키워드를 함께 동기화해 해결했습니다.

| 항목 | 내용 |
|---|---|
| 프로젝트 | 3D 캐릭터 기반 비주얼 노벨 |
| 담당 | Unity 클라이언트 개발 |
| 기술 | Unity, C#, lilToon, Shader Variant, Frame Debugger |
| 직접 구현 | 런타임 투명·불투명 전환, 렌더링 상태 동기화, 원본 상태 복구 |
| 핵심 성과 | CPU 프로퍼티 변경과 GPU 셰이더 실행 경로를 분리해 Variant 불일치 진단 |
문제 상황
캐릭터의 등장·퇴장 연출을 위해 lilToon 멀티 머티리얼의 _Color.a와 Blend, Render Queue 관련 속성을 런타임에서 변경했습니다. Inspector에는 변경된 값이 표시됐지만 캐릭터는 투명해지지 않았습니다.
반면 실행 중 Inspector에서 해당 머티리얼을 클릭하면 같은 프로퍼티 값으로 투명화가 뒤늦게 적용됐습니다. 이 차이로부터 Alpha 계산 자체보다 Inspector 갱신 과정에서 추가적인 머티리얼 상태가 동기화되고 있다고 판단했습니다.
해결해야 했던 조건
- Inspector의 수동 갱신 없이 런타임에서 즉시 전환할 것
- 여러 파츠와 서브 머티리얼에 동일한 상태를 적용할 것
- 에디터 전용 API에 의존하지 않아 실제 빌드에서도 동작할 것
- 투명화에 필요한 렌더링 상태를 누락 없이 변경할 것
- 불투명 복귀 시 원래 상태와 키워드를 함께 복구할 것
원인 분석 방법
프로퍼티 설정 코드만 반복해서 수정하지 않고 CPU에서 바꾼 값과 GPU가 실행한 셰이더 경로를 분리해 확인했습니다.
flowchart LR A["런타임 프로퍼티 변경"] --> B["Frame Debugger로<br/>Draw Call 확인"] B --> C{"Color와 Alpha가<br/>GPU에 전달됐는가?"} C -->|"예"| D["프로퍼티 전달 문제 제외"] D --> E["Inspector 갱신 전후<br/>키워드 비교"] E --> F["lilToon 에디터와<br/>셰이더 소스 추적"] F --> G["Transparent Variant<br/>미선택 확인"]
Frame Debugger에서 Draw Call을 확인한 결과 스크립트가 변경한 Color와 Alpha 값은 GPU에 정상적으로 전달되고 있었습니다. 따라서 문제 범위를 다음과 같이 좁혔습니다.
프로퍼티 값은 변경됐지만 해당 값을 처리할 투명 셰이더 Variant가 선택되지 않고 있다.
Inspector 갱신 전후 키워드 비교
Inspector로 머티리얼을 갱신하기 전과 후의 키워드를 비교했습니다. 투명화가 적용된 상태에서는 UNITY_UI_CLIP_RECT가 활성화된다는 차이를 발견했습니다.
이름만 보면 UI 클리핑을 위한 키워드이므로 바로 투명 렌더링과 연결하기 어려웠습니다. 프로젝트에서 사용한 lilToon 버전의 에디터 코드와 셰이더 코드를 따라가며 실제 역할을 확인했습니다.

▼
<img
src=“/Portfolio/assets/after.png”
alt=“인스펙터 갱신 후 키워드 변화”
width=“720”
lilToon 셰이더 Variant 추적
lilToon 에디터 코드는 _TransparentMode == 2인 멀티 셰이더 머티리얼에 UNITY_UI_CLIP_RECT 키워드를 활성화합니다. 셰이더의 키워드 변환부에서는 이 키워드를 LIL_RENDER 2, 즉 Transparent 경로로 매핑합니다.
#if defined(UNITY_UI_CLIP_RECT) || defined(LIL_REFRACTION)
#define LIL_RENDER 2 // Transparent
#elif defined(UNITY_UI_ALPHACLIP) || defined(LIL_FUR)
#define LIL_RENDER 1 // Cutout
#else
#define LIL_RENDER 0 // Opaque
#endif따라서 Alpha와 Blend 값만 변경하면 값 자체는 전달되더라도 불투명 Variant가 계속 실행됩니다. Inspector를 클릭했을 때 투명화가 적용된 이유는 lilToon 에디터가 머티리얼 프로퍼티에 맞춰 키워드를 다시 동기화했기 때문이었습니다.
렌더링 상태와 키워드 동기화
투명 모드 전환 함수에서 색상 Alpha뿐 아니라 다음 상태를 함께 변경했습니다.
_TransparentMode,_BlendMode- Source/Destination Blend
- ZWrite, ZTest
- RenderType, Render Queue
- Outline Blend 상태
UNITY_UI_CLIP_RECT,_ALPHABLEND_ON키워드
private static void SetTransparentKeyword(
Material material,
bool enabled)
{
if (enabled)
{
material.EnableKeyword("UNITY_UI_CLIP_RECT");
material.EnableKeyword("_ALPHABLEND_ON");
}
else
{
material.DisableKeyword("UNITY_UI_CLIP_RECT");
material.DisableKeyword("_ALPHABLEND_ON");
}
}투명화와 불투명 복귀가 서로 다른 코드 경로에서 일부 값만 변경하지 않도록 하나의 Helper에서 대칭적으로 처리했습니다. 캐릭터의 여러 파츠에 사용된 머티리얼도 같은 진입점을 통해 전환했습니다.
결과
- Inspector 갱신 없이 런타임에서 즉시 투명·불투명 상태를 전환했습니다.
- Unity Editor와 실제 빌드에서 동일하게 동작하는 것을 확인했습니다.
- 파츠별 투명 머티리얼을 미리 복제해 준비하던 방식을 제거했습니다.
- 렌더링 상태 변경과 원복을 공통 Helper로 통합했습니다.
- Frame Debugger로 프로퍼티 전달과 셰이더 실행 경로를 분리해 진단했습니다.
- lilToon 패키지 소스를 추적해 셰이더 Variant 선택에 필요한 키워드를 확인했습니다.