파티클 시스템은 파티클이라고 부르는 매우 작은 이미지나 메시를 시뮬레이션하고 렌더링하여 시각 효과를 생성합니다. 시스템의 각 파티클은 효과의 개별 그래픽 요소를 구현합니다. 또한 모든 파티클을 종합적으로 시뮬레이션하여 완성된 효과를 구현합니다.
파티클 시스템은 불, 연기, 액체 등과 같은 동적 오브젝트를 구현할 때 유용합니다.
이러한 오브젝트는 메시(3D)나 스프라이트(2D)로 묘사하기 어렵습니다.
메시와 스프라이트는 집이나 자동차 같은 솔리드 오브젝트를 묘사하는 데 탁월합니다.
즉, 움직임이 살아있는 동적 오브젝트의 표현에 탁월하기에 사용하는 시스템이다.
※ 참고 : Unity메뉴얼 - 파티클 시스템
※particle(입자): 물리적 성질과 화학적 성질을 가진 작은 물체(아주 작은 단위를 표현하고 싶어 사용한거같음)
파티클을 적은 수로 표현할 수도 있겠지만, 방대한 파티클을 다룰 경우가 더 많을 것이다. 이러한 방대한 양의 오브젝트의 계산을 일일히 싱글 스레드인 시스템 메모리 단에서 처리하는 것은 매우 매우 매우 비효율적일 것이다.(아마 frame이 아주 아주 아주 낮아질 것이다.)
이러한 오브젝트 계산을 어찌 어찌 해결하였다해도 결국 렌더링 파이프라인에 저 많은 오브젝트를 일일이 렌더링 해주는 일은 매우 매우 매우 비효율적이다. 이유는 시스템 메모리에서 GPU로 명령을 주는것(렌더링 파이프라인 draw call)은 매우 매우 비싼 작업이기 때문이다.
즉, 우리는 이 2가지 문제(이하 Object Update, Object Render)를 해결해야 게임에 Particle System을 구현해줄 수 있을 것이다.
Object Render는 GPU를 사용하는 방법중에 Graphic 즉, 렌더링에 관한 일을 주는게 아닌 연산을 도와주는 역할을 맡기는 것이다.(실제로 GPU는 이러한 단순한 연산을 많이 하는 일에 적합하다. 이유는 GPU는 수많은 코어(수많은 스레드)를(메모리가 적지만) 활용해 병렬 연산을 하기 때문입니다. 파티클 같은 경우는 단순한 효과이기 때문에 복잡한 연산이 들어가지 않음)
즉, GPGPU(Genral Purpose GPU)를 사용해서 Object Update 연산을 GPU에게 맡기는 것이다.
이는 Compute Shader를 활용한다.
1-1. 우선 Compute Shader를 사용할 것이니 Compute Shader를 만들어준다. 이는 vertex,pixel Shader을 만들때와 마찬가지로 만들면됩니다.
D3DCompileFromFile(_strFilePath.c_str()
, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE
, _strFuncName.c_str(), "cs_5_0", D3DCOMPILE_DEBUG, 0
, m_CSBlob.GetAddressOf(), m_ErrBlob.GetAddressOf())
1-2. 그리고 해당 컴퓨트 쉐이더를 렌더링 파이프라인에 바인딩 해주고
CONTEXT->CSSetShader(m_CS.Get(), 0, 0);
1-3. 마지막으로 우리가 사용할 스레드의 수에 맞는 그룹을 정해주면됩니다.
CONTEXT->Dispatch(m_GroupX, m_GroupY, m_GroupZ);
1-4. Shader 코드에서는 해당 thread 갯수를 지정해주고 해당 그룹의 해당하는 threadID를 주는 시맨틱 SV_DispatchThreadID를 사용하여 각각의 스레드를 이용해주면됩니다. 그럼 병렬로 해당 스레드들은 일을 시작합니다.

1. SV_GroupThreadID
- 해당 GROUP안에서 해당 THREAD가 가지는 번호를 의미합니다. 예를 들면 초,중,고에서 몇학년 몇반의 번호 몇!에서 몇학년 몇반이 되겠습니다.
2. SV_GropuID- 해당 thread가 속한 Group의 번호입니다. 예를 들면 초,중,고에서 초,중,고등학교 이름을 예로 들 수 있습니다.
3. SV_DispatchThreadID- 해당 thread를 그룹 관점에서 본 thread의 번호입니다.
4. SV_GroupIndex- 해당 Group 안에서 해당 thread의 순서를 의미합니다.예를 들면 초,중,고에서 몇학년 몇반의 번호 몇!에서 번호를 예로 들수 있습니다.
1-5. 그 외에는 structred buffer 또는 상수 버퍼를 활용해 일정 값을 넣어 particle을 조정해주면된다.
물론 shader을 바인딩 해주기 전에 건내주는게 순서상으로는 바람직하다.
같은 오브젝트를 (mesh shader는 같이 쓰니) 하나의 드로우콜로 모조리 그려버리는 방법이다. 이러면 draw call이 줄어드니 매우 효율적일것이다.
1. 해당 draw call을 보내줄 shader을 생성해주고 해당 shader에서 instancing으로서 사용할 IA 멤버를 시맨틱으로 지정해줍니다.
uint iInstID : SV_InstanceID;
2. 그리고 원래 drawcall인 색인drawcall DrawIndexed 대신 DrawIndexedInstanced을 사용합니다.
CONTEXT->DrawIndexedInstanced(m_IndexCount, _particleCount, 0, 0, 0);
※ 그려줄 파티클에 대한 정보는 structredbuffer 또는 constantbuffer 을 통해 보내줍니다.