Unity URP 포스트 프로세싱 풀다운 효과와 화면 피격(Hit-flash) 구현법

Unity URP의 Full Screen Pass와 HLSL 셰이더로 화면을 아래로 끌어내리는 풀다운 효과와 피격 플래시를 제작한다. UV 왜곡, 마스크, 시간 감쇠, 블렌딩 순서와 성능 점검 방법을 단계별로 정리한다.

✓ 복사 완료!
챕터렌더링 파이프라인 & 배칭
난이도중급
상태● 검토 완료
대상 엔진
Unity
최종 업데이트
Unity URP 포스트 프로세싱 풀다운 효과와 화면 피격(Hit-flash) 구현법

핵심 요약

포스트 프로세싱 풀다운 효과는 화면 UV의 y좌표를 마스크와 시간값으로 아래 방향으로 이동시키는 방식이며 Hit-flash는 원본 색상을 흰색 또는 지정 색상으로 보간해 짧은 피격 피드백을 만드는 효과다. Unity URP에서는 Full Screen Pass Renderer Feature에 한 개의 머티리얼을 연결하고 카메라 색상 텍스처를 샘플링하면 두 효과를 한 패스로 합칠 수 있다.

효과를 분리된 패스로 여러 번 그리기보다 같은 해상도의 화면 효과라면 하나의 셰이더에서 UV 왜곡 → 색상 플래시 순서로 처리하는 편이 대역폭과 관리 비용을 줄인다.

포스트 프로세싱 풀다운 효과는 어떻게 동작하는가?

풀다운은 화면 전체가 수직으로 내려앉거나 찢기는 듯 보이게 하는 스크린 스페이스 왜곡이다. 메시를 변형하지 않고 최종 화면의 UV를 바꾸므로 캐릭터, 배경, 파티클을 한 번에 흔들 수 있다.

기본 이동량은 다음처럼 정의할 수 있다.

offsetY=amount×mask(u,v)×envelope(t)offsetY = amount \times mask(u,v) \times envelope(t)
  • amount: 최대 이동 거리다. UV 기준이므로 보통 0.01~0.08 범위에서 시작한다.
  • mask(u,v): 화면 위치별 영향도다. 노이즈, 세로 그라데이션, 원형 마스크를 사용할 수 있다.
  • envelope(t): 시간에 따른 강도다. 빠르게 내려가고 천천히 복귀하는 곡선이 피격감에 잘 맞는다.
방식장점주의점어울리는 상황
균일 UV 이동가장 저렴하고 읽기 쉽다단순한 슬라이드처럼 보일 수 있다전환, 컷신
노이즈 마스크찢김·충격 표현이 좋다노이즈 텍스처 샘플 비용이 든다강한 공격, 글리치
중심 기준 마스크공격 지점이 전달된다월드 좌표를 화면 UV로 변환해야 한다폭발, 보스 공격

화면 상단과 하단의 마스크 값에 따라 서로 다른 크기로 아래로 당겨지는 포스트 프로세싱 풀다운 효과

Unity URP에서 Full Screen Pass를 어떻게 구성할까?

  1. URP Renderer Data 에셋을 열고 Add Renderer Feature에서 Full Screen Pass Renderer Feature를 추가한다.
  2. 아래 셰이더를 사용하는 머티리얼을 할당하고 이벤트는 일반적으로 After Rendering Post Processing 또는 프로젝트의 톤 매핑 순서에 맞는 지점으로 둔다.
  3. 게임 코드에서 _PullAmount, _HitFlash, _HitColor를 갱신한다. UI까지 효과에 포함할지는 카메라 및 Canvas 렌더 순서로 결정한다.

렌더 순서는 결과를 크게 바꾼다. 먼저 UV를 왜곡한 뒤 그 결과에 플래시를 적용하면 화면 전체가 같은 타이밍으로 밝아진다. 반대로 플래시 후 UV를 왜곡하면 색상 경계까지 끌려가므로 글리치 느낌이 강해진다.

풀다운과 Hit-flash를 한 셰이더 패스로 구현하기

아래 예시는 URP의 블릿(Blit) 소스 텍스처를 샘플링하는 전체 화면 셰이더다. TEXTURE2D_X는 XR 단일 패스 인스턴싱을 고려한 URP 매크로다.

