본문 바로가기

Graphics/Unity

사각형 패턴 셰이더 만들기

(유니티 빌트인 렌더 파이프라인 - Vertex&Fragment 셰이더 사용)

 

개요

사각형 패턴 연출을 셰이더로 만들어보려고 하던 도중 아래 셰이더 코드를 찾아, 뜯어보며 만들어보기로 했습니다.

 

 

Shadertoy

 

www.shadertoy.com

작업된 최종 패턴 애니메이션

 

 


 

UV 회전 및 UV의 중앙점 옮기기

 

UV 중앙점 이동 및 회전 적용

 

···

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

    float2 uv = i.uv;
    uv -= 0.5; // UV 기준점(0,0)을 중앙으로 옮기기

    // ----- UV 회전 - 회전 행렬을 사용
    float rotation = radians(_Rotation);
    // 사각형의 각도, 라디안값으로 변환 (숫자 45를 각도 45도로)

    float2x2 m = float2x2(cos(rotation), -sin(rotation), sin(rotation), cos(rotation));
    uv = mul(m, uv);
    // 벡터 uv와 행렬 m을 곱연산
    // ----- UV 회전 끝

    float2 pos = (_QuadSize * uv) + 0.5;
    // UV를 _QuadSize 만큼 타일링한 이후 UV의 중앙점(0.5, 0.5)을 메시의 중앙점으로 이동

    ···
}

 

단순하게 UV에 값을 곱해 타일링하게 되면, 좌측 하단을 기준으로 UV가 타일링되게 됩니다.
중앙을 기준으로 타일링 될 수 있도록 UV의 중앙점 (0.5, 0.5)을 옮겨줍니다.

그 중간에선, UV에 회전 행렬을 곱해 _Rotation 값 만큼 UV가 회전되도록 합니다.

행렬을 보면서 이게 뭔가... 싶었는데, 그래픽스를 공부하는 친구에게 물어보니 회전행렬이라고 알려주더라고요.
연산 과정의 내부는 심연처럼 느껴져서, '회전 행렬에 각도 값을 넣어주고, 벡터와 곱하면 회전한다' 정도로만 이해하고 넘어가기로 했습니다.

 


 

패턴을 만들 수 있도록 UV 타일화하기

 

패턴 생성이 가능하도록 UV를 타일화한 예시

 

원본 코드의 도움을 받아 UV를 회전시켰으니, 이제 패턴을 만들 수 있도록 UV를 타일의 형태로 바꿔줘야 합니다.
해당 부분의 코드는 아래와 같은데, 차근차근 하나씩 뜯어보며 맛보겠습니다.

 

···

float2 rep = frac(pos);
// frac() : pos의 소숫점을 반환함
// uv.x 의 값이 1.0 ~ 1.9 인 구간이 있다면 0.0~0.9 반환
// 2.0~2.9도 동일하게 0.0~0.9 반환

float2 adjust = float2(min(rep.x, 1.0 - rep.x), min(rep.y, 1.0 - rep.y));
// min(a, b) : 두 값 중 작은 값을 반환
// 현재 uv 좌표를 토대로 만든 float2(x,y)를 사용하고 있는데, 두가지 중 작은 값을 반환
float dist = min(adjust.x, adjust.y) * 2.0;
// adjust 의 x와 y 값을 둘 중 작은 값으로 합침

···

 

1. frac() 함수를 적용한 값을 r과 g로 표시한 예시

 

첫 단계는 frac() 함수를 통해 UV값이 특정 범위에서 반복되도록 만들어주는 것입니다.

rep 변수에는 frac() 함수를 통해서 타일링과 회전이 적용된 pos의 값을 0~1 사이에서 반복되게 한 값이 들어갑니다. 보기 쉽게 rep의 x 값을 그래프로 나타낸다면 아래 이미지와 같은 모습이 됩니다.

일반적인 y=x 그래프와 frac() 함수를 적용한 그래프

 

2. min() 함수를 통해 UV의 중앙점 부분의 값을 1로 보정

 

두 번째 단계는, 반복되는 UV의 중앙점(0.5, 0.5)를 기준으로 패턴의 사각형이 커지거나 작아질 수 있게
min() 함수를 이용해서 중앙점의 값이 1이 되도록 보정해주는 것입니다.

