유니티 엔진에서 컴퓨트 쉐이더 Compute Shader 사용 방법 - Whitmem
유니티 엔진에서 컴퓨트 쉐이더 Compute Shader 사용 방법
게임 개발 및 엔진
2026-01-13 23:29 게시 03ac6d166c3a2bb30f3b

0
0
35
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
유니티 엔진에서 컴퓨트 쉐이더를 사용하는 방법을 찾아봤는데, 제법 그렇게 어렵지 않아서 놀랐던 적이 있다.
아무래도 상용 엔진인 만큼 게임 개발에 집중하라고 구현을 모두 간단 간단하게 해둔 것 같다.
일단 빈 에셋 공간에 Create -> Shader -> Compute Shader 메뉴를 통해 새 컴퓨트 쉐이더를 생성한다.
소스코드를 열어보면 기본 코드가 작성되어 있는데 필자의 경우 약간 수정하였다. 우선 아래와 같이 수정했는데,
위 코드를 보면 먼저 첫 번째로 kernel이 CSMain으로 지정되어 있는데, 이 컴퓨트 쉐이더 코드에서 커널의 진입점이 무엇이 있는지 정의하는 부분이다. CSMain 이라는 진입점이 존재한다는 것을 알려주는 것이다.
다음으로는 사용할 변수들을 정의한다. RWTexture2D 는 읽고 쓰기가 가능한 텍스처이다. 텍스처 말고도 버퍼 공간, Float, Matrix 등을 입출력으로 넣을 수 있다.
마지막으로 실제 함수 영역인데 눈에 띄는 설정 numthreads()이다. 이 부분은 이 작업이 수행될 쓰레드를 의미한다. 즉 8,8,1 이라고 지정한 것은 내부 처리에서 8,8,1 개의 작업 공간으로 나누어 멀티 작업을 수행한다는 의미이다. 물론 여기 입력하는 숫자 그대로 100% 분할하여 실행되는 것은 아니다. 그리고 numthreads에 입력하는 공간은 실제 그래픽 프로세서가 레지스터 위에서 실행되는 공간으로 개수 제한이 있다고 한다.
따라서 numthreads는 적은 숫자로 잡고, 밖에서 x, y, z 작업 개수를 조절하여 예약하여 수행한다고 한다.
그런데 왜 하필 위와 같이 (x,y,z) 작업 형태인 걸까? 보통 그래픽스 작업 안에서는 픽셀이나 텍셀 작업등 2차원, 3차원 작업을 하는 경우가 대다수이다. 이 경우 내부에서 for을 사용하는 경우 멀티 쓰레드의 작업 이점을 가져갈 수 없고, 그렇다고 단순 개수 하나로 처리하기에는 사용자가 직접 X,Y,Z 를 계산해야 한다는 문제가 존재한다.
따라서 X,Y,Z 라는 작업 영역을 3차원으로 나누어 수행함으로써 배열이 필요한 경우 바로 id에서 3차원 형태로 가져오거나, 2차원 또는 1차원 형태로 가져가 사용할 수 있게끔 제공되는 것이다.
위 numthreads에서 x,y,z 를 각각 8,8,1 로 지정하였으므로 횟수를 각각 1회로 지정한다면 0~7, 0~7, 0 만큼 작업 범위로 총 64개의 픽셀 작업이 동시 수행된다.
외부에서 작업 개수를 X,Y,Z 에 별도로 Dispatch할 수 있는데, 이 때 Dispatch 하는 수만큼 해당 차원에 배(Scaling)하여 작업이 수행된다. 즉 numthreads가 (8,8,1)인 상황에서 밖에서 Dispatch(2,2,2)를 하면 0~15, 0~15, 0~1 로 총 16*16*2 개의 픽셀 작업이 수행되는 것이다.
void CSMain (uint3 id : SV_DispatchThreadID) { // TODO: insert actual code here! Result[id.xy] = float4(id.x/256.0, id.y/256.0,0,1); }
아무튼 위 코드는 Result 라는 RW 가능한 Texture2D 공간에 id.xy 라는 2차원 픽셀에 256으로 나눈 값을 x, y에 적용하는 것이다. 어떤 변수에 색상 값을 지정하는 과정인데(여기서는 컴퓨트 쉐이더이므로 반드시 텍스처 등의 출력 값이 없을 수 있다.), 256로 나눈 값을 넣도록 하였다. numthreads가 8,8,1 이므로 외부 Dispatch를 (1,1,1)로 실행하면 총 0~7, 0~7, 0 범위만 수행된다.
이때 0~256 범위로 작업되게 하기 위해 numthreads를 256, 256, 1로 늘리면 될까?
아까 말했다시피 numthreads는 처리 횟수에 제한이 있다. 따라서 numthreads의 권장 값은 8,8, 16,16 등이라고 한다... 따라서 최소한의 값으로 유지하고, 밖에서 Dispatch를 통해 256 단위의 횟수가 되도록 맞춰줘야 한다. 아무튼 여기서는 8,8,1 로 고정하겠다.
Dispatch 방법
이제 이 쉐이더를 실행하려면 그래픽 프로세서에게 요청해야하는데, 유니티 엔진에서는 이를 ComputeShader 라는 클래스에서 요청자를 구현하고 있다.
심지어 작업이 복잡하지 않다. 그래픽 파이프라인에 맞추어 작성할 필요가 없다. 필요한 시점에 ComputeShader을 생성 또는 가져와 Dispatch을 호출하면 엔진이 내부적으로 작업을 예약하고 실행 가능할 때 그래픽 파이프라인에 맞춰 실행해준다.
특히 기존 Fragment 파이프라인이랑은 관련이 없기 때문에 큰 부담 없이 자유롭게 사용하면 된다.
일단 ComputeShader 클래스의 전역 변수를 하나 만들어주면 이 공간에 cs 코드를 담을 수 있는 인스펙터가 하나 생긴다. (게임 오브젝트에 스크립트를 넣었다.) 인스펙터 없이 직접 로드하고자 하는 경우 전역적으로 사용되는 Resources.Load로 바로 로드하면 되는 듯 싶다.
사용 방법은 극도로 쉽다. 먼저 진입점 Kernel을 찾은 뒤 해당 Kernel 에 Dispatch 해주면 된다. Dispatch 할 때 X, Y, Z 작업 개수를 넣어주면 되는데, 이 입력이 쉐이더 코드에 작성했던 numthreads에 배율되어 처리되는 개수이다. 여기서 320을 넣었으니, 아까 numthreads에 넣었던 8을 곱하면 2560, 즉 2560, 2560, 1회 수행된다.
2560 2560 으로 한 이유는 텍스처 크기를 2560, 2560으로 맞추기 위해서이다.
아까 말했다시피 ComputeShader은 텍스처 출력을 기본으로 하지 않는다. 직접 작업하고자 하는 변수를 입력으로 넣어줘야한다. 즉 입력하는 변수는 단순 ReadOnly가 아니라 Write도 가능한 RW 모드로 받아줄 수 있다.
RenderTexture를 하나 선언 해 2560 사이즈로 만들어준다.
enableRandomWrite 옵션을 반드시 켜줘야 하는데, 단순히 텍스처를 출력하려는 목적이 아니라 텍스처의 좌표를 각 컴퓨터 쉐이드에서 수정하기 때문에 RandomWrite를 켜서 특정 플래그를 줘야하는 것으로 보인다.
그리고 Create()를 통해 텍스처를 생성한다. 이제 이 텍스처를 특정 커널에서 사용할 수 있도록 파라메터를 넘겨주면 된다.
kernel을 하나 만들어서 SetTexture을 통해 특정 명칭의 텍스처(Result)를 넘겨준다.
그리고 Dispatch하면 GPU에서 해당 작업이 수행되어 텍스처에 반영된다. 이를 눈으로 확인하기 위해서 Material에 넣어보자
참고로 여기서 RW Texture2D는 프래그먼트 쉐이더가 아니기 때문에 읽고 쓰기를 동시에 할 수 있다.
이렇게 쓴 텍스처를 이제 material의 SetTexture를 통해 BaseMap에 넣어줄 수 있다.
material은 내가 출력하고 싶은 객체의 표준 Lit Material 이다.
코드를 실행하면 이런 맵이 표시되는 것을 볼 수 있다. 성공적으로 ComputeShader가 각 텍스처의 픽셀에 색상을 집어넣고 실행된 결과를 Material에 출력한 것이다. 왜 노란색이 절반을 차지하냐면, 총 2560, 2560 픽셀을 수행하는데 256만 나눠서 그런다.
이렇게 수행하고 실행해본다.
아주 잘 실행되는 것을 볼 수 있다.
댓글 0개
댓글을 작성하는 경우 댓글 처리 방침에 동의하는 것으로 간주됩니다. 댓글을 작성하면 일회용 인증키가 발급되며, 해당 키를 분실하는 경우 댓글을 제거할 수 없습니다. 댓글을 작성하면 사용자 IP가 영구적으로 기록 및 부분 공개됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.