simple add function

두수 더하기

컴퓨터 셰이더로 값 두개를 더해 보겠습니다.
간단한 함수부터 시작하면 복잡해 보이는것도 쉽게 할수 있을 겁니다.

Compute Shader에서는 “단일 float 값을 바로 반환”할 수는 없고,
입력은 float 변수로, 출력은 크기 1짜리 버퍼로 받는 방식이 정석입니다.

아래는 float 두 개를 넘겨서 더한 결과를 하나의 float로 받는 간단한 예제입니다.

AddFloat.compute 파일
#pragma kernel CSMain

// 입력 값 (C#에서 SetFloat로 전달)
float A;
float B;

// 출력 값 (크기 1짜리 버퍼)
RWStructuredBuffer<float> Result;

[numthreads(1, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    Result[0] = A + B;
}


ComputeAddFloat.cs 파일
using UnityEngine;

public class ComputeAddFloat : MonoBehaviour
{
    public ComputeShader addShader;

    ComputeBuffer resultBuffer;

    void Start()
    {
        float a = 3.5f;
        float b = 7.25f;

        // 결과용 버퍼 (float 1개)
        resultBuffer = new ComputeBuffer(1, sizeof(float));

        int kernel = addShader.FindKernel("CSMain");

        // float 값 전달
        addShader.SetFloat("A", a);
        addShader.SetFloat("B", b);

        // 결과 버퍼 연결
        addShader.SetBuffer(kernel, "Result", resultBuffer);

        // 실행 (1 스레드)
        addShader.Dispatch(kernel, 1, 1, 1);

        // 결과 가져오기
        float[] result = new float[1];
        resultBuffer.GetData(result);

        Debug.Log($"{a} + {b} = {result[0]}");
    }

    void OnDestroy()
    {
        resultBuffer.Release();
    }
}

결과)
3.5 + 7.25 = 10.75


포인트
  • float A, float B → 상수 버퍼(Constant Buffer)
  • 출력은 반드시 RWStructuredBuffer
  • 스레드는 1개만 실행

SetFloat의 "A", "B", SetBuffer의 "Result" 값은 HLSL의 변수와 일치 해야 합니다.

Unity Compute Shader는 내부적으로:
문자열 이름 → Shader Property ID → Constant Buffer 슬롯

이 구조를 사용합니다.
그래서 이름이 곧 “연결 고리”입니다.

ComputerShader.SetFloat이란?

SetFloat은 Compute Shader의 float 상수 변수에 CPU 값을 전달하기 위한 상수 버퍼 설정 명령입니다.
HLSL에 선언된 float / half / int / bool 같은 변수 타입을 사용 할수 있습니다.

내부 처리 순서

C# float 값
   ↓
Unity 내부 Constant Buffer (CPU)
   ↓
Dispatch 시 GPU Constant Buffer로 업로드
   ↓
Compute Shader에서 float A로 읽음

두수 배열 더하기

셰이더 파일을 저장할때는 euc-kr 포맷 대신 utf 파일로 저장한다.
한글이 포함 되어 있으면 실행 에러가 발생한다.

add.compute 파일
#pragma kernel CSMain

// 입력 버퍼
StructuredBuffer<float> A;
StructuredBuffer<float> B;

// 출력 버퍼
RWStructuredBuffer<float> Result;

// 스레드 그룹 크기
[numthreads(64, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    uint index = id.x;
    Result[index] = A[index] + B[index];
}

ComputeAdd.cs 파일
using UnityEngine;

public class ComputeAddExample : MonoBehaviour
{
    public ComputeShader addShader;

    const int dataSize = 1024;

    ComputeBuffer bufferA;
    ComputeBuffer bufferB;
    ComputeBuffer bufferResult;

    void Start()
    {
        // 데이터 생성
        float[] A = new float[dataSize];
        float[] B = new float[dataSize];
        float[] Result = new float[dataSize];

        for (int i = 0; i < dataSize; i++)
        {
            A[i] = i;
            B[i] = i * 2;
        }

        // ComputeBuffer 생성
        bufferA = new ComputeBuffer(dataSize, sizeof(float));
        bufferB = new ComputeBuffer(dataSize, sizeof(float));
        bufferResult = new ComputeBuffer(dataSize, sizeof(float));

        bufferA.SetData(A);
        bufferB.SetData(B);

        int kernel = addShader.FindKernel("CSMain");

        // 셰이더에 버퍼 연결
        addShader.SetBuffer(kernel, "A", bufferA);
        addShader.SetBuffer(kernel, "B", bufferB);
        addShader.SetBuffer(kernel, "Result", bufferResult);

        // 실행 (1024 / 64 = 16 그룹)
        addShader.Dispatch(kernel, dataSize / 64, 1, 1);

        // 결과 가져오기
        bufferResult.GetData(Result);

        // 테스트 출력
        for (int i = 0; i < 5; i++)
        {
            Debug.Log($"{A[i]} + {B[i]} = {Result[i]}");
        }
    }

    void OnDestroy()
    {
        bufferA.Release();
        bufferB.Release();
        bufferResult.Release();
    }
}

결과)
0 + 0 = 0
1 + 2 = 3
2 + 4 = 6
3 + 6 = 9
4 + 8 = 12


