๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“ Development Study/๐Ÿ–ผ GameProgramming 2D

Particle System

by eazuooz 2023. 4. 8.

์˜ˆ์ œ :)

https://github.com/eazuooz/YamYamEngine/commit/ef9a0e889bd89a4b810d83edeabb85d20277f724

 

Particle refactoring · eazuooz/YamYamEngine@ef9a0e8

Show file tree Showing 11 changed files with 78 additions and 90 deletions.

github.com

ํŒŒํ‹ฐํด์ด๋ž€?

๋ฌผ๋ฆฌํ•™์—์„œ ์ž…์ž๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ๊ธฐ๋ณธ ๋˜๋Š” ๊ธฐ๋ณธ ์š”์†Œ์ธ ์ž‘๊ณ  ๊ตญ์†Œํ™”๋œ ๋ฌผ์ฒด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌผ์ฒด๋Š” ๋ฌผ์งˆ ์ž…์ž ๋˜๋Š” ํž˜์„ ์ „๋‹ฌํ•˜๋Š” ์ž…์ž์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ์งˆ ์ž…์ž๋Š” ์šฐ์ฃผ์˜ ๊ตฌ์„ฑ ์š”์†Œ์ด๋ฉฐ ์ „์ž, ์ฟผํฌ, ์ค‘์„ฑ๋ฏธ์ž ๋“ฑ์˜ ์ž…์ž๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ํž˜ ์šด๋ฐ˜ ์ž…์ž๋Š” ๋ฌผ์งˆ ์ž…์ž ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋งค๊ฐœํ•˜๋ฉฐ ์ „์ž๊ธฐ๋ ฅ, ์•ฝ๋ ฅ, ๊ฐ•๋ ฅ, ์ค‘๋ ฅ ๋“ฑ ์ž์—ฐ์˜ ๊ทผ๋ณธ์ ์ธ ํž˜์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

์ž…์ž๋Š” ์งˆ๋Ÿ‰, ์ „ํ•˜, ์Šคํ•€๊ณผ ๊ฐ™์€ ์†์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์งˆ๋Ÿ‰์€ ์ž…์ž์— ํฌํ•จ๋œ ๋ฌผ์งˆ์˜ ์–‘์„ ๊ฒฐ์ •ํ•˜๊ณ , ์ „ํ•˜๋Ÿ‰์€ ์ „์ž๊ธฐ์žฅ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฒฐ์ •ํ•˜๋ฉฐ, ์Šคํ•€์€ ๊ฐ์šด๋™๋Ÿ‰๊ณผ ๊ด€๋ จ๋œ ๋ณธ์งˆ์ ์ธ ์†์„ฑ์ž…๋‹ˆ๋‹ค.

์ž…์ž๋Š” ๋งค์šฐ ์ž‘์€ ๊ทœ๋ชจ์˜ ํ˜„์ƒ์„ ๋‹ค๋ฃจ๋Š” ๋ฌผ๋ฆฌํ•™์˜ ํ•œ ๋ถ„์•ผ์ธ ์–‘์ž์—ญํ•™์˜ ํ‹€ ์•ˆ์—์„œ ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค. ์–‘์ž์—ญํ•™์€ ์ž…์ž๊ฐ€ ์ƒํ™ฉ์— ๋”ฐ๋ผ ํŒŒ๋™๊ณผ ์ž…์ž ๊ฐ™์€ ํ–‰๋™์„ ๋ชจ๋‘ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ํŒŒ๋™-์ž…์ž ์ด์ค‘์„ฑ ๊ฐœ๋…์„ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค.

