렌더링 파이프라인과 별개로 GPU에서 범용 연산을 수행한다. 파티클, 물리 시뮬레이션, AI 연산에 활용된다

 

렌더링 파이프라인 밖의 GPU

 

기존 셰이더 (렌더링 파이프라인 안)

- 버텍스 셰이더 -> 래스터화 -> 픽셀 셰이더

- 3D를 2D 화면으로 변환하는 것이 목적이다.

- 입력 = 버텍스 데이터, 출력 = 픽셀 색상. 입출력이 정해져있다.

 

컴퓨트 셰이더 (렌더링 파이프라인 밖)

- 렌더링과 무관하게 GPU에서 실행한다

- 입출력이 자유롭다. -> GPU의 병렬 연산 능력만 빌려 쓰는 것이다

 

GPU = 수천 개의 코어가 동시에 같은 연산을 하는 기계

컴퓨트 셰이더 = 이 병렬성을 렌더링 외에도 활용

 

워크그룹과 스레드 : 컴퓨트 셰이더의 실행 단위

 

컴퓨트 셰이더 실행 구조

Dispatch (CPU에서 GPU에 실행 명령) : 몇 개의 워크그룹을 실행할지 지정

 

WorkGroup : GPU의 실행 블록 단위로 같은 워크그룹 내 스레드는 공유  메모리 사용 가능하다

 

Thread ( 스레드 = Invocation ) : 가장 작은 실행 단위로 실제로 셰이더 코드를 실행하는 것이다.

 

ex) 1024 x 1024 텍스처 처리

Dispatch(64, 64, 1) -> 64 x 64 = 4096 워크그룹

[numthreads(16, 16, 1)]  -> 워크그룹당 16x16 = 256 스레드

총 스레드 수: 4096 x 256 = 1048576개 -> 픽셀 하나당 스레드 하나

 

스레드 ID

SV_GroupThreadID : 워크그룹 내 스레드 번호 (0~15, 0~15)

SV_GroupID : 워크그룹 번호 (0~63, 0~63)

SV_DispatchThreadID : 전체에서 고유한 스레드 번호

 

HLSL 컴퓨트 셰이더 기본 구조

// 파티클 시뮬레이션 컴퓨트 셰이더 (HLSL)

// 파티클 데이터 구조
struct Particle {
     float3 position;
     float3 velocity;
     float lifetime;
     float age;
};

// GPU 버퍼 - 읽기 / 쓰기 가능
RWStructuredBuffer<Particle> particles : register(u0);

// CPU에서 전달하는 상수
cbuffer SimConstants : register(b0) {
     float deltaTime;
     float3 gravity;
     int particleCount;
};

// 워크그룹 크기 선언
[numthreads(64, 1, 1)]
void CSMain(
     uint3 dispatchID : SV_DispatchThreadID // 전체에서 고유 ID
){
     uint id = dispatchID.x;
     
     // 범위 체크 - 스레드가 파티클 수 초과하면 종류
     if(id >= (uint)particleCount) return;
     
     Particle p = particles[id];
     
     // 각 스레드가 자신의 파티클만 독립적으로 처리
     p.velocity += gravity * deltaTime;
     p.position += p.velocity * deltaTime;
     p.age += deltaTime;
     
     // 수명 다하면 리셋
     if(p.age >= p.lifetime) {
           p.position = float3(0, 0, 0);
           p.veloctiy = float3((float(id % 7) - 3.0) * 0.5, 5.0, (float(id % 5) - 2.0) * 0.5);
           p.age = 0.0;
     }
     
     // 결과 저장
     particles[id] = p;
}

// CPU에서 디스패치
// int groups = (particleCount + 63) / 64;
// commandList->Dispatch(groups, 1, 1);

 

GLSL 컴퓨트 셰이더 ( OpenGL/Vulkan )

#version 430 // OpenGL ES 3.1 / OpenGL 4.3 이상

// 워크그룹 크기
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;

// 이미지 읽기/쓰기 (이미지 프로세싱)
layout(rgba8, binding = 0)
     uniform readonly image2D inputImage;
layout(rgba8, binding = 1)
     uniform writeonly image2D outputImage;
    
void main() {
     // 현재 스레드가 처리할 픽셀 좌표
     ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
     
     // 이미지 크기 체크
     ivec2 size = imageSize(inputImage);
     if(coord.x >= size.x || coord.y >= size.y) return;
     
     // 블러 처리 (3x3 평균)
     vec4 sum = vec4(0.0);
     for(int dy = -1; dy <= 1; dy++)
     {
          for(int dx = -1; dx <= 1; dx++)
          {
                ivec2 sampleCoord = coord + ivec2(dx, dy);
                sampleCoord = clamp(sampleCoord, ivec2(0), size - 1);
                sum += imageLoad(inputImage, sampleCoord);
          }
     }
     
     // 평균값 출력
     imageStore(outputImage, coord, sum / 9.0);
}

 

컴퓨트 셰이더의 실제 활용 사례

 

파티클 시스템

- UE5 niagara : 컴퓨트 셰이더로 수백만 파티클 병렬 시뮬레이션

- CPU로 하면 초당 수천 개 한계

- GPU로 하면 초당 수백만 개 가능

 

물리 시뮬레이션

- 천 시뮬레이션 (Cloth) : 각 버텍스를 스레드로

- 유체 시뮬레이션 (Fluid) : 격자 셀을 스레드로

- 군중 AI (Crowd) : 각 에이전트를 스레드로

 

컬링 (GPU Driven Rendering)

