Instancing - MeshRenderer
- 인스턴싱을 하기 위해서 만든 프레임워크들을 통합해서 관리할 수 있도록 만드는게 목표
InstancingBuffer
InstancingBuffer.h
- 인스턴싱 버퍼를 매 프레임마다 싹 비워주고 데이터를 다시 채워주고 그 데이터들의 대표를 쉐이더에 밀어는 식으로 작업
#pragma once
class VertexBuffer;
struct InstancingData
{
Matrix world;
};
#define MAX_MESH_INSTANCE 500
class InstancingBuffer
{
public:
InstancingBuffer();
~InstancingBuffer();
public:
void ClearData();
void AddData(InstancingData& data);
void PushData();
public:
uint32 GetCount() { return static_cast<uint32>(_data.size()); }
shared_ptr<VertexBuffer> GetBuffer() { return _instanceBuffer; }
private:
void CreateBuffer(uint32 maxCount = MAX_MESH_INSTANCE);
private:
shared_ptr<VertexBuffer> _instanceBuffer;
uint32 _maxCount = 0;
vector<InstancingData> _data;
};
InstancingBuffer.cpp
#include "pch.h"
#include "InstancingBuffer.h"
InstancingBuffer::InstancingBuffer()
{
CreateBuffer(MAX_MESH_INSTANCE);
}
InstancingBuffer::~InstancingBuffer() { }
void InstancingBuffer::ClearData()
{
_data.clear();
}
void InstancingBuffer::AddData(InstancingData& data)
{
_data.push_back(data);
}
void InstancingBuffer::PushData()
{
const uint32 dataCount = GetCount();
if (dataCount > _maxCount)
CreateBuffer(dataCount);
D3D11_MAPPED_SUBRESOURCE subResource;
DC->Map(_instanceBuffer->GetComPtr().Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
{
::memcpy(subResource.pData, _data.data(), sizeof(InstancingData) * dataCount);
}
DC->Unmap(_instanceBuffer->GetComPtr().Get(), 0);
_instanceBuffer->PushData();
}
void InstancingBuffer::CreateBuffer(uint32 maxCount)
{
_maxCount = maxCount;
_instanceBuffer = make_shared<VertexBuffer>();
vector<InstancingData> temp(maxCount);
_instanceBuffer->Create(temp, 1, true);
}
Types.h
- Mesh와 Material을 묶어서 관리하기 위해
using InstanceID = std::pair<uint64, uint64>;
MeshRenderer
- InstanceID를 만들어주기위한 함수 생성
void MeshRenderer::RenderInstancing(shared_ptr<class InstancingBuffer>& buffer)
{
if (_mesh == nullptr || _material == nullptr)
return;
auto shader = _material->GetShader();
if (shader == nullptr)
return;
_material->Update();
_mesh->GetVertexBuffer()->PushData();
_mesh->GetIndexBuffer()->PushData();
buffer->PushData();
shader->DrawIndexedInstanced(0, _pass, _mesh->GetIndexBuffer()->GetCount(), buffer->GetCount());
}
InstanceID MeshRenderer::GetInstanceID()
{
return make_pair((uint64)_mesh.get(), (uint64)_material.get());
}
InstancingManager
InstancingManager.h
#pragma once
#include "InstancingBuffer.h"
class GameObject;
class InstancingManager
{
DECLARE_SINGLE(InstancingManager);
public:
void Render(vector<shared_ptr<GameObject>>& gameObjects);
void ClearData();
private:
void RenderMeshRenderer(vector<shared_ptr< GameObject >> &gameObjects);
private:
void AddData(InstanceID instanceId, InstancingData& data);
private:
map<InstanceID, shared_ptr<InstancingBuffer>> _buffers;
};
InstancingManager.cpp
#include "pch.h"
#include "InstancingManager.h"
#include "GameObject.h"
#include "MeshRenderer.h"
#include "ModelRenderer.h"
#include "ModelAnimator.h"
void InstancingManager::Render(vector<shared_ptr<GameObject>>& gameObjects)
{
ClearData();
RenderMeshRenderer(gameObjects);
}
void InstancingManager::ClearData()
{
for (auto& pair : _buffers)
{
shared_ptr<InstancingBuffer>& buffer = pair.second;
buffer->ClearData();
}
}
void InstancingManager::RenderMeshRenderer(vector<shared_ptr<GameObject>>& gameObjects)
{
map<InstanceID, vector<shared_ptr<GameObject>>> cache;
for (shared_ptr<GameObject>& gameObject : gameObjects)
{
if (gameObject->GetMeshRenderer() == nullptr)
continue;
const InstanceID instanceId = gameObject->GetMeshRenderer()->GetInstanceID();
cache[instanceId].push_back(gameObject);
}
for (auto& pair : cache)
{
const vector<shared_ptr<GameObject>>& vec = pair.second;
{
const InstanceID instanceId = pair.first;
for (int32 i = 0; i < vec.size(); i++)
{
const shared_ptr<GameObject>& gameObject = vec[i];
InstancingData data;
data.world = gameObject->GetTransform()->GetWorldMatrix();
AddData(instanceId, data);
}
shared_ptr<InstancingBuffer>& buffer = _buffers[instanceId];
vec[0]->GetMeshRenderer()->RenderInstancing(buffer);
}
}
}
void InstancingManager::AddData(InstanceID instanceId, InstancingData& data)
{
if (_buffers.find(instanceId) == _buffers.end())
{
_buffers[instanceId] = make_shared<InstancingBuffer>();
}
_buffers[instanceId]->AddData(data);
}
MeshInstancingDemo.cpp
#include "pch.h"
#include "MeshInstancingDemo.h"
#include "Camera.h"
#include "CameraScript.h"
#include "Material.h"
#include "MeshRenderer.h"
#include "IndexBuffer.h"
void MeshInstancingDemo::Init()
{
RESOURCES->Init();
_shader = make_shared<Shader>(L"20.MeshInstancing.fx");
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -10.f });
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
{
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(_shader);
auto texture = RESOURCES->Load<Texture>(L"Veigar", L"../Resources/Textures/veigar.jpg");
material->SetDiffuseMap(texture);
MaterialDesc& desc = material->GetMaterialDesc();
desc.ambient = Vec4(1.f);
desc.diffuse = Vec4(1.f);
desc.specular = Vec4(1.f);
RESOURCES->Add(L"Veigar", material);
}
for (int32 i = 0; i < 500; i++)
{
auto obj = make_shared<GameObject>();
obj->GetOrAddTransform()->SetPosition(Vec3(rand() % 100, 0, rand() % 100));
obj->AddComponent(make_shared<MeshRenderer>());
{
obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar"));
}
{
auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
obj->GetMeshRenderer()->SetMesh(mesh);
}
_objs.push_back(obj);
}
RENDER->Init(_shader);
}
void MeshInstancingDemo::Update()
{
_camera->Update();
RENDER->Update();
{
LightDesc lightDesc;
lightDesc.ambient = Vec4(0.f);
lightDesc.diffuse = Vec4(0.f);
lightDesc.specular = Vec4(0.f);
lightDesc.direction = Vec3(1.f, 0.f, 1.f);
RENDER->PushLightData(lightDesc);
}
INSTANCING->Render(_objs);
}
void MeshInstancingDemo::Render() { }
참고강의
https://www.inflearn.com/courses/lecture?courseId=329791&tab=script&type=LECTURE&unitId=161156&subtitleLanguage=ko