์ž…์ž ๋ฌผ๋ฆฌํ•™์—์„œ ๊ณผํ•™์ž๋“ค์€ ๊ฐ•๋ ฅํ•œ ์ž…์ž ๊ฐ€์†๊ธฐ์™€ ๊ฒ€์ถœ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž…์ž์˜ ํŠน์„ฑ๊ณผ ์ƒํ˜ธ ์ž‘์šฉ์„ ์—ฐ๊ตฌํ•˜๊ณ  ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์‹คํ—˜์„ ํ†ตํ•ด ์ค‘๋ ฅ์„ ์ œ์™ธํ•œ ๊ธฐ๋ณธ ์ž…์ž์™€ ๊ทธ ์ƒํ˜ธ์ž‘์šฉ์„ ์„ค๋ช…ํ•˜๋Š” ํ˜„์žฌ์˜ ์ด๋ก ์  ํ‹€์ธ ํ‘œ์ค€๋ชจํ˜•์ด ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ฒŒ์ž„์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํšจ๊ณผ๋“ค์„ ๊ทธ๋ž˜ํ”ฝ์ ์œผ๋กœ ํ‘œํ˜„ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” Particle System ์ด๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ์ž‘ํ•˜๊ณ  ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŒŒํ‹ฐํด์˜ ์ƒํƒœ๊ฐ’์„ ์กฐ์ •ํ• ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

namespace ya
{
	enum class eSimulationSpace
	{
		Local,
		World,
	};

	class ParticleSystem : public BaseRenderer
	{
	public:
		ParticleSystem();
		~ParticleSystem();

		virtual void Initialize() override;
		virtual void Update() override;
		virtual void LateUpdate() override;
		virtual void Render() override;

		void SetSimulationSpace(eSimulationSpace space) { mSimulationSpace = space; }

	private:
		class StructedBuffer* mBuffer;
		class StructedBuffer* mSharedBuffer;

		std::shared_ptr<ParticleShader> mCS;
		renderer::ParticleSystemCB mCBData;
		float	mTime;

		Vector4 mStartSize;
		Vector4 mStartColor;
		
		float   mStartLifeTime;
		float	mStartSpeed;
		float	mFrequency;
		UINT    mMaxParticles;
		
		eSimulationSpace mSimulationSpace;
		float	mRadius;
	};
}

ํ•ด๋‹น ๊ฐ’๋“ค์„ ์กฐ์ •ํ•˜์—ฌ ์†๋„, ์ฃผ๊ธฐ, ํฌ๊ธฐ ๋“ฑ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํšจ๊ณผ๋ฅผ ๋งŒ๋“ค์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํŒŒํ‹ฐํด ์‹œ์Šคํ…œ์—์„œ ํ•ต์‹ฌ์€ ํŒŒํ‹ฐํด์€ ๋‹จ์ˆœํ•˜๊ฒŒ ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ…์Šค์ฒ˜๋กœ ๊ทธ๋ ค์ฃผ๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์—

Rect(4๊ฐœ์˜ ์ •์ )์ด ์•„๋‹Œ Point(ํ•˜๋‚˜์˜ ์ •์ )์„ vertex shader๋กœ ๋ณด๋‚ด์ค๋‹ˆ๋‹ค.

Point์ •์ ์„ Geometry shader๋กœ ๋ณด๋‚ด์„œ Rect๋ฉ”์‹œ๋กœ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ ์ •์…ฐ์ด๋„ ํ˜ธ์ถœํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ์„œ

์—ฐ์‚ฐ์—์„œ์˜ ์ด์ ์„ ๊ฐ€์ ธ๊ฐˆ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ •์ ์…ฐ์ด๋”์—์„œ๋Š” Local position๋งŒ geometry shader๋กœ ๋ณด๋‚ด์ค๋‹ˆ๋‹ค.

#include "globals.hlsli"

struct VSIn
{
    float3 LocalPosition : POSITION;
    uint Instance : SV_InstanceID;
};

struct VSOut
{
    float4 LocalPosition : SV_Position;
    uint Instance : SV_InstanceID;
};

VSOut main(VSIn In)
{
    VSOut Out = (VSOut) 0.f;
  
    Out.LocalPosition = float4(In.LocalPosition, 1.0f);
    Out.Instance = In.Instance;
        
    return Out;
}

๊ทธ๋ฆฌ๊ณ  Geometry shader์—์„œ ์ •์ ์„ ๋ณต์‚ฌํ•ด์ค€ํ›„ ๋Š˜๋ ค์ค๋‹ˆ๋‹ค.

#include "globals.hlsli"

struct VSOut
{
    float4 LocalPosition : SV_Position;
    uint Instance : SV_InstanceID;
};

struct GSOut
{
    float4 Position : SV_Position;
    float2 UV : TEXCOORD;
    uint Instance : SV_InstanceID;
};

