본문 바로가기

Graphics

색수차 효과 셰이더 만들기

(Unity URP / Unreal 5.1 버전 사용)

 

개요

 

유니티(좌)와 언리얼(우)의 색수차 효과

 

이번 글에서는, 색수차 (Chromatic  aberration) 효과를 셰이더와 메테리얼로 만들어보려 합니다.
유니티와 언리얼 모두 후처리 (Post processing) 효과에서 색수차 효과를 기능으로 제공하곤 있지만, 제공되는 기본적인 기능에 노이즈를 더한다던지 등의 기능을 추가해 더 커스텀하게 만들어보고 싶었어요. 🔧

먼저 언리얼 메테리얼 노드를 통해 보기 쉽게 색수차 효과를 만들어보고, 바로 이어서 유니티 셰이더 코드로도 작성해보겠습니다.

 


 

색수차 효과란

 

색수차 효과를 적용해본 샘플 이미지들

 

색수차 효과를 적용한 샘플 이미지들을 가져와봤습니다. 위의 이미지들에서 보이듯, 색상의 잔상이 보이는 느낌의 효과입니다. 유명한 틱톡 로고 또한 이러한 색수차 효과의 일종이라고 봐도 될 것 같습니다. 개인적으로 색수차 효과는 환각 효과를 줄 때나, SF틱한 장르에서 빛을 발하는 것 같아요.

자세한 설명은 아래 색수차 효과에 대해 잘 설명해주신 글을 인용해왔습니다. 출처 블로그 글에선 설명과 더불어 포토샵으로 색수차 효과를 만드는 방법도 설명해주시고 계시니, 잠깐 시간 내서 읽어보시는 것도 추천드립니다.

 

색수차 (chromatic aberration)

 이 현상은 렌즈를 사용하는 사진이나 영화에서 볼 수 있는 현상으로 빛의 파장의 길이에 따라 굴절률이 달라 생기는 렌즈의 오류이다.

요즘 카메라는 렌즈를 여러 개를 겹쳐 이런 오류를 보정하고 있다. 그 효과에서 아날로그의 향기를 느끼는 사람도 있어 임의로 구현하기도 한다. 이것은 일종의 시각적 트릭으로 좀 더 photorealistic 한 효과를 추가하기 위한 것이다(비록 이것이 오류고 현실에서는 눈으로는 볼 수 없는 현상이라고 할지라도).

이런 아날로그 렌즈 감성을 표현하기 위해 이 효과를 GTA, 블러드 본, 위쳐3, 디 오더 등. 극실사를 지향하는 AAA 게임들이 리얼리티를 위해 화면에서도 구현하기도 해서 한때 유행하기도 했다.

- Photoshop cc Tip #4 - 색수차 (chromatic aberration) 만들기 / 작성자 초이 中
 

Photoshop cc Tip #4 - 색수차 (chromatic aberration) 만들기

색수차 (chromatic aberration) :  이 현상은 렌즈를 사용하는 사진이나 영화에서 볼 수 있는 현...

blog.naver.com

 


 

색수차 효과 만들기

작업하기에 앞서 색수차 효과를 적용할 준비를 해줍니다.

언리얼 엔진에선 메테리얼 도메인을 Post Process 로 변경해주고, 레벨에 Post Process Volume 을 먼저 추가해주고 만든 메테리얼을 Post Process Volume 에 등록해주면 됩니다.
유니티에선 스크립트 단에서 Graphics.Bilt() 메소드를 사용하면 되므로, 별도로 신경 쓸 것 없이 편하게 셰이더 코딩을 하면 됩니다.

언리얼에서 메테리얼을 포스트 프로세싱에 등록하는 법은 아래 영상을 참고해주시면 됩니다. 언리얼 4버전이긴 하지만, 큰 차이는 없습니다.

 

 

색수차 효과를 만드는 방법은 간단합니다. 앞서 소개드린 블로그 글의 내용처럼 R, G, B 채널을 각각 따로 움직여주면 됩니다.  채널을 움직이기에 앞서, 각 채널을 움직여줄 방향 벡터를 먼저 만들어줍시다.

 

해당 벡터의 구성 요소로는, 색상 채널을 얼마나 움직일지의 크기값과, 어떤 방향으로 움직일지의 각도값이 있을 것입니다.