핵심 설명
  • StructuredBuffer : 읽기 전용
  • RWStructuredBuffer : 읽기/쓰기 가능
  • numthreads(64,1,1) : 한 그룹에 64개의 스레드
  • Dispatch()에서 전체 데이터 개수 ÷ 64 만큼 실행

실행 (1024 / 64 = 16 그룹) 한 그룹에 쓰레드는 64개이므로 나눠 그룹의 갯수를 구한다.
addShader.Dispatch(kernel, dataSize / 64, 1, 1);

ComputeBuffer란?

CPU(C#) ↔ GPU 사이에 데이터를 전달하기 위한 GPU 메모리 버퍼입니다.

기본 사용 흐름

  1. ComputeBuffer 생성
  2. CPU 데이터 → GPU로 전송 (SetData)
  3. Shader에서 사용
  4. 결과 필요 시 GetData
  5. 반드시 Release()로 해제

ComputerBuffer.SetData : 데 이터를 GPU 버퍼에 올리는 단계

역할: CPU 메모리에 있는 데이터를 GPU 메모리(ComputeBuffer)로 복사합니다.

SetData 내부 동작:
  1. CPU 메모리 → GPU 메모리 복사
  2. GPU에서 나중에 Compute Shader나 Material 등에서 접근 가능
  3. 주의: 이 시점에서는 아직 Shader에 연결된 것은 아님, 그냥 GPU 버퍼에 데이터가 올라간 것뿐입니다.

ComputeShader.SetBuffer – GPU 버퍼를 Shader 변수와 연결하는 단계

역할: GPU에 있는 ComputeBuffer를 Compute Shader 변수에 바인딩합니다.

SetBuffer 내부 동작:
  1. Compute Shader 코드에서 정의된 RWStructuredBuffer<float> dataBuffer와 GPU의 ComputeBuffer 연결
  2. Dispatch() 호출 시 Shader가 이 버퍼를 읽고 쓰게 됨
  3. 주의: 데이터는 이미 GPU에 올라와 있어야 의미 있음 → 보통 SetData() 후 호출

단계 C# 코드 GPU 측 의미
1 buffer.SetData(array) CPU → GPU 메모리 복사
2 computeShader.SetBuffer(kernel, "dataBuffer", buffer) GPU Shader 변수에 버퍼 바인딩
3 computeShader.Dispatch(...) Shader 실행, GPU가 바인딩된 버퍼 읽기/쓰기

핵심 정리

  • buffer.SetData: GPU에 데이터 올리기

  • Shader.SetBuffer: GPU 버퍼를 Shader 변수와 연결

  • Shader.Dispatch: Shader 실행 → GPU가 버퍼를 사용함

  • buffer.GetData: GPU → CPU 데이터 읽기

버퍼 사용법을 정리한 간단한 예제는 다음과 같습니다.
// 1. CPU 데이터 준비
float[] data = new float[10];

// 2. ComputeBuffer 생성
ComputeBuffer buffer = new ComputeBuffer(10, sizeof(float));

// 3. GPU 버퍼에 데이터 전송
buffer.SetData(data);

// 4. Compute Shader에 버퍼 연결
int kernel = computeShader.FindKernel("CSMain");
computeShader.SetBuffer(kernel, "dataBuffer", buffer);

// 5. Shader 실행
computeShader.Dispatch(kernel, 10, 1, 1);

// 6. GPU → CPU 데이터 읽기
buffer.GetData(data);

// 7. 버퍼 해제
buffer.Release();