For me
[DirectX12] 22) Instancing 본문
같은 물체를 다수를 그려야할 때
DrawCall은 성능을 많이 잡아먹는데, Setting 하는 부분이 많은 부분을 굉장한 부하를 주기 때문
=> Instancing으로 묶어 진행한다면?
완전히 똑같은 물체를 만들 때 Instancing으로 묶어 만들 수 있음
SV_InstanceID를 통해 추적, 그거에 따른 배치 정보를 활용
Particle은 배치정보를 어떻게 기입?
=> Particle 같은 경우는 Compute Shader을 사용해서 t_register에 넣기위해 SturcturedBuffer을 사용
Instancing 같은 경우는?
=> Rendering을 할 때 IASetVertexBuffer을 이용하는데, 여러개의 슬롯을 사용함
InstancingBuffer.h
더보기
#pragma once
struct InstancingParams
{
Matrix matWorld;
Matrix matWV;
Matrix matWVP;
};
class InstancingBuffer
{
public:
InstancingBuffer();
~InstancingBuffer();
void Init(uint32 maxCount = 10);
void Clear();
void AddData(InstancingParams& params);
void PushData();
public:
uint32 GetCount() { return static_cast<uint32>(_data.size()); }
ComPtr<ID3D12Resource> GetBuffer() { return _buffer; }
D3D12_VERTEX_BUFFER_VIEW GetBufferView() { return _bufferView; }
void SetID(uint64 instanceId) { _instanceId = instanceId; }
uint64 GetID() { return _instanceId; }
private:
uint64 _instanceId = 0;
ComPtr<ID3D12Resource> _buffer;
D3D12_VERTEX_BUFFER_VIEW _bufferView;
uint32 _maxCount = 0;
vector<InstancingParams> _data;
};
InstancingBuffer.cpp
더보기
#include "pch.h"
#include "InstancingBuffer.h"
#include "Engine.h"
InstancingBuffer::InstancingBuffer()
{
}
InstancingBuffer::~InstancingBuffer()
{
}
void InstancingBuffer::Init(uint32 maxCount)
{
_maxCount = maxCount;
const int32 bufferSize = sizeof(InstancingParams) * maxCount;
D3D12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
DEVICE->CreateCommittedResource(
&heapProperty,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&_buffer));
}
void InstancingBuffer::Clear()
{
_data.clear();
}
void InstancingBuffer::AddData(InstancingParams& params)
{
_data.push_back(params);
}
void InstancingBuffer::PushData()
{
const uint32 dataCount = GetCount();
if (dataCount > _maxCount)
Init(dataCount);
const uint32 bufferSize = dataCount * sizeof(InstancingParams);
void* dataBuffer = nullptr;
D3D12_RANGE readRange{ 0, 0 };
_buffer->Map(0, &readRange, &dataBuffer);
memcpy(dataBuffer, &_data[0], bufferSize);
_buffer->Unmap(0, nullptr);
_bufferView.BufferLocation = _buffer->GetGPUVirtualAddress();
_bufferView.StrideInBytes = sizeof(InstancingParams);
_bufferView.SizeInBytes = bufferSize;
}
InstancingManager.h
더보기
#pragma once
#include "InstancingBuffer.h"
class GameObject;
class InstancingManager
{
DECLARE_SINGLE(InstancingManager);
public:
void Render(vector<shared_ptr<GameObject>>& gameObjects);
void ClearBuffer();
void Clear() { _buffers.clear(); }
private:
void AddParam(uint64 instanceId, InstancingParams& data);
private:
map<uint64/*instanceId*/, shared_ptr<InstancingBuffer>> _buffers;
};
InstancingManager.cpp
더보기
#include "pch.h"
#include "InstancingManager.h"
#include "InstancingBuffer.h"
#include "GameObject.h"
#include "MeshRenderer.h"
#include "Transform.h"
#include "Camera.h"
void InstancingManager::Render(vector<shared_ptr<GameObject>>& gameObjects)
{
map<uint64, vector<shared_ptr<GameObject>>> cache;
//물체 배치
for (shared_ptr<GameObject>& gameObject : gameObjects)
{
const uint64 instanceId = gameObject->GetMeshRenderer()->GetInstanceID();
cache[instanceId].push_back(gameObject);
}
for (auto& pair : cache)
{
const vector<shared_ptr<GameObject>>& vec = pair.second;
//물체 분류
if (vec.size() == 1)
{
// 1개일 경우 원래방식과 같음
vec[0]->GetMeshRenderer()->Render();
}
else
{
// 한번에 그리기 위해 모으는중
const uint64 instanceId = pair.first;
for (const shared_ptr<GameObject>& gameObject : vec)
{
InstancingParams params;
params.matWorld = gameObject->GetTransform()->GetLocalToWorldMatrix();
params.matWV = params.matWorld * Camera::S_MatView;
params.matWVP = params.matWorld * Camera::S_MatView * Camera::S_MatProjection;
AddParam(instanceId, params);
}
shared_ptr<InstancingBuffer>& buffer = _buffers[instanceId];
vec[0]->GetMeshRenderer()->Render(buffer);
}
}
}
void InstancingManager::ClearBuffer()
{
for (auto& pair : _buffers)
{
shared_ptr<InstancingBuffer>& buffer = pair.second;
buffer->Clear();
}
}
void InstancingManager::AddParam(uint64 instanceId, InstancingParams& data)
{
if (_buffers.find(instanceId) == _buffers.end())
_buffers[instanceId] = make_shared<InstancingBuffer>();
_buffers[instanceId]->AddData(data);
}
Unity에서 Compute Buffer 를 사용하여 GPU 인스턴싱을 이용하는 기법 (참고자료)
▶ https://rito15.github.io/posts/unity-compute-buffer-gpu-instancing/
'DirectX12' 카테고리의 다른 글
[DirectX12] 24) Terrain (0) | 2023.01.20 |
---|---|
[DirectX12] 23) Tesellation (0) | 2023.01.20 |
[DirectX12] 21) Particle System (0) | 2023.01.19 |
[DirectX12] 20) Compute Shader (0) | 2023.01.19 |
[DirectX12] 19) Deferred , Forward Rendering (0) | 2023.01.19 |