adjust 변수의 값이 되는 min(rep.x, 1.0 - rep.x) 은, 아래의 좌측 그래프와 같이 값이 0.5를 기점으로 꺾여 내려갑니다. (adjust.y 도 rep.y 값을 동일하게 계산)

다만, 위 연산 결과의 최대값은 0.5이기 때문에, 추후 색과 연산하거나 알파로 표현할 때 등의 편의를 위해 2를 곱해 최대값이 1이 되도록 만들어줍니다. 그렇게 되면, x 의 값은 아래 우측 그래프와 같이 나타내집니다.

 

min() 함수와 최대값이 1이 되도록 보정한 그래프

 


 

사각형 만들고 크기 조절하기

패턴을 만들 수 있도록 밑작업은 끝났으니, 이제 위의 값을 바탕으로 사각형을 만들고 그 크기를 조절하면 됩니다.
이전의 버텍스 그라데이션 셰이더 처럼 위에서 작업한 그라데이션 값(0~1) 과 비교 연산할 threshold 값을 만들어줍니다.
(dist 값이 threshold 값보다 크면 보여지도록)

그리고는, threshold 값에 시간 값을 더해줘서 아래 gif처럼 threshold 값이 시간에 따라 변화되도록 만들어주면 됩니다.

 

threshold 에 Sin(Time)을 적용해 애니메이션한 결과 (_QuadSize 값 6 기준)

 

threshold 를 계산하고 애니메이션을 적용하는 코드는 다음과 같습니다.

 

···

float thresholdX = floor(pos.x + 0.5 * _QuadSize) / _QuadSize;
thresholdX -= 0.5;
// abs()와 floor()를 통해 0을 기준으로 계단 형태의 값을 만들어주고, 타일링 수치로 보정

float thresholdY = floor(pos.y + 0.5 * _QuadSize) / _QuadSize;
thresholdY -= 0.5;

float threshold = min( 1 - abs(thresholdX),  1 - abs(thresholdY));
// 중앙이 1이 되도록 threshold 값을 반전해 합침

col.a = dist > threshold + sin(_Time.y);
// 시간에 따라 값이 변화하도록 적용 후 패턴 애니메이션을 알파값으로 반환

···

threshold 값들을 rgb값으로 표시한 예 (_QuadSize 값 6 기준)

 

threshold 값이 한 타일 간격으로 다르게 적용될 수 있도록 floor() 를 통해 값을 계단식으로 보정해줍니다.
floor 함수를 적용하지 않을 경우, 사각형의 크기가 uv 좌표에 따라 선형적으로 적용되어 (사각형의 각 꼭지점이 모두 같은 값을 통해 변화하지 않고, 꼭지점마다 다른 값을 통해 변화함) 사다리꼴 모양이 됩니다.

pos.x 값에 0.5를 더하고, 소숫점값을 버리는 floor 함수를 통해 계단으로 만들어 준 뒤에 abs 함수를 통해 절대값으로 만들어주면 아래의 좌측 그래프의 형태로 만들어지게 됩니다.
(다만, 코드에서는 타일링되는 정도인 _QuadSize 값으로 보정해 threshold 값이 아래 그래프와는 달리 0~1 사이가 되도록 만들어줍니다.)

이후에 1 에 해당 threshold 값을 빼서 값을 역전시켜주면 아래의 우측 그래프와 같은 모습이 되고,
thresholdX 와 thresholdY 값을 min() 을 통해 합쳐준 뒤 rgb 로 표시한 값은 위의 우측 이미지와 같이 됩니다.

그리고 앞에서 적었듯, Sin(Time) 을 더해줘 애니메이션을 만들어줍니다.

floor() 를 사용한 그래프 예

 


 

셰이더 응용

 

조금만 코드를 건드리면 위와 같이 조금씩 다른 패턴 애니메이션을 만들 수 있습니다.

 

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

 

GitHub - rkato0128/ShaderStudy_Unity

Contribute to rkato0128/ShaderStudy_Unity development by creating an account on GitHub.

github.com