먼저 각도값은 라디안 값으로 변환한 뒤 'float2(cos( 라디안 값 ), sin( 라디안 값 ))' 의 형태로 방향 벡터를 회전시켜줍니다. 벡터를 회전시켜 준 뒤엔, 전체 채널의 움직임 크기를 결정하는 Intensity 파라미터와 R 채널의 움직임 크기를 조절하는 R_Range 파라미터를 사용해 해당 벡터의 크기를 조정할 수 있게 해주면 됩니다.

 

색수차 효과의 R채널 움직임 메테리얼 노드 구조와 적용한 예시

 

이 과정을 G와 B 채널 당 하나씩, 총 두번 반복해서 RGB 채널 모두가 개별로 움직일 수 있도록 만들어주면 완성입니다.

 

색수차 효과를 적용한 예

 

완성된 메테리얼 노드와 유니티 셰이더 코드는 아래와 같습니다.

 

색수차 효과 최종 메테리얼 노드

···

float _Intensity;

float _RChannelAngle;
float _GChannelAngle;
float _BChannelAngle;

float _RChannelRange;
float _GChannelRange;
float _BChannelRange;

fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);

    // 색수차 효과의 적용 각도 라디안값으로 변환
    float rRadian = radians(_RChannelAngle);
    float gRadian = radians(_GChannelAngle);
    float bRadian = radians(_BChannelAngle);

    // 색수차 효과 벡터의 크기를 조정
    float rMotionValue = _RChannelRange * _Intensity * 0.001;
    float gMotionValue = _GChannelRange * _Intensity * 0.001;
    float bMotionValue = _BChannelRange * _Intensity * 0.001;

    // 각도에 맞게 색수차 효과를 내줄 벡터 회전
    float2 rMotion = float2(cos(rRadian), sin(rRadian)) * rMotionValue;
    float2 gMotion = float2(cos(gRadian), sin(gRadian)) * gMotionValue;
    float2 bMotion = float2(cos(bRadian), sin(bRadian)) * bMotionValue;

    // 채널별 UV에 색수차 효과 벡터를 더해 이미지를 채널별로 움직임
    col.r = tex2D(_MainTex, i.uv + rMotion).r;
    col.g = tex2D(_MainTex, i.uv + gMotion).g;
    col.b = tex2D(_MainTex, i.uv + bMotion).b;

    return col;
}

···

 


 

화면의 외곽으로 갈수록 색수차 효과 강해지게 하기

개요에서 확인했던 유니티와 언리얼의 기본 색수차효과에선, 화면 중앙부일때는 색수차 효과가 거의 적용되지 않고, 화면 외곽으로 갈수록 효과가 강해지는 것을 확인하실 수 있습니다. 해당 기능을 추가해보겠습니다.

 

SDF왜곡 셰이더 만들기 글에서 다뤘던 distance() 함수를 활용하면 간단하게 만들 수 있습니다. distance() 함수를 사용해 화면 중앙(UV좌표 0.5, 0.5)과 화면 UV좌표와의 거리를 구하고, 이 값을 효과의 강도 값에 최종적으로 곱해주면 됩니다.

화면 중앙일 경우 0.5, 0.5 좌표와 동일하므로 distance로 연산한 결과값은 0으로 효과는 적용되지 않겠죠. 반대로 화면 외곽일 경우엔 결과값은 0.7 가량으로, 중앙에 비해서 효과의 강도가 강해지게 됩니다.

 

기존의 메테리얼 노드에서 distance 연산을 하는 노드들을 추가하고, lerp 노드와 DistanceParam 변수를 사용해 distance 효과의 강도를 설정할 수 있게 했습니다. 결과값 강도 보정용 상수 0.001 은 multiply 노드 안으로 옮겨 간소화했습니다. 아래 이미지의 노드 구조를 G, B 채널 부분에도 동일하게 추가해주면 됩니다.

최적화를 위해선 distance 연산 결과값을 텍스처화한 텍스처를 distance 연산 부분 대신 사용할 수 있습니다.

 

distance 를 활용한 기능을 추가한 메테리얼 노드

 

유니티 셰이더에서도 distance 연산부를 추가하고, lerp와 _DistanceParam 변수를 사용해 효과를 조정할 수 있게 동일한 작업을 해주면 됩니다.

 