StructuredBuffer<Particle> ParticleBufferGS : register(t16);

[maxvertexcount(6)]
void main(point VSOut In[1], inout TriangleStream<GSOut> output)
{
    GSOut Out[4] = { (GSOut) 0.f, (GSOut) 0.f, (GSOut) 0.f, (GSOut) 0.f };
    
    if (0 == ParticleBufferGS[In[0].Instance].active)
        return;
   
        
    float3 vWorldPos = In[0].LocalPosition.xyz + ParticleBufferGS[In[0].Instance].position.xyz;
    
    if (simulationSpace == 0)
    {
        vWorldPos += world._41_42_43;
    }
    
    float3 vViewPos = mul(float4(vWorldPos, 1.f), view).xyz;
    float3 vScale = startSize.xyz;
    
    float3 NewPos[4] =
    {
        vViewPos - float3(-0.5f, 0.5f, 0.f)     * vScale,
        vViewPos - float3(0.5f, 0.5f, 0.f)      * vScale,
        vViewPos - float3(0.5f, -0.5f, 0.f)     * vScale,
        vViewPos - float3(-0.5f, -0.5f, 0.f)    * vScale
    };
    
    for (int i = 0; i < 4; ++i)
    {
        Out[i].Position = mul(float4(NewPos[i], 1.f), projection);
    }
    
    
    Out[0].UV = float2(0.f, 0.f);
    Out[1].UV = float2(1.f, 0.f);
    Out[2].UV = float2(1.f, 1.f);
    Out[3].UV = float2(0.f, 1.f);
       
    Out[0].Instance = In[0].Instance;
    Out[1].Instance = In[0].Instance;
    Out[2].Instance = In[0].Instance;
    Out[3].Instance = In[0].Instance;
    // 0 -- 1
    // | \  |
    // 3 -- 2
    output.Append(Out[0]);
    output.Append(Out[1]);
    output.Append(Out[2]);
    output.RestartStrip();
    
    output.Append(Out[0]);
    output.Append(Out[2]);
    output.Append(Out[3]);
    output.RestartStrip();
}

ํ”ฝ์…€์…ฐ์ด๋”์—์„œ๋Š” ๊ทธ๋ƒฅ ์ถœ๋ ฅํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

#include "globals.hlsli"


struct GSOut
{
    float4 Position : SV_Position;
    float2 UV : TEXCOORD;
    uint Instance : SV_InstanceID;
};

float4 main(GSOut In) : SV_Target
{
    float4 Color = (float4) 0.f;
        
    Color = triangleTexture.Sample(anisotropicSampler, In.UV);
    
    if (Color.a <= 0.f)
        discard;
    
    //float fRatio = ParticleBufferGS[In.Instance] / ParticleBuffer[_in.iInstance].fMaxTime;
    Color.rgb *= startColor.rgb;
    
    
    
    return Color;

}

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ˆ˜๋งŽ์€ particle์ด ๊ฐ๊ฐ ์—ฐ์‚ฐ์ด ๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŒŒํ‹ฐํด์˜ ์ด๋™์ด๋‚˜ ํ™œ์„ฑํ™”๋“ฑ ์—ฐ์‚ฐ์„ CPU์—์„œ ํ•˜๋Š”๊ฒƒ๋ณด๋‹ค GPU์—์„œ ํ•˜๋Š”๊ฒƒ์ด ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

Dx11์— Compute shader๋ฅผ ์ด์šฉํ•ด ํŒŒํ‹ฐํด์„ ๊ณ„์‚ฐํ•ด์ฃผ๋ฉด ๊ทธ๋ž˜ํ”ฝ์นด๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

#include "globals.hlsli"


RWStructuredBuffer<Particle> ParticleBufferCS : register(u0);
RWStructuredBuffer<ParticleShared> ParticleSharedBufferCS : register(u1);


