본문 바로가기
XR/Graphics

[Graphics] Compute Shader 알아보기

by 여기는 정글 2025. 4. 3.

안녕하세요.
정글러 입니다.
 
Compute Shader에 대해서 알아보겠습니다.

Computer Shader는 vertex와 상관없이 GPU에서 별도로 사용할 수 있습니다.
픽셀 쉐이더는 어떤 픽셀의 색깔값을 선택할 수 없지만, computer shader는 필요한 부분의 색깔과 포지션 값의 수정이 가능합니다.
- 픽셀 쉐이더는 vertex가 갖고 있는 texture좌표를 pixel 단위로 interpolation해 pixel shader에 넣어주는 sampling
- computer shader는 필요한 대로 sampling을 할 수 있음
 

GPU Render Pipeline

m_context->CSSetConstBuffers(0, 1, m_constsGPU.GetAddressOf());
m_context->SetUnorderedAccessViews(0, 1, m_backUAV.GetAddressOf(), NULL);
Dispatch(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ)
// 픽셀의 구역을 ThreadGroup으로 나눔

ComputeShader 기본

RWTexture2D<float4> gOutput : register(u0)  // 읽기와 쓰기가 가능한 텍스쳐2D
cbuffer MyBuffer : register(b0)
{   float3 position;
    float scale;    
    float4 color; 
}
 
[numthreads(256, 1, 1)]란?
하나의 스레드 그룹 안에서 실행되는 스레드의 갯수
Maximum Threads (X*Y*Z) = 1024

void main(uint3 gID : SV_GroupID, uint3 tID : SV_DispatchThreadID)
// 이때 tID는 픽셀의 좌표, 픽셀의 인덱스라고 볼 수 있습니다.
// 여기서 gOutput는 읽고 쓸 수 있기 때문에 픽셀을 바꿀 수 있습니다.
 

Warp

GPU는 이렇게 블럭 단위로 일을 하기 때문에 warp를 할 수 있습니다.
Warp는 동시에 일을 하는 스레드의 묶음 입니다. (32 parallel threads)

Block단위로 스레드를 제어할 수 있습니다. 하지만 다른 블럭과는 동기화시킬 수 없습니다.
모든 블럭들이 다 끝날 때까지 기다릴 수는 있습니다.
 
// 컴퓨터 쉐이더가 하던 일을 끝내게 만들고 Resources를 해제한다.
AppBase::ComputeShaderBarrier();
 

Structured Buffer

texture memory- pixel의 2차원 메모리이다.
structured buffer는? 1차원 배열처럼 사용할 수 있다.
 
예를들어 position과 color가 있는 particle template 구조체의 배열로 사용할 수 있다.

CPP
[데이터 멤버]
vector<T_ELEMENT> m_cpu; // gpu로 보낼 데이터
ComPtr<ID3D11Buffer> m_gpu;
ComPtr<ID3D11Buffer> m_staging;
ComPtr<ID3D11ShaderResourceView> m_srv;
ComPtr<ID3D11UnorderedAccessView> m_uav;
 
Initialize (device, UINT(m_cpu.size()), sizeof(T_ELEMENT), m_cpu.data(), m_gpu, m_srv, m_uav);
 
데이터 초기화
랜덤 넘버 지정
vertex shader
pixel shader
compute shader
 
update
// 입자들의 위치를 바꿔주는 작업
 
render
m_context->CSSetConstBuffers(0, 1, m_constsGPU.GetAddressOf());
m_context->SetUnorderedAccessViews(0, 1, m_backUAV.GetAddressOf(), NULL);
Dispatch(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ)
AppBase::ComputeShaderBarrier();
 
computeshader.hlsl
RWStructureBuffer<particle> outputParticles : register(u0)
[numthreads(256, 1, 1)]
void main(int3 gID : SV_GroupID, int3 gtID : SV_GroupThreadID, uint3 dtID : SV_DispatchThreadID)
 
//여기서 애니메이션 효과 추가!
Particle p = outputParticles[dtID.x]; //Read
float3 velocity = ...;
p.pos += velocity * dt;
outputParticles[dtID.x].pos = p.pos //Write
 
속도를 어떻게 계산하는가에 따라 다양한 애니메이션을 만들 수 있습니다.
나중에 언리얼엔진의 나이아가라에서 흐름을 제대로 이해하기 위해서
제가 이해할 수 있는 흐름정도로만 정리가 된 것 같네요;;
 
compute shader를 통해 정말 다양한 애니메이션을 만들 수 있을 것 같아서 흥미롭습니다.
감사합니다.