Instancing - ModelRenderer
ModelInstanctingDemo
- 메인 함수에서 사용할 모델을 Tower로 사용
Init() 수정
void ModelInstancingDemo::Init()
{
RESOURCES->Init();
_shader = make_shared<Shader>(L"21.ModelInstancing.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<class Model> m1 = make_shared<Model>();
m1->ReadModel(L"Tower/Tower");
m1->ReadMaterial(L"Tower/Tower");
for (int32 i = 0; i < 500; i++)
{
auto obj = make_shared<GameObject>();
obj->GetOrAddTransform()->SetPosition(Vec3(rand() % 100, 0, rand() % 100));
obj->GetOrAddTransform()->SetScale(Vec3(0.1f));
obj->AddComponent(make_shared<ModelRenderer>(_shader));
{
obj->GetModelRenderer()->SetModel(m1);
}
_objs.push_back(obj);
}
RENDER->Init(_shader);
}
ModelInstancing.fx
- 이전 Model 작업했던 쉐이더와 동일하게 수정
#include "00.Global.fx"
#include "00.Light.fx"
#define MAX_MODEL_TRANSFORMS 250
cbuffer BoneBuffer
{
matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
};
uint BoneIndex;
struct VS_In
{
float4 position : POSITION;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float4 blendIndices : BLEND_INDICES;
float4 blednWeights : BLEND_WEIGHTS;
uint instanceID : SV_INSTANCEID;
matrix world : INST;
};
struct VS_OUT
{
float4 position : SV_POSITION;
float3 worldPosition : POSITION1;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
};
VS_OUT VS(VS_In input)
{
VS_OUT output;
output.position = mul(input.position, BoneTransforms[BoneIndex]);
output.position = mul(input.position, input.world);
output.worldPosition = output.position;
output.position = mul(output.position, VP);
output.uv = input.uv;
output.normal = input.normal;
return output;
}
float4 PS(VS_OUT input) : SV_TARGET
{
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
return color;
}
technique11 T0
{
PASS_VP(P0, VS, PS)
};
ModelRenderer
InstancingManager에서 사용할 GetInstanceID()와 RenderInstancing()함수 생성
GetInstanceID()에서는 Model과 shader를 가져오기
void ModelRenderer::RenderInstancing(shared_ptr<class InstancingBuffer>& buffer)
{
if (_model == nullptr)
return;
BoneDesc boneDesc;
const uint32 boneCount = _model->GetBoneCount();
for (uint32 i = 0; i < boneCount; i++)
{
shared_ptr<ModelBone> bone = _model->GetBoneByIndex(i);
boneDesc.transforms[i] = bone->transform;
}
RENDER->PushBoneData(boneDesc);
const auto& meshes = _model->GetMeshes();
for (auto& mesh : meshes)
{
if (mesh->material)
mesh->material->Update();
_shader->GetScalar("BoneIndex")->SetInt(mesh->boneIndex);
mesh->vertexBuffer->PushData();
mesh->indexBuffer->PushData();
buffer->PushData();
_shader->DrawIndexedInstanced(0, _pass, mesh->indexBuffer->GetCount(), buffer->GetCount());
}
}
InstanceID ModelRenderer::GetInstanceID()
{
return make_pair((uint64)_model.get(), (uint64)_shader.get());
}
InstancingManager
- 오브젝트가 여러개 만들어지고,
INSTANCING->Render()가 진행되기에 InstancingManager에서 Model 작업 추가
GetInstanceID()와 RenderInstancing()을 Render마다 해줘야 하기에 나중에 더 상위 클래스인 Render라는 클래스로 묶어줄 필요가 있다.
void InstancingManager::RenderModelRenderer(vector<shared_ptr<GameObject>>& gameObjects)
{
map<InstanceID, vector<shared_ptr<GameObject>>> cache;
for (shared_ptr<GameObject>& gameObject : gameObjects)
{
if (gameObject->GetModelRenderer() == nullptr)
continue;
const InstanceID instanceId = gameObject->GetModelRenderer()->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]->GetModelRenderer()->RenderInstancing(buffer);
}
}
}
결과

참고 강의
https://www.inflearn.com/courses/lecture?courseId=329791&type=LECTURE&unitId=161157&tab=curriculum&subtitleLanguage=ko