Nephrite21

언리얼 유체 시뮬레이션 과정 3-1 - Shadeup을 이용한 Unreal Compute Shader 구현 및 이해 - 셰이더 본문

Unreal SPH 시뮬레이션

언리얼 유체 시뮬레이션 과정 3-1 - Shadeup을 이용한 Unreal Compute Shader 구현 및 이해 - 셰이더

Nephrite21 2024. 11. 4. 20:11

이제 Shaderup 코드를 이해해 볼 차례이다.

Shaderup 세팅을 통해서 얻은 코드는 다음과 같다. (모든 디렉토리는 Unreal Project 솔루션 파일의 탐색기에서 확인한 디렉토리이다. 실제 로컬 디렉토리와는 다를 수 있다.)

1. Plugins/{설정한 플러그인 이름}/Shaders/{설정한 모듈 이름}/Private/{설정한 셰이더 이름}/{설정한 셰이더 이름}.usf

2. Plugins/{설정한 플러그인 이름}/Source/{설정한 모듈 이름}/Private/{설정한 셰이더 이름}/{설정한 셰이더 이름}.cpp

3. Plugins/{설정한 플러그인 이름}/Source/{설정한 모듈 이름}/Private/{설정한 셰이더 이름}/{설정한 셰이더 이름}.h

4. Plugins/{설정한 플러그인 이름}/Source/{설정한 모듈 이름}/Public/{설정한 셰이더 이름}/{설정한 셰이더 이름}.h

여기에 

5. Plugins/{설정한 플러그인 이름}/Source/{설정한 모듈 이름}/Private/{설정한 셰이더 이름}/{설정한 셰이더 이름}_readme.md

까지 해서 총 5가지의 주요 파일이 있다.

이중 readme.md는 다음과 같은 내용을 담고 있는데

# TestShader usage

## Blueprint

1. Right-click in any blueprint editor and add the node: "Execute RTCompute Shader"
2. Pass in a render target
3. Enjoy, and get digging into the code

Find the main shader code under `Plugin/Shaders/Module/Private/TestShader/TestShader.usf`

Feel free to delete this file

별 볼일은 없고 셰이더 위치랑 노드 쓰는 방법 알려준다. 지워도 된다니까 나중에 쓸모없어지면 지워도 된다.

먼저 가장 중요한 1번부터 확인을 해보면

지난 포스트에서 수정했던 셰이더가 존재한다.

#include "/Engine/Public/Platform.ush"

RWTexture2D<float3> RenderTarget;

[numthreads(THREADS_X, THREADS_Y, THREADS_Z)]
void TestShader(
	uint3 DispatchThreadId : SV_DispatchThreadID,
	uint3 groupThread : SV_GroupThreadID )
{
    float groupU = ((float) groupThread.x) / ((float) THREADS_X);
    float groupV = ((float) groupThread.y) / ((float) THREADS_Y);
    RenderTarget[DispatchThreadId.xy] = float3(groupU, groupV, 0.0);
}

이렇게 나와있는데

기능은 간단하게 변수선언/ 함수 두가지로 구성되어 있다.

첫번째 라인인

RWTexture2D<float3> RenderTarget;

이부분은 RenderTarget을 변수로 선언하는 간단한 라인이고,

두 번째 부터가 함수인데

[numthreads(THREADS_X, THREADS_Y, THREADS_Z)]
void TestShader(
	uint3 DispatchThreadId : SV_DispatchThreadID,
	uint3 groupThread : SV_GroupThreadID )
{
    float groupU = ((float) groupThread.x) / ((float) THREADS_X);
    float groupV = ((float) groupThread.y) / ((float) THREADS_Y);
    RenderTarget[DispatchThreadId.xy] = float3(groupU, groupV, 0.0);
}

numthreads는 뭔지는 모르겠지만 스레드의 수 인것 같다.

함수에서는 인자를 ThreadID와 groupThread를 받고있다.

그러면 알 수 있는 것은 스레드가 여러 개 있고, 각각의 스레드의 아이디 또한 알 수 있다는 사실을 알게 되었다!

해당 스레드에 대해서 아래에서 연산을 해주고 있는데

float groupU = ((float) groupThread.x) / ((float) THREADS_X);
float groupV = ((float) groupThread.y) / ((float) THREADS_Y);

여기는 간단하게 변수를 선언하고 있다.

그룹 스레드가 아까 말했던 스레드의 그룹이고, x,y 좌표를 받아오는 것을 보니 그룹 내에서의 좌표라는 것을 알 수 있다.

이것을 스레드의 개수로 나누고 있으니 각 스레드 내에서 상대적인 위치값을 바꾸고 있는 것을 알 수 있다.

이걸

RenderTarget[DispatchThreadId.xy] = float3(groupU, groupV, 0.0);

이렇게 해서 저장을 해주고 있는 모습이다.

그래서 결과값이

이렇게 나오고 있는 모습이다. 각각의 위치에 따라 스레드 그룹 따라서 이렇게 8*8*1 크기의 스레드 그룹 내에 32개의 스레드 들이 연산을 해서 데이터를 저장한 모습이다.

이렇게 해서 셰이더 파일에 대한 이해를 마쳤다!