본문 바로가기
공부/DirectX3D

[DirectX3D] 폭죽 효과(파티클효과 응용)

by MY블로그 2023. 8. 30.

 

DirectX11 3D 상수버퍼, 쉐이더를 사용한 폭죽 효과

기능 구현에 필요한 코드외 생략 하도록 합니다.

이전에 구현하는 눈내리는 효과와는 다르게 하나의 정점에서 시작되어 파티클이 퍼지는 효과입니다.

파티클은 퍼지는 강도, 중력값 등의 조절을 통하여 퍼지는 방식을 변경할 수 있습니다.

 

피격, 스킬, 블러드 이펙트 등등에 사용할 수 있지않을까 생각합니다.

 

폭죽 효과를 적용하기 위한 클래스 생성 (접은글 참조)

더보기
// Pop.h
#pragma once

struct POP_DESC
{
    float duration;		//	총 재생시간
    float time;			//  현재 재생중인시간
    float gravity;		//  중력값
    float padding;
    POP_DESC()
    {
        gravity = 60.0f;
    }
};

class Pop : public Actor, public Particle
{
    static ID3D11Buffer* PopBuffer;
public:
    POP_DESC			desc;
    Vector2             particleScale = Vector2(0, 0); //이미지 크기값
    int                 particleCount = 10;
    float               velocityScalar = 10.0f;

    static void         CreateStaticMember();
    static void         DeleteStaticMember();
    static Pop* Create(string name = "Pop");
    virtual void        Render();
    virtual void        Update();
    void                RenderDetail();
    void                Reset();
    virtual void        Play();
    virtual void        Stop();
};

// Pop.cpp
#include "framework.h"
ID3D11Buffer* Pop::PopBuffer = nullptr;
void Pop::CreateStaticMember()
{
	{
		D3D11_BUFFER_DESC desc = { 0 };
		desc.ByteWidth = sizeof(POP_DESC);
		desc.Usage = D3D11_USAGE_DYNAMIC;
		desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;//상수버퍼
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		desc.MiscFlags = 0;
		desc.StructureByteStride = 0;
		HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, NULL, &PopBuffer);
		assert(SUCCEEDED(hr));
	}
}

void Pop::DeleteStaticMember()
{
	SafeRelease(PopBuffer);
}

Pop* Pop::Create(string name)
{
	Pop* temp = new Pop();
	temp->name = name;
	temp->mesh = make_shared<Mesh>();
	temp->mesh->LoadFile("8.Billboard.mesh");
	temp->shader = RESOURCE->shaders.Load("8.Pop.hlsl");
	temp->shader->LoadGeometry();
	temp->type = ObType::Pop;
	return temp;
}

void Pop::Render()
{
	desc.duration = duration;
	//재생을 시작한 시간
	desc.time = playTime;
	{
		D3D11_MAPPED_SUBRESOURCE mappedResource;
		D3D->GetDC()->Map(PopBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
		memcpy_s(mappedResource.pData, sizeof(POP_DESC), &desc,
			sizeof(POP_DESC));
		D3D->GetDC()->Unmap(PopBuffer, 0);
		D3D->GetDC()->VSSetConstantBuffers(10, 1, &PopBuffer);
	}
	if (isPlaying)
		Actor::Render();
}

void Pop::Update()
{
	Particle::UpdateParticle();
	Actor::Update();
}

void Pop::Reset()
{
	delete[](VertexPSV*)mesh->vertices;
	delete[] mesh->indices;
	mesh->vertices = new VertexPSV[particleCount];
	mesh->indices = new UINT[particleCount];
	mesh->vertexCount = particleCount;
	mesh->indexCount = particleCount;

	Vector2 scale;


	for (UINT i = 0; i < particleCount; i++)
	{
		//이미지 크기 가로세로를 랜덤값
		//4~8 사이값
		scale.x = RANDOM->Float(-particleScale.x, particleScale.x);
		scale.y = RANDOM->Float(-particleScale.y, particleScale.y);
		scale.x = S._11 + scale.x;
		scale.y = S._22 + scale.y;
		if (scale.x < 1.0f)scale.x = 1.0f;
		if (scale.y < 1.0f)scale.y = 1.0f;

		Vector3 position = Vector3(0, 0, 0);


		//방향벡터 Right
		Vector3 velocity = Vector3(1, 0, 0);

		//임의의 회전된 3개축
		Vector3 rot;
		rot.x = RANDOM->Float(0.0f, PI * 2.0f);
		rot.y = RANDOM->Float(0.0f, PI * 2.0f);
		rot.z = RANDOM->Float(0.0f, PI * 2.0f);

		//임의의 회전행렬
		Matrix matRot = Matrix::CreateFromYawPitchRoll(rot.y, rot.x, rot.z);
		// v = v * R
		velocity = Vector3::TransformNormal(velocity, matRot);
		velocity *= velocityScalar;

		//내가 방향벡터를 3개축을 랜덤값으로 회전시켜 잡는다.
		((VertexPSV*)mesh->vertices)[i].velocity = velocity;
		((VertexPSV*)mesh->vertices)[i].position = position;
		((VertexPSV*)mesh->vertices)[i].size = scale;
		mesh->indices[i] = i;
	}
	SafeRelease(mesh->vertexBuffer);
	SafeRelease(mesh->indexBuffer);

	//CreateVertexBuffer
	{
		D3D11_BUFFER_DESC desc;
		desc = { 0 };
		desc.Usage = D3D11_USAGE_DEFAULT;
		desc.ByteWidth = sizeof(VertexPSV) * particleCount;
		desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

		D3D11_SUBRESOURCE_DATA data = { 0 };
		data.pSysMem = mesh->vertices;

		HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, &data, &mesh->vertexBuffer);
		assert(SUCCEEDED(hr));
	}

	//Create Index Buffer
	{
		D3D11_BUFFER_DESC desc;
		ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
		desc.ByteWidth = sizeof(UINT) * particleCount;
		desc.BindFlags = D3D11_BIND_INDEX_BUFFER;

		D3D11_SUBRESOURCE_DATA data = { 0 };
		data.pSysMem = mesh->indices;

		HRESULT hr = D3D->GetDevice()->CreateBuffer(&desc, &data, &mesh->indexBuffer);
		assert(SUCCEEDED(hr));
	}
}

