Blog

ENGINEERING NOTE

[Unreal Engine] An introduction to Shader Art Coding

참고 영상 : kishimisu - An introduction to Shader Art Coding쉐이더는 기본적으로 GPU에서 실행되는 코드이며 캔버스의 각 픽셀의 위치 및 색상을 결정하는 과정이다.GPU는 기본적으로 씬의 모든 픽셀에 대해 병렬적으로 계산하여 실시

Unreal Engine

What is Shader?

쉐이더는 기본적으로 GPU에서 실행되는 코드이며 캔버스의 각 픽셀의 위치 및 색상을 결정하는 과정이다.

GPU는 기본적으로 씬의 모든 픽셀에 대해 병렬적으로 계산하여 실시간 그래픽 화면을 보여줄 수 있다.

쉐이더를 통해 다양한 시각적 효과와 애니메이션을 제작할 수 있다.

Basic HLSL

우선 언리얼 엔진에서 Material을 하나 생성하자.

그리고 우클릭을 한 뒤, Custom Node 하나를 생성한다.

생성 시 기본적으로 Float3값을 출력하는 흰색의 Material이 보인다.

Input Parameter 설정

![](/api/media/velog-e1bf635a95cfd2b05ea02ee17e626264)

사진과 같이 TexCoord 변수를 추가하고 출력 타입을 CMOT Float2로 변경하자.

HLSL 코드를 수정하고 TextureCoordinate 노드를 입력으로 연결하자.

그러면 출력값으로 Texture UV의 색상이 나오는 것을 확인할 수 있다.

Texture UV Coordinate

언리얼 엔진에서 텍스쳐는 기본적으로 왼쪽 상단을 기준점으로 피벗 되어있다.

(1, 1) 좌표는 녹색과 빨간색이 모두 1의 값을 지니므로 빨강 + 초록 = 노란색으로 표시되는 것을 볼 수 있다.

현재 UV 좌표계의 기준점이 왼쪽 상단에 있으므로 이를 중앙으로 옮겨보자.

cpp
float2 tex = TexCoord * 2 - 1;

return float3(tex.x, tex.y, 0.0);

UV length()

해당 텍스쳐에서 각 UV 좌표의 거리를 나타내보자.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex);

return float3(d, d, d);

해당 코드를 통해 UV좌표와 원점 사이의 거리를 나타낼 수 있다.

SDF (Signed Distance Function)

이제 여기에 Input Parameter로 Radius를 추가하고 값을 0.5로 설정하고 노드에 연결한 뒤, 코드를 다음과 같이 변경하자.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex) - Radius;

return float3(d, d, d);

그러면 매우 간단한 원 형태의 SDF를 나타내는 Texture가 나타난다.

SDF는 경계를 기준으로 음과 양의 부호의 값으로 나뉘기에 이를 이용하여 다양한 모양의 거리 함수를 찾거나 직접 만들 수 있다.

현재는 원을 기준으로 외부는 양수이며 내부는 음수인 Texture이다.

Edge Detection

이제 해당 HLSL 코드를 다음과 같이 바꿔보자.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex);

d -= Radius;

d = abs(d);

return float3(d, d, d);

원 내부의 음수값을 절대값 함수를 적용하면 다음과 같이 링 형태의 Texture가 생기는 것을 볼 수 있다.

경계면을 더욱 뚜렷하게 보이도록 하기 위해 다음 코드를 추가한다.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex);

d -= Radius;

d = abs(d); 

d = step(0.1, d);

return float3(d, d, d);

그러면 이제 다음과 같이 뚜렷한 Edge Detection이 가능하다.

sin(), cos()

이제 해당 Texture를 반복해서 나타내보도록 하자.

파동함수의 주기와 진폭을 조절하여 반복되는 Texture를 생성할 수 있다.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex);

d -= Radius;

d = sin(d*8)/8; //cos(d*8)/8 가능

d = abs(d);

d = step(0.1, d);

return float3(d, d, d);

Animated Texture

노드에 Time이라는 Input Parameter를 추가하고 Time 노드를 추가하자.

그리고 주기 함수 값에 Time을 더해주면 시간에 따라 움직이는 Texture가 생성된다.

cpp
float2 tex = TexCoord * 2 - 1;

float d = length(tex);

d -= Radius;

d = sin(d*8 + Time)/8; // Time 값 더함

d = abs(d);

d = step(0.1, d);

return float3(d, d, d);

Conclusion

그 외에도 다양한 수학적 계산을 통해 기하학적인 패턴이나 디자인을 만들 수 있다.

해당 코드는 참고 영상을 통해 만든 최종적인 결과물이다

cpp
// 색상 팔레트 정의
float3 b = float3(0.5, 0.5, 0.5);   // 색상 진폭 (팔레트의 색 변화 크기)
float3 e = float3(0.263, 0.416, 0.557); // 색상 위상(phase shift), 색 조합을 다양하게 만듦

// uv 좌표를 0 ~ 1 범위로 변환 (중심 기준으로 대칭 좌표계 만들기)
uv = (uv * 2 - 1);
uv.y *= Resolution.x / Resolution.y;
float2 uv0 = uv;
float3 finalcolor = float3(0, 0, 0); // 최종 색상 누적 변수

// 3번 반복 루프 (각 반복에서 패턴을 조금씩 겹쳐 더 풍부한 결과 생성)
for (float i = 0; i < 4; i++) {

    uv = frac(uv * 1.5) - 0.5;

    // 현재 타일 내에서의 중심까지 거리
    float d = length(uv) * exp(-length(uv0));

    // 코사인 팔레트로 색상 생성
    // a = 기본 색 오프셋, b = 진폭, c = 주파수, e = 위상
    // d0 + time*0.4 → 시간에 따라 색상이 변하게 함
    float3 col = a + b * cos(6.28318 * (c * (length(uv0) + (i * 0.4) + (time * 0.2)) + e));

    // 거리값을 파동으로 변형
    d = sin(d * 8 + time) / 8;  // 사인파 변형
    d = abs(d);                 // 음수 방지 (밝기용)
    d = pow(0.01 / d, 1.2);     // 거리 기반 가중치 (가까울수록 더 강하게)

    // 최종 색상에 누적 (빛이 겹쳐지는 효과)
    finalcolor += col * d;
}

// 최종 결과 출력 (알파=1)
return float4(finalcolor, 1);
추가적으로 HLSL 코드로 만들어진 Material은 블루프린트 노드로도 똑같이 구현이 가능하다.