관리 메뉴

caLAB

[Compute Shader 02] 버퍼의 사용 본문

Unity/유니티 Shader

[Compute Shader 02] 버퍼의 사용

도이(doi) 2021. 7. 30. 19:20
728x90

오늘은 C# 스크립트와 compute shader 사이에 데이터를 주고 받는 것을 배웠다.

C# 스크립트에서 buffer에 데이터를 배열로 저장하고, 이를 compute shader에 전달해서 compute shader에서 연산해서 그리는게 기본적인 구조이다.

cpu에서 gpu값에 정의한 것을 set해서 값을 변경할 수 있음.
RWTexture2D를 통해서 single call을 통해서 원을 그릴 수 있음.

 

fragment shader는 pixel을 그리고 compute shader는 가능한 표현이 더 많다.
buffer에는 custom 구조체의 값을 저장해서 보낼 수 있다
C#에서 compute shader로 buffer에 값 저장해서 보내는 방법

C# 코드

using UnityEngine;
using System.Collections;

public class BufferJoy : MonoBehaviour
{
    public ComputeShader shader;
    public int texResolution = 1024;
    Renderer rend;
    RenderTexture outputTexture;
    int circlesHandle;
    int clearHandle;
    public Color clearColor = new Color();
    public Color circleColor = new Color();

    struct Circle
    {
        public Vector2 origin;
        public Vector2 velocity;
        public float radius;
    }

    int count = 10;
    Circle[] circleData; //circle 정보 
    ComputeBuffer buffer; //comptebuffer 선언

    void Start()
    {
        outputTexture = new RenderTexture(texResolution, texResolution, 0);
        outputTexture.enableRandomWrite = true;
        outputTexture.Create();
        rend = GetComponent<Renderer>();
        rend.enabled = true;
        InitData();
        InitShader();
    }
    
    private void InitData()
    {
        circlesHandle = shader.FindKernel("Circles");
        uint threadGroupSizeX; //스레드 그룹 사이즈 
        shader.GetKernelThreadGroupSizes(circlesHandle, out threadGroupSizeX, out _, out _);

        int total = (int)threadGroupSizeX * count;
        circleData = new Circle[total]; //circleData 사이즈 지정

        float speed = 100;
        float halfSpeed = speed * 0.5f;
        float minRadius = 10.0f;
        float maxRadius = 30.0f;
        float radiusRange = maxRadius - minRadius;
        //circle data 계산
        for(int i=0; i<total; i++)
        {
            Circle circle = circleData[i];
            circle.origin.x = Random.value * texResolution;
            circle.origin.y = Random.value * texResolution;
            circle.velocity.x = (Random.value * speed) - halfSpeed;
            circle.velocity.y = (Random.value * speed) - halfSpeed;
            circle.radius = Random.value * radiusRange + minRadius;
            circleData[i] = circle;
        }
    }

    private void InitShader()
    {
    	clearHandle = shader.FindKernel("Clear");
    	
        shader.SetVector( "clearColor", clearColor );
        shader.SetVector( "circleColor", circleColor );
        shader.SetInt( "texResolution", texResolution );

        int stride = (2 + 2 + 1) * 4; //circle 구조체 정보의 크기 float2 + float2 + float * float의 크기(4바이트) 
        buffer = new ComputeBuffer(circleData.Length, stride);
        buffer.SetData(circleData);
        shader.SetBuffer(circlesHandle, "circlesBuffer", buffer);

        shader.SetTexture( circlesHandle, "Result", outputTexture );
        shader.SetTexture( clearHandle, "Result", outputTexture );

        rend.material.SetTexture("_MainTex", outputTexture);
    }
 
    private void DispatchKernel(int count)
    {
    	shader.Dispatch(clearHandle, texResolution/8, texResolution/8, 1);
        shader.SetFloat("time", Time.time);
        shader.Dispatch(circlesHandle, count, 1, 1);
    }

    void Update()
    {
        DispatchKernel(10);
    }

    private void OnDestroy()
    {
        buffer.Dispose();
    }
}

compute shader 코드

#pragma kernel Circles
#pragma kernel Clear
shared RWTexture2D<float4> Result;
//circle 구조체 선언
struct circle
{
	float2 origin;
	float2 velocity;
	float radius;
};
//circleBuffer 생성
StructuredBuffer<circle> circlesBuffer;
float4 clearColor;
float4 circleColor;
int texResolution;
int clearScreen = 0;
float time;
//연산 함수
float random(float value, float seed = 0.546){
	float random = (frac(sin(value + seed) * 143758.5453));
	return random;
}
float2 random2(float value){
	return float2(
		random(value, 3.9812),
		random(value, 7.1536)
	);
}
void plot1( int x, int y, int2 centre){
    Result[uint2(centre.x + x, centre.y + y)] = circleColor;
}
void plot8( int x, int y, int2 centre ) {
	plot1(  x,  y, centre );  plot1(  y,  x, centre );
	plot1(  x, -y, centre );  plot1(  y, -x, centre );
	plot1( -x, -y, centre );  plot1( -y, -x, centre );
	plot1( -x,  y, centre );  plot1( -y,  x, centre );
}
void drawCircle( int2 centre, int radius){
	int x = 0;
	int y = radius;
	int d = 1 - radius;

	while (x < y){
		if (d < 0){
			d += 2 * x + 3;
		}else{
			d += 2 * (x - y) + 5;
			y--;
		}
		
		plot8(x, y, centre);

		x++;
	}

}

//circle 생성 함수 
[numthreads(32,1,1)]
void Circles (uint3 id : SV_DispatchThreadID)
{
	int2 centre = (int2)(circlesBuffer[id.x].origin + circlesBuffer[id.x].velocity * time);
	while (centre.x>texResolution) centre.x -= texResolution;
	while (centre.x<0) centre.x += texResolution;
	while (centre.y>texResolution) centre.y -= texResolution;
	while (centre.y<0) centre.y += texResolution;
	
	uint radius = (int)circlesBuffer[id.x].radius;

	drawCircle( centre, radius );
}
//배경
[numthreads(8,8,1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
	Result[id.xy] = clearColor;
}
728x90
반응형
Comments