void Pop::Play()
{
	Reset();
	Particle::Play();
}

void Pop::Stop()
{
	Particle::Stop();
}


void Pop::RenderDetail()
{
	Actor::RenderDetail();
	if (ImGui::BeginTabBar("MyTabBar3"))
	{
		if (ImGui::BeginTabItem("Pop"))
		{
			Particle::Gui();
			ImGui::SliderFloat("gravity", &desc.gravity, -100.0f, 100.0f);
			ImGui::SliderFloat("velocityScalar", &velocityScalar, 0.0f, 1000.0f);
			ImGui::SliderFloat2("particleScale", (float*)&particleScale, 0, 100);
			ImGui::SliderInt("particleCount", &particleCount, 1, 100);

			if (ImGui::Button("Reset"))
			{
				Reset();
			}
			ImGui::EndTabItem();
		}
		ImGui::EndTabBar();
	}
}

Mesh, Shader 생성 ( Shader 파일 접은글 참조 )

이번효과 또한 Mesh는 1개의 정점으로 구성되어진 Mesh를 사용하였습니다.

더보기
#include "Common.hlsl"

struct VertexInput
{
    float4 Position : POSITION0;
    float2 Size : SIZE0;
    float3 Velocity : VELOCITY0;
};
struct VertexOutput
{
    float4 Position : POSITION0;
    float2 Size : SIZE0;
};

struct PixelInput
{
    float4 Position : SV_POSITION;
    float2 Uv : UV0;
};

cbuffer VS_Data : register(b10)
{
    float duration;
    float time;
    float gravity;
    float padding1;
}
VertexOutput VS(VertexInput input)
{
    VertexOutput output;
    
    input.Velocity.y -= gravity * time;
    
    //중심점값
    output.Position = mul(input.Position, World);
    
     //벨로시티값으로 퍼지기
    output.Position.xyz += (input.Velocity * time);
    output.Size = input.Size;
    
    
	output.Position.w = 1.0f;
	output.Position = mul(output.Position, View);
    
    return output;
}

static const float2 TEXCOORD[4] =
{
    float2(0.0f, 1.0f),
    float2(0.0f, 0.0f),
    float2(1.0f, 1.0f),
    float2(1.0f, 0.0f)
};
[maxvertexcount(4)]
void GS(point VertexOutput input[1], inout TriangleStream<PixelInput> output)
{
    //한개의 점을 네개로 나누기
    
    // 월드변환후 뷰 프로젝션변환
    
    float3 up = float3(0, 1, 0);
	float3 forward = float3(0, 0, 1);
	float3 right = float3(1, 0, 0);
    
    float2 halfSize = input[0].Size * 0.5f;
    
    float4 vertices[4];
    //input[0].Position.xyz (기준좌표,중점)
    
    //왼쪽 아래
   // vertices[0] = float4(input[0].Position.xyz - halfSize.x * right - halfSize.y * up, 1.0f);
    vertices[0] = float4(input[0].Position.xyz - right * halfSize.x - up * halfSize.y, 1.0f);
    // 왼 위
    vertices[1] = float4(input[0].Position.xyz - right * halfSize.x + up * halfSize.y, 1.0f);
    // 오 아래
    vertices[2] = float4(input[0].Position.xyz + right * halfSize.x - up * halfSize.y, 1.0f);
    // 오 위
    vertices[3] = float4(input[0].Position.xyz + right * halfSize.x + up * halfSize.y, 1.0f);
    
    PixelInput pixelInput;
    
    [unroll(4)]
    for (int i = 0; i < 4; i++)
    {
        //월드에서 다시 ndc까지 변환
        pixelInput.Position = mul(vertices[i], GSProj);
        pixelInput.Uv = TEXCOORD[i];
        
        output.Append(pixelInput);
    }
    
}

float4 PS(PixelInput input) : SV_TARGET
{
    
    float4 BaseColor = DiffuseMapping(input.Uv);
       
    if (BaseColor.a == 0)
        discard;
    return BaseColor;
}

 

조심해야 할것은 지난번 눈내리는 효과와같습니다.

이전글 참조

 

[DirectX3D] 눈내리는 효과(파티클효과 응용)

DirectX11 3D 상수버퍼, 쉐이더를 사용한 눈내리는 효과 기능 구현에 필요한 코드외 생략 하도록 합니다. 구현된 기능은 카메라의 시점에 고정되어져 RTT, PostEffect 처럼 렌더에 적용되는 효과가아닙

rhksgml78.tistory.com

 

댓글