···

float _Intensity;

float _RChannelAngle;
float _GChannelAngle;
float _BChannelAngle;

float _RChannelRange;
float _GChannelRange;
float _BChannelRange;

float _DistanceParam; // Distance 효과 강도 조정용 변수

···

    // 색수차 효과 벡터의 크기를 조정
    float rMotionValue = _RChannelRange * _Intensity * 0.001;
    float gMotionValue = _GChannelRange * _Intensity * 0.001;
    float bMotionValue = _BChannelRange * _Intensity * 0.001;

    // Distance 함수를 사용해 화면 외곽부로 갈수록 효과 강해지게 하기
    float distanceValue = distance(float2(0.5, 0.5), i.uv);

    // Distance 값 적용
    rMotionValue = lerp(rMotionValue, rMotionValue * distanceValue, _DistanceParam);
    gMotionValue = lerp(gMotionValue, gMotionValue * distanceValue, _DistanceParam);
    bMotionValue = lerp(bMotionValue, bMotionValue * distanceValue, _DistanceParam);

···

 

색수차 효과에 distance 를 추가해 적용한 예

 

간단한 추가 작업을 해준 뒤엔 위와 같이 화면 중앙으로 갈수록 효과의 강도가 약해지고, 화면 외곽으로 갈수록 효과가 강해지는 모습을 볼 수 있습니다!

 


 

색수차 글리치 효과 추가하기

기왕 색수차 효과를 만들어 보았는데, 이대로 끝내기는 아쉽죠. '색수차 효과란' 단락에서 다뤘던 틱톡 로고처럼 색수차 효과를 활용한 글리치 효과도 만들어 보겠습니다.

글리치를 적용하는 방법은 크게 두가지가 생각났습니다.
첫번째는 Intensity 값에 노이즈 값을 연산해주는 방식이었고, 두번째는 색수차 효과가 적용되는 방향 각도를 노이즈 값을 사용해서 바꿔주는 방식이었습니다. Intensity 값에 연산하는 첫번째 방법은 한 방향으로만 동작하기에 뭔가 풍부한 느낌이 덜할 것 같아, 두번째 방식으로 진행했습니다.

노이즈 값을 채널의 각도 변수와 더해주는 아래의 노이즈 연산부를 G, B 채널 부분에도 추가해주면 됩니다.

 

글리치용 노이즈 부분의 메테리얼 노드

 

색수차 글리치 적용 예

 

···

float _isGlitchOn;
float _GlitchSpeed;


// Random 함수
float random (in float2 st)
{
    return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
}


fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);

    // Random 함수를 사용해 색수차 글리치 애니메이션 추가
    float rand = random(floor(i.uv + float2(floor(_Time.y * _GlitchSpeed), 0)));

    // 색수차 효과의 적용 각도 라디안값으로 변환
    // 랜덤 값을 더해 각도를 불규칙적으로 변경해 글리치 효과 생성
    float rRadian = radians(_RChannelAngle + rand * 360 * _isGlitchOn);
    float gRadian = radians(_GChannelAngle + rand * 360 * _isGlitchOn);
    float bRadian = radians(_BChannelAngle + rand * 360 * _isGlitchOn);
    
···

 

유니티 셰이더 코드에선 노이즈 텍스처 대신 random 함수를 통한 노이즈 값을 사용했는데 (텍스처 이미지 찾기 귀찮아서... 😂), 최적화를 위해선 언리얼 메테리얼 노드에서 작업한 방식과 동일하게 노이즈 텍스처를 사용하는 편이 좋습니다.

 

유니티 셰이더 코드 전문은 링크에서 확인하실 수 있습니다.

 


 

자료 및 이미지 출처

색수차 효과 샘플 이미지 1 - 날아다니는 흰 새 (Unsplash.com)
색수차 효과 샘플 이미지 2 - 사이버펑크 엣지러너

 

 

 

'Graphics' 카테고리의 다른 글

셰이더에서의 Sin(time) 그래프의 이해  (0) 2023.10.28
색상 간의 혼합 - 기초 블렌딩 연산  (0) 2023.10.09
SDF (Signed Distance Field)  (0) 2023.09.17
색체계와 색의 구성요소  (0) 2023.07.15
디지털 색상과 수  (0) 2023.06.25