- 수만 개 오브젝트 중 화면에 보이는 것을 CPU가 아닌 GPU에서 직접 판별

- UE5 Nanite의 핵심 기술

 

포스트 프로세싱

- 블룸, 블러, SSR, SSAO

- 픽셀 셰이더 대신 컴퓨트로 처리하면 더 효율적인 경우가 많음

 

스키닝 (Vertex Skinning)

- 캐릭터 애니메이션의 본 변환 계산

- 수천 개 버텍스를 병렬 처리

 

머신러닝 / AI

- TensorFlow, PyTorch의 GPU 연산 기반

- WebGPU 컴퓨트 셰이더로 브라우저 AI 추론

 

공유 메모리 (워크그룹 내 최적화)

// 공유 메모리는 같은 워크그룹 스레드들이 공유하는 빠른 메모리
// GPU 온칩 메모리 -> 글로벌 메모리보다 훨씬 빠르다

[numthreads(16, 16, 1)]
void BlurCS(uint8 groupID : SV_GroupID,
            uint8 threadID : SV_GroupThreadID,
            uint8 dispatchID : SV_DispatchThreadID)
{
     // 공유 메모리 선언 (16 + 2) x (16 + 2) = 경계 포함
     groupshared float4 sharedPixels[18][18];
     
     // 각 스레드가 자신 담당 픽셀을 공유 메모리에 로드
     // (글로벌 메모리 -> 공유 메모리: 한 번만)
     ivec2 coord = dispatchID.xy;
     sharedPixels[threadID.y+1][threadID.x+1] = inputTexture[coord];
     
     // 모든 스레드 로드 완료 대기 (동기화)
     GroupMemoryBarrierWithGroupSync();
     
     // 공유 메모리에서 빠르게 읽기
     float4 sum = float4(0,0,0,0);
     for(int dy = -1; dy <= 1; dy++)
     for(int dx = -1; dx <= 1; dx++)
          sum += sharedPixels[threadID.y+1+dy][threadID.x+1+dx];
          
     outputTexture[coord] = sum / 9.0;
     // 글로벌 메모리 접근 : 로드 1번 + 저장 1번
     // 공유 메모리 없으면 : 로드 9번 필요
}

 

UE5에서 컴퓨트 셰이더 직접 작성하기

// UE5 커스텀 컴퓨트 셰이더 C++ 선언
class FMyComputeShader : public FGlobalShader {
    DECLARE_GLOBAL_SHADER(FMyComputeShader);
    SHADER_USE_PARAMETER_STRUCT(FMyComputeShader,
                                FGlobalShader);

    BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
        SHADER_PARAMETER_RDG_BUFFER_UAV(
            RWStructuredBuffer<FParticle>,
            ParticleBuffer)
        SHADER_PARAMETER(float, DeltaTime)
        SHADER_PARAMETER(int32, ParticleCount)
    END_SHADER_PARAMETER_STRUCT()

    static bool ShouldCompilePermutation(
        const FGlobalShaderPermutationParameters& Params) {
        return IsFeatureLevelSupported(
            Params.Platform, ERHIFeatureLevel::SM5);
    }
};

// HLSL 파일과 연결
IMPLEMENT_GLOBAL_SHADER(FMyComputeShader,
    "/Project/Shaders/MyComputeShader.usf",
    "CSMain",
    SF_Compute);

// 실행 (RDG — Render Dependency Graph)
void RunComputeShader(FRDGBuilder& GraphBuilder,
                      int32 ParticleCount) {
    FMyComputeShader::FParameters* PassParams =
        GraphBuilder.AllocParameters
            FMyComputeShader::FParameters>();

    PassParams->DeltaTime    = GetWorld()->DeltaTimeSeconds;
    PassParams->ParticleCount = ParticleCount;

    TShaderMapRef<FMyComputeShader> ComputeShader(
        GetGlobalShaderMap(GMaxRHIFeatureLevel));

    // 디스패치
    int32 GroupCount = FMath::DivideAndRoundUp(
        ParticleCount, 64);

    FComputeShaderUtils::AddPass(
        GraphBuilder,
        RDG_EVENT_NAME("MyComputePass"),
        ComputeShader,
        PassParams,
        FIntVector(GroupCount, 1, 1));
}

 

연결되는 단어들

 

GPGPU : General Purpose GPU. GPU를 그래픽스 외 범용 연산에 사용하는 개념. CUDA, OpenCL, 컴퓨트 셰이더가 모두 이 범주

 

Wrap/Wavefront : GPU의 최소 실행 단위. NVIDIA: Wrap(32스레드), AMD: Wavefront(64스레드). 워크그룹 크기를 이 배수로 맞춰야 효율적이다.

 

UAV (Unordered Access View) : DX12에서 컴퓨트 셰이더가 읽기/쓰기 가능한 버퍼. GLSL의 SSBO에 대응. RWStructuredBuffer가 UAV.

 

RDG (Render Dependency Graph) : UE5의 렌더링 의존성 관리 시스템. 컴퓨트 셰이더 패스를 추가할 때 사용. 자동으로 리소스 배리어와 패스 순서를 관리한다.

'Word' 카테고리의 다른 글

UV 매핑 (UV Mapping)  (0) 2026.07.22
노멀 맵 (Normal Map)  (0) 2026.07.21
HLSL/GLSL  (0) 2026.07.16
WebGL / WebGPU  (0) 2026.07.14
OpenGL ES ( Embedded Systems )  (0) 2026.07.13

+ Recent posts