[소스코드]
SourceCode.z01
10.00MB
SourceCode.z02
10.00MB
SourceCode.z03
10.00MB
SourceCode.zip
0.51MB
0. ParticleSpark Shader
더보기
#include "Header.hlsli"
cbuffer SparkBuffer : register(b10)
{
float time;
float duration;
//
float2 startSize;
float3 startDirection;
//
float sizeOverLifeTime;
float3 rotOverLifeTime;
}
struct VertexInput
{
float4 pos : Position;
float2 size : Size;
float3 direction : Direction;
//
float startLifeTime : LifeTime;
float speed : Speed;
float4 particleColor : Color;
};
struct VertexOutput
{
float3 pos : Position;
float2 size : Size;
float time : Time;
//
float4 particleColor : Color;
};
VertexOutput VS(VertexInput input)
{
VertexOutput output;
// 0 ~ 1의 비율로 전달
output.time = time / input.startLifeTime;
//output.time = time / duration;
//
input.direction += startDirection * time;
input.pos = mul(input.pos, world);
output.pos = input.pos.xyz + (input.direction * time) * input.speed;
output.size = input.size;
output.particleColor = input.particleColor;
//
return output;
}
struct PixelInput
{
float4 pos : SV_Position;
float2 uv : UV;
float time : Time;
float4 particleColor : Color;
};
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 camPos = invView._41_42_43;
//
float3 up = invView._21_22_23;
//
float3 forward = camPos - input[0].pos;
forward = normalize(forward);
//
float3 right = normalize(cross(up, forward));
//
float halfWidth = input[0].size.x * 0.5f;
float halfHeight = input[0].size.y * 0.5f;
//
float4 vertices[4];
vertices[0] = float4(input[0].pos + halfWidth * right - halfHeight * up, 1.0f);
vertices[1] = float4(input[0].pos + halfWidth * right + halfHeight * up, 1.0f);
vertices[2] = float4(input[0].pos - halfWidth * right - halfHeight * up, 1.0f);
vertices[3] = float4(input[0].pos - halfWidth * right + halfHeight * up, 1.0f);
//
PixelInput pixelInput;
pixelInput.time = input[0].time;
//
[unroll]
for (int i = 0; i < 4; ++i)
{
pixelInput.pos = mul(vertices[i], view);
pixelInput.pos = mul(pixelInput.pos, projection);
pixelInput.uv = TEXCOORD[i];
pixelInput.particleColor = input[0].particleColor;
// pushback
output.Append(pixelInput);
}
}
//
cbuffer ColorBuffer : register(b10)
{
float4 totalColor;
float4 colorOverLifeTime;
}
//
float4 PS(PixelInput input) : SV_Target
{
float4 albedo = diffuseMap.Sample(samp, input.uv) * mDiffuse;
//
float4 color = lerp(input.particleColor, colorOverLifeTime, input.time);
//
return albedo * color;
}
1. Spark
더보기
#pragma once
class Spark : public Particle
{
public:
enum SHAPE
{
SPHERE,
CIRCLE,
CONE
};
//
public:
struct ParticleInfo
{
int shape{};
bool loop{ true };
UINT count{ 100 };
float duration{ 3.0f };
//
Float2 startLifeTime { 1.0f, 3.0f };
Float2 startSpeed { 1.0f, 3.0f };
Float2 startRadius { 1.0f, 5.0f };
Float2 startSize { 0.1f, 1.0f };
Float4 startColor[2] { {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f} };
//
Float4 colorOverLifeTime{ 1.0f, 1.0f, 1.0f, 0 };
float sizeOverLifeTime { 0.0f };
Float3 rotOverLifeTime {};
};
///////////////////////////////
public:
class SparkBuffer : public ConstBuffer
{
public:
struct Data
{
float time{};
float duration{};
//
Float2 startSize{};
Float3 startDirection{};
//
float sizeOverLifeTime{};
Float3 rotOverLifeTime{};
//
float padding{};
}data;
SparkBuffer() :ConstBuffer(&data, sizeof(Data)) {}
SparkBuffer(Data sparkData) :ConstBuffer(&sparkData, sizeof(Data)) {}
};
///////////////////////////////
private:
struct VertexParticle
{
Float3 position{};
Float2 size{ 1.0f, 1.0f };
Float3 direction{};
//
float startLifeTime{ 1.0f };
float speed{ 1.0f };
Float4 color{1.0f, 1.0f, 1.0f, 1.0f};
float padding[2]{};
//
VertexParticle() {};
};
///////////////////////////////
private:
//vector<VertexParticle*> vertices;
VertexParticle* vertices;
///////////////////////////////
SparkBuffer* sparkBuffer;
ParticleColorBuffer* colorBuffer;
//
ParticleInfo* info;
///////////////////////////////
public:
Spark(wstring diffuseFile, ParticleInfo* info);
~Spark();
// Particle을(를) 통해 상속됨
virtual void Create() override;
virtual void Update() override;
virtual void Render() override;
virtual void PostRender() override;
virtual void Play(Vector3 pos) override;
///////////////////////////////
void UpdateParticle();
///////////////////////////////
ParticleColorBuffer* GetColorBuffer() { return colorBuffer; }
SparkBuffer* GetSparkBuffer() { return sparkBuffer; }
Texture* GetTexture() { return material->diffuseMap; }
//
void SetDirectionByShape(int shape, int i);
};
========================================================================
#include "Framework.h"
Spark::Spark(wstring diffuseFile, ParticleInfo* info)
: Particle(L"Textures/Effect/" + diffuseFile + L".png"),
sparkBuffer(nullptr), colorBuffer(nullptr), info(info)
{
material->SetShader(L"ParticleSpark");
geometryShader = Shader::AddGS(L"ParticleSpark");
//
sparkBuffer = new SparkBuffer();
colorBuffer = new ParticleColorBuffer();
//
Create();
}
Spark::~Spark()
{
delete[] vertices;
//
delete colorBuffer;
delete sparkBuffer;
}
void Spark::Create()
{
vertices = new VertexParticle[MAX_COUNT];
//
UpdateParticle();
//
vertexBuffer = new VertexBuffer(vertices, sizeof(VertexParticle), MAX_COUNT);
}
void Spark::Update()
{
if (!isActive || !isPlay) return;
//
sparkBuffer->data.time += DELTA;
//
if (sparkBuffer->data.time > sparkBuffer->data.duration)
if (info->loop == false)
Stop();
else
Play(this->position);
}
void Spark::Render()
{
if (isActive == false) return;
//
sparkBuffer->SetVSBuffer(10);
colorBuffer->SetPSBuffer(10);
//
Particle::Render();
}
void Spark::PostRender()
{
ImGui::Text("[Particle]");
ImGui::Text("playTime : %.2f", sparkBuffer->data.time);
ImGui::Text("Loop : %d", info->loop);
}
void Spark::Play(Vector3 pos)
{
Particle::Play(pos);
UpdateParticle();
vertexBuffer->Update(vertices, info->count);
}
void Spark::UpdateParticle()
{
sparkBuffer->data.time = 0;
sparkBuffer->data.duration = info->duration;
particleCount = info->count;
//
for (UINT i = 0; i < info->count; ++i)
{
{ // LifeTime
float min = info->startLifeTime.x;
float max = info->startLifeTime.y;
float value = Random(min, max);
vertices[i].startLifeTime = value;
}
{ // Speed
float min = info->startSpeed.x;
float max = info->startSpeed.y;
float value = Random(min, max);
vertices[i].speed = value;
//sparkBuffer->data.startSpeed = value;
}
{ // Size
float min = info->startSize.x;
float max = info->startSize.y;
float value = Random(min, max);
vertices[i].size = Float2(value, value);
}
{ // Direction
SetDirectionByShape(info->shape, i);
}
// Color
Float4 color0 = info->startColor[0];
Float4 color1 = info->startColor[1];
Float4 color = {
(color1.x - color0.x) * Random(0.0f, 1.0f) + color0.x,
(color1.y - color0.y) * Random(0.0f, 1.0f) + color0.y,
(color1.z - color0.z) * Random(0.0f, 1.0f) + color0.z,
1.0f
//(color1.w - color0.w) * Random(0.0f, 1.0f) + color0.w
};
vertices[i].color = color;
}
colorBuffer->data.colorOverLifeTime = info->colorOverLifeTime;
}
void Spark::SetDirectionByShape(int shape, int i)
{
float min = info->startRadius.x;
float max = info->startRadius.y;
float radius = Random(min, max);
//
Vector3 dir = Forward() * radius;
Float3 rot;
//
switch (shape)
{
case Spark::SPHERE:
{
rot.x = Random(0.0f, XM_2PI);
rot.y = Random(0.0f, XM_2PI);
rot.z = Random(0.0f, XM_2PI);
Matrix matRot = XMMatrixRotationRollPitchYaw(rot.x, rot.y, rot.z);
XMStoreFloat3(&vertices[i].direction,
XMVector3TransformCoord(dir.data, matRot));
}
break;
case Spark::CIRCLE:
{
float r1 = Random(-1.0f, 1.0f);
float r2 = Random(-1.0f, 1.0f);
dir = (Forward() * r1 + Right() * r2).Normal() * radius;
XMStoreFloat3(&vertices[i].direction, dir.data);
}
break;
case Spark::CONE:
break;
default:
break;
}
}
2. Editor Scene
더보기
#pragma once
class ParticleScene : public Scene
{
private:
Collider* collider;
//
Spark::ParticleInfo particleInfo;
//
Particle* particle;
//
Texture* selectTexture;
Texture* defaultImage;
ImVec2 imgSize{ 64.0f, 64.0 };
vector<const char*> items{ "image1", "image2", "image3", "image4" };
int e{};
int selectedItem{};
wstring filename{};
////////////////////////////////
public:
ParticleScene();
~ParticleScene();
// Scene을(를) 통해 상속됨
virtual void Update() override;
virtual void PreRender() override;
virtual void Render() override;
virtual void PostRender() override;
////////////////////////////////
void Pick();
////////////////////////////////
void Create();
void Play();
void Pause();
void Resume();
void Clear();
void Load();
void Save();
};
========================================================================
#include "Framework.h"
#include "ParticleScene.h"
ParticleScene::ParticleScene()
:particle(nullptr)
{
collider = new SphereCollider();
collider->UpdateWorld();
collider->scale *= 0.5f;
//
CAMERA->position = { 0, 13.0f, -15.0f };
CAMERA->rotation.x = 0.5f;
//
defaultImage = Texture::Add(L"Textures/Effect/default.png");
}
ParticleScene::~ParticleScene()
{
if(particle)
delete particle;
//
delete collider;
}
void ParticleScene::Update()
{
collider->UpdateWorld();
//
if(particle)
particle->Update();
}
void ParticleScene::PreRender()
{
}
void ParticleScene::Render()
{
//collider->Render();
//
if(particle)
particle->Render();
}
void ParticleScene::PostRender()
{
////////////////////////////////////////////
// Buttons //
if (particle != nullptr)
{
ImGui::Begin("[Particle Transform]");
///////////
ImGui::InputFloat3("Pos", (float*)&particle->position);
ImGui::InputFloat3("Rot", (float*)&particle->rotation);
///////////
ImGui::End();
}
////////////////////////////////////////////
// Buttons //
ImGui::Begin("[ParticleEdior]");
///////////
if (ImGui::Button("Create")) Create();
if (ImGui::Button("Play")) Play();
if (ImGui::Button("Pause")) Pause();
if (ImGui::Button("Resume")) Resume();
if (ImGui::Button("Clear")) Clear();
if (ImGui::Button("Save")) Save();
if (ImGui::Button("Load")) Load();
//
if(particle)
particle->PostRender();
///////////
ImGui::End();
///////////////////////////////
// Particle Info //
ImGui::Begin("[ParticleInfo]");
///////////
{
ImGui::Text("Loop");
ImGui::Checkbox("LP", &particleInfo.loop);
//
ImGui::Text("Shape");
ImGui::RadioButton("sphere", &e, 0); ImGui::SameLine();
ImGui::RadioButton("circle", &e, 1); ImGui::SameLine();
ImGui::RadioButton("cone", &e, 2);
particleInfo.shape = e;
//
ImGui::Text("Texture");
filename = L"Textures/Effect/" + ToWString(items[selectedItem]) + L".png";
selectTexture = Texture::Add(filename);
if (selectTexture && selectTexture->GetSRV())
ImGui::Image(selectTexture->GetSRV(), imgSize);
else
ImGui::Image(defaultImage->GetSRV(), imgSize);
//
ImGui::ListBox("Images", &selectedItem, items.data(), items.size());
ImGui::Text("Count");
ImGui::InputInt("CT", (int*)&particleInfo.count);
ImGui::Text("Duration");
ImGui::InputFloat("DR", &particleInfo.duration);
}
{
ImGui::Text("StartLifeTime");
ImGui::InputFloat2("lt", (float*)&particleInfo.startLifeTime);
ImGui::Text("StartSpeed");
ImGui::InputFloat2("sp", (float*)&particleInfo.startSpeed);
ImGui::Text("StartRadius");
ImGui::InputFloat2("rd", (float*)&particleInfo.startRadius);
float min = particleInfo.startRadius.x * particleInfo.startSpeed.x;
float max = particleInfo.startRadius.y * particleInfo.startSpeed.y;
switch (particleInfo.shape)
{
case 0:
collider->scale = { max, max, max };
collider->SetColor({ 1,0,0,1 });
collider->Render();
collider->scale = { min, min, min };
collider->SetColor({ 0,0,1,1 });
collider->Render();
break;
case 1:
collider->scale = { max, 0, max };
collider->SetColor({ 1,0,0,1 });
collider->Render();
collider->scale = { min, 0, min };
collider->SetColor({ 0,0,1,1 });
collider->Render();
break;
case 2:
break;
default:
break;
}
ImGui::Text("StartSize");
ImGui::InputFloat2("sz", (float*)&particleInfo.startSize);
}
ImGui::Text("StartColor");
ImGui::ColorEdit4("cr1", (float*)&particleInfo.startColor[0]);
ImGui::ColorEdit4("cr2", (float*)&particleInfo.startColor[1]);
{
ImGui::Text("ColorOverLifeTime");
ImGui::ColorEdit4("COLT", (float*)&particleInfo.colorOverLifeTime);
ImGui::Text("SizeOverLifeTime");
ImGui::InputFloat("SOLT", &particleInfo.sizeOverLifeTime);
ImGui::Text("RotOverLifeTime");
ImGui::InputFloat3("ROLT", (float*)&particleInfo.rotOverLifeTime);
}
///////////
ImGui::End();
}
void ParticleScene::Create()
{
if (particle)
Clear();
//
wstring diffuseFile = ToWString(items[selectedItem]);
//selectTexture;
particle = new Spark(diffuseFile, &particleInfo);
}
void ParticleScene::Play()
{
if (particle == nullptr) return;
//
particle->Play(collider->position);
}
void ParticleScene::Pause()
{
if (particle == nullptr) return;
//
particle->Pause();
}
void ParticleScene::Resume()
{
if (particle == nullptr) return;
//
particle->Resume();
}
void ParticleScene::Clear()
{
delete particle;
particle = nullptr;
}
void ParticleScene::Load()
{
if (particle)
Clear();
Spark::ParticleInfo info{};
// Create Binary File
HANDLE file{};
DWORD read{}; // unsigned long
file = CreateFile(L"Textures/Effect/myParticle.fx", GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
V(ReadFile(file, &info, sizeof(Spark::ParticleInfo), &read, nullptr));
//
V(ReadFile(file, &selectedItem, sizeof(int), &read, nullptr));
// Load End
CloseHandle(file);
particleInfo = info;
e = info.shape;
Create();
}
void ParticleScene::Save()
{
// Create Binary File
HANDLE file;
DWORD write; // unsigned long
file = CreateFile(L"Textures/Effect/myParticle.fx", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
V(WriteFile(file, &particleInfo, sizeof(Spark::ParticleInfo), &write, nullptr));
//
V(WriteFile(file, &selectedItem, sizeof(int), &write, nullptr));
// Save End
CloseHandle(file);
}