Shader "Hidden/PulldownHitFlash"
{
    Properties
    {
        _PullAmount ("Pull Amount", Range(0, 0.1)) = 0
        _PullCenter ("Pull Center", Range(0, 1)) = 0.5
        _PullWidth ("Pull Width", Range(0.01, 1)) = 0.25
        _HitFlash ("Hit Flash", Range(0, 1)) = 0
        _HitColor ("Hit Color", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { "RenderPipeline" = "UniversalPipeline" }
        ZWrite Off ZTest Always Cull Off

        Pass
        {
            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            TEXTURE2D_X(_BlitTexture);
            SAMPLER(sampler_LinearClamp);

            CBUFFER_START(UnityPerMaterial)
                float _PullAmount;
                float _PullCenter;
                float _PullWidth;
                float _HitFlash;
                float4 _HitColor;
            CBUFFER_END

            struct Attributes { uint vertexID : SV_VertexID; };
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            Varyings Vert(Attributes input)
            {
                Varyings output;
                output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
                output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
                return output;
            }

            half4 Frag(Varyings input) : SV_Target
            {
                float distanceFromCenter = abs(input.uv.y - _PullCenter);
                float mask = 1.0 - smoothstep(0.0, _PullWidth, distanceFromCenter);

                float2 uv = input.uv;
                uv.y = saturate(uv.y - _PullAmount * mask);

                half4 color = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
                color.rgb = lerp(color.rgb, _HitColor.rgb, _HitFlash);
                return color;
            }
            ENDHLSL
        }
    }
}

이 구현은 saturate로 화면 가장자리의 UV를 고정한다. 가장자리를 복제하는 연출에는 적합하지만 검은색으로 밀려나가게 하려면 saturate를 제거하고 Clamp가 아닌 Border 처리가 가능한 별도 샘플링 전략을 사용해야 한다.

원본 화면에서 수직 UV 왜곡을 적용한 뒤 흰색 Hit-flash가 짧게 겹쳐지는 게임 전투 화면

Hit-flash 강도와 지속 시간은 어떻게 제어할까?

피격 플래시는 일정한 값으로 켰다 끄기보다 빠른 상승과 감쇠를 사용해야 자연스럽다. 0~1로 정규화한 시간 t에 대해 다음 곡선을 사용할 수 있다.

flash(t)=(1−t)3flash(t) = (1 - t)^3

Unity C#에서는 코루틴 없이 Update에서 남은 시간을 줄이는 방식이 충분하다.

using UnityEngine;

public sealed class ScreenHitFeedback : MonoBehaviour
{
    [SerializeField] Material effectMaterial;
    [SerializeField] float duration = 0.12f;
    float remaining;

    public void PlayHit(float pullAmount = 0.025f)
    {
        remaining = duration;
        effectMaterial.SetFloat("_PullAmount", pullAmount);
    }

    void Update()
    {
        float t = duration <= 0f ? 1f : 1f - remaining / duration;
        float flash = Mathf.Pow(1f - Mathf.Clamp01(t), 3f);

        effectMaterial.SetFloat("_HitFlash", flash);
        effectMaterial.SetFloat("_PullAmount", flash * 0.025f);
        remaining = Mathf.Max(0f, remaining - Time.deltaTime);
    }
}

여러 공격이 짧은 간격으로 들어올 수 있다면 단순히 시간을 덮어쓰는 방식부터 사용해도 된다. 공격 위치별 마스크, 공격별 색상 누적, 우선순위가 실제로 필요해질 때만 이벤트 큐를 추가하는 편이 낫다.

화면 피격 포스트 효과의 성능과 품질을 점검하는 방법

  • 카메라 색상 텍스처를 읽는 전체 화면 패스는 해상도에 비례해 비용이 증가한다. 모바일에서는 Render Scale과 GPU 프레임 타임을 함께 확인한다.
  • 노이즈 텍스처를 추가한다면 1회 샘플링으로 제한하고 반복 타일링이 눈에 띄지 않는 작은 텍스처를 사용한다.
  • smoothstep 마스크는 경계가 부드럽지만 화면 중심이 너무 넓으면 단순한 화면 흔들림처럼 보인다. _PullWidth를 낮춰 공격의 초점을 만든다.
  • UI는 별도 Overlay Canvas라면 이 패스의 영향을 받지 않는다. 체력바까지 흔들지 않으려면 이 구성이 오히려 유리하다.

자주 묻는 질문 (FAQ)

Hit-flash는 흰색만 사용해야 하나요?

아니다. 흰색은 범용성이 높지만 독, 화염, 전기처럼 피해 속성을 구분해야 한다면 _HitColor에 상태 색상을 넣는 편이 명확하다. 단, 화면 전체를 높은 채도로 덮으면 적 식별성이 떨어질 수 있다.

풀다운 효과와 카메라 셰이크는 함께 써도 되나요?

가능하다. 카메라 셰이크는 공간과 관성을 UV 풀다운은 충격과 화면 반응을 전달한다. 둘 다 강하면 멀미를 유발하므로 지속 시간은 0.1~0.2초부터 조정한다.

효과를 여러 카메라에 적용하려면 어떻게 하나요?

카메라마다 서로 다른 머티리얼 인스턴스를 사용하거나 URP Renderer Feature에서 카메라 필터를 둔다. 하나의 머티리얼을 공유한 상태에서 런타임 값을 바꾸면 다른 카메라도 같은 값을 보게 된다.

정리

풀다운 효과는 UV의 수직 이동, Hit-flash는 색상 보간이라는 단순한 두 연산으로 시작할 수 있다. 먼저 한 개의 URP Full Screen Pass에서 왜곡 후 플래시를 적용하고 플레이 테스트에서 부족할 때만 노이즈 마스크나 공격 위치 기반 마스크를 추가하면 된다.