[numthreads(128, 1, 1)]
void main(uint3 _id : SV_DispatchThreadID)
{
    if (maxParticles <= _id.x)
        return;
        
    if (0 == ParticleBufferCS[_id.x].active)
    {
        while (0 < ParticleSharedBufferCS[0].gActiveCount)
        {
            int iOriginValue = ParticleSharedBufferCS[0].gActiveCount;
            int iExchange = iOriginValue - 1;
        
            //InterlockedExchange(ParticleSharedBufferCS[0].gActiveCount
            //, iExchange, iExchange);
            InterlockedCompareExchange(ParticleSharedBufferCS[0].gActiveCount
            , iOriginValue, iExchange, iExchange);
        
            if (iOriginValue == iExchange)
            {
                ParticleBufferCS[_id.x].active = 1;
                break;
            }
        }
        
        if (ParticleBufferCS[_id.x].active)
        {
            // ๋žœ๋ค๊ฐ’์œผ๋กœ ์œ„์น˜์™€ ๋ฐฉํ–ฅ์„ ์„ค์ •ํ•œ๋‹ค.
            // ์ƒ˜ํ”Œ๋ง์„ ์‹œ๋„ํ•  UV ๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.
            float4 vRandom = (float4) 0.f;
            
            float2 vUV = float2((float) _id.x / maxParticles, 0.5f);
            vUV.x += elapsedTime;
            vUV.y += sin((vUV.x + elapsedTime) * 3.141592f * 2.f * 10.f) * 0.5f;
            
            vRandom = float4
            (
                  GaussianBlur(vUV + float2(0.f, 0.f)).x
                , GaussianBlur(vUV + float2(0.1f, 0.f)).x
                , GaussianBlur(vUV + float2(0.2f, 0.f)).x
                , GaussianBlur(vUV + float2(0.3f, 0.f)).x
            );
            
            //ParticleBufferCS[_id.x].position.xyz = vRandom.xyz * 1000.0f - 500.0f;
            //ParticleBufferCS[_id.x].position.z = 100.f;
            
                      // ์‚ฌ๊ฐํ˜•๋ฒ”์œ„๋กœ ์Šคํฐ
            // Particle.vRelativePos.xyz = vRandom.xyz * SpawnRange - SpawnRange / 2.f;
            
            // ์›ํ˜• ๋ฒ”์œ„๋กœ ์Šคํฐ
            float fTheta = vRandom.x * 3.141592f * 2.f;
            ParticleBufferCS[_id.x].position.xy = float2(cos(fTheta), sin(fTheta)) * vRandom.y * radius;
            ParticleBufferCS[_id.x].position.z = 0.f;
            ParticleBufferCS[_id.x].direction.xy 
                = -normalize(float2(ParticleBufferCS[_id.x].position.xy));
            
            if (simulationSpace)
            {
                ParticleBufferCS[_id.x].position.xyz += worldPosition.xyz;
            }
            
            // ํŒŒํ‹ฐํด ์†๋ ฅ
            ParticleBufferCS[_id.x].speed = startSpeed; /*vRandom.z * (maxSpeed - minSpeed) + minSpeed*/;
            
            // ํŒŒํ‹ฐํด Life
            ParticleBufferCS[_id.x].time = 0.f;
            ParticleBufferCS[_id.x].lifeTime = startLifeTime; //vRandom.w * (MaxLife - MinLife) + MinLife;
        }
    }
    else
    {
        ParticleBufferCS[_id.x].time += deltaTime;
        if (ParticleBufferCS[_id.x].lifeTime < ParticleBufferCS[_id.x].time)
        {
            ParticleBufferCS[_id.x].active = 0;
        }
        else
        {
            ParticleBufferCS[_id.x].position 
            += ParticleBufferCS[_id.x].direction * ParticleBufferCS[_id.x].speed * deltaTime;
        }
        //ParticleBufferCS[_id.x].position += ParticleBufferCS[_id.x].direction 
        //* ParticleBufferCS[_id.x].speed * deltaTime;
    }
}

'๐Ÿ“ Development Study > ๐Ÿ–ผ GameProgramming 2D' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Audio system (FMOD) ์‚ฌ์šฉ  (0) 2023.06.29
ํฌ์ŠคํŠธ ํ”„๋กœ์„ธ์‹ฑ ํšจ๊ณผ  (0) 2023.06.29
Compute Shader  (0) 2023.04.08
2D Light  (0) 2023.04.08
Animation Event  (0) 2023.03.02

๋Œ“๊ธ€