AnimaitonDemo에서 Kachujin의 모델이랑 머터리얼, 애니메이션 읽어준 부분을 가져오기void AnimInstancingDemo::Init()
{
RESOURCES->Init();
_shader = make_shared<Shader>(L"21.ModelInstancing.fx");
// Camera
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
shared_ptr<class Model> m1 = make_shared<Model>();
m1->ReadModel(L"Kachujin/Kachujin");
m1->ReadMaterial(L"Kachujin/Kachujin");
m1->ReadAnimation(L"Kachujin/Idle");
m1->ReadAnimation(L"Kachujin/Run");
m1->ReadAnimation(L"Kachujin/Slash");
for (int32 i = 0; i < 500; i++)
{
auto obj = make_shared<GameObject>();
obj->GetOrAddTransform()->SetPosition(Vec3(rand() % 100, 0, rand() % 100));
//obj->GetOrAddTransform()->SetRotation(Vec3(0.5f, 0, 0));
obj->GetOrAddTransform()->SetScale(Vec3(0.01f));
obj->AddComponent(make_shared<ModelRenderer>(_shader));
{
obj->GetModelRenderer()->SetModel(m1);
}
_objs.push_back(obj);
}
RENDER->Init(_shader);
}

instancingManager에서 ReadAnimRenderer()생성ModelAnimator에서 GetInstanceID()와 REnderInstancing()생성world와 동일하게 각각 따로 가지게끔 처리하자RenderManger에서 버퍼 수정void InstancingManager::Render(vector<shared_ptr<GameObject>>& gameObjects)
{
// 매 프레임마다 호출
ClearData();
RenderMeshRenderer(gameObjects);
RenderModelRenderer(gameObjects);
RenderAnimRenderer(gameObjects);
}
void InstancingManager::RenderAnimRenderer(vector<shared_ptr<GameObject>>& gameObjects)
{
map<InstanceID, vector<shared_ptr<GameObject>>> cache;
// 분류 단계
for (shared_ptr<GameObject>& gameObject : gameObjects)
{
if (gameObject->GetModelAnimator() == nullptr)
continue;
const InstanceID instanceId = gameObject->GetModelAnimator()->GetInstanceID();
cache[instanceId].push_back(gameObject);
}
for (auto& pair : cache)
{
shared_ptr<InstancedTweenDesc> instancedTweenDesc = make_shared<InstancedTweenDesc>();
const vector<shared_ptr<GameObject>>& vec = pair.second;
/*if (vec.size() == 1)
{
}
else*/
{
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); // 개인의 월드정보를 가져오는
// INSTANCE
gameObject->GetModelAnimator()->UpdateTweenData();
instancedTweenDesc->tweens[i] = gameObject->GetModelAnimator()->GetTweenDesc();
}
RENDER->PushTweenData(*instancedTweenDesc.get());
// 그려주기 (한명만 대표적으로)
shared_ptr<InstancingBuffer>& buffer = _buffers[instanceId];
vec[0]->GetModelAnimator()->RenderInstancing(buffer);
}
}
}
UpdateTweenData(), RenderInstancing(), GetInstanceID(), GetTweenDesc()UpdateTweenData() : 각각의 모델이 개인으로 들고 있을 애니메이션RenderInstancing() : 공통적으로 가지고 있는 것들 (Bone, Mesh 등)class ModelAnimator : public Component
{
public:
void UpdateTweenData();
void RenderInstancing(shared_ptr<class InstancingBuffer>& buffer);
InstanceID GetInstanceID();
TweenDesc& GetTweenDesc() { return _tweenDesc; }
}
void ModelAnimator::UpdateTweenData()
{
TweenDesc& desc = _tweenDesc;
desc.curr.sumTime += DT;
// 현재 애니메이션
{
shared_ptr<ModelAnimation> currentAnim = _model->GetAnimationByIndex(desc.curr.animIndex);
if (currentAnim)
{
float timePerFrame = 1 / (currentAnim->frameRate * desc.curr.speed);
if (desc.curr.sumTime >= timePerFrame)
{
desc.curr.sumTime = 0;
desc.curr.currFrame = (desc.curr.currFrame + 1) % currentAnim->frameCount;
desc.curr.nextFrame = (desc.curr.currFrame + 1) % currentAnim->frameCount;
}
desc.curr.ratio = (desc.curr.sumTime / timePerFrame);
}
}
// 다음 애니메이션이 예약되어 있다면
if (desc.next.animIndex >= 0)
{
desc.tweenSumTime += DT;
desc.tweenRatio = desc.tweenSumTime / desc.tweenDuration;
if (desc.tweenRatio >= 1.f)
{
// 애니메이션 교체 성공
desc.curr = desc.next;
desc.ClearNextAnim();
}
else
{
// 교체
shared_ptr<ModelAnimation> nextAnim = _model->GetAnimationByIndex(desc.next.animIndex);
desc.next.sumTime += DT;
float timePerFrame = 1.f / (nextAnim->frameRate * desc.next.speed);
if (desc.next.ratio >= 1.f)
{
desc.next.sumTime = 0;
desc.next.currFrame = (desc.next.currFrame + 1) % nextAnim->frameCount;
desc.next.nextFrame = (desc.next.currFrame + 1) % nextAnim->frameCount;
}
desc.next.ratio = desc.next.sumTime / timePerFrame;
}
}
}
void ModelAnimator::RenderInstancing(shared_ptr<class InstancingBuffer>& buffer)
{
if (_model == nullptr)
return;
if (_texture == nullptr)
CreateTexture();
// SRV를 통해 정보 전달
_shader->GetSRV("TransformMap")->SetResource(_srv.Get());
// Bone
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();
// BoneIndex
_shader->GetScalar("BoneIndex")->SetInt(mesh->boneIndex);
mesh->vertexBuffer->PushData();
mesh->indexBuffer->PushData();
buffer->PushData();
_shader->DrawIndexedInstanced(0, _pass, mesh->indexBuffer->GetCount(), buffer->GetCount());
}
}
InstanceID ModelAnimator::GetInstanceID()
{
return make_pair((uint64)_model.get(), (uint64)_shader.get());
}
#include "00.Global.fx"
#include "00.Light.fx"
#define MAX_MODEL_TRANSFORMS 250
#define MAX_MODEL_KEYFRAMES 500
#define MAX_MODEL_INSTANCE 500
struct KeyframeDesc
{
int animIndex;
uint currFrame;
uint nextFrame;
float ratio;
float sumTime;
float speed;
float2 padding;
};
struct TweenFrameDesc
{
float tweenDuration;
float tweenRatio;
float tweenSumTime;
float padding;
KeyframeDesc curr;
KeyframeDesc next;
};
cbuffer TweenBuffer
{
// TweenFramse를 배열로 선언
TweenFrameDesc TweenFrames[MAX_MODEL_INSTANCE];
};
cbuffer BoneBuffer
{
matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
};
uint BoneIndex;
Texture2DArray TransformMap;
struct VS_In
{
float4 position : POSITION;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float4 blendIndices : BLEND_INDICES;
float4 blendWeights : BLEND_WEIGHTS;
// INSTANCING
uint instanceID : SV_INSTANCEID;
matrix world : INST;
};
struct VS_OUT
{
float4 position : SV_POSITION;
float3 worldPosition : POSITION1;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
};
matrix GetAnimationMatrix(VS_In input)
{
float indices[4] = { input.blendIndices.x, input.blendIndices.y, input.blendIndices.z, input.blendIndices.w };
float weights[4] = { input.blendWeights.x, input.blendWeights.y, input.blendWeights.z, input.blendWeights.w };
int animIndex[2];
int currFrame[2];
int nextFrame[2];
float ratio[2];
animIndex[0] = TweenFrames[input.instanceID].curr.animIndex;
currFrame[0] = TweenFrames[input.instanceID].curr.currFrame;
nextFrame[0] = TweenFrames[input.instanceID].curr.nextFrame;
ratio[0] = TweenFrames[input.instanceID].curr.ratio;
animIndex[1] = TweenFrames[input.instanceID].next.animIndex;
currFrame[1] = TweenFrames[input.instanceID].next.currFrame;
nextFrame[1] = TweenFrames[input.instanceID].next.nextFrame;
ratio[1] = TweenFrames[input.instanceID].next.ratio;
float4 c0, c1, c2, c3;
float4 n0, n1, n2, n3;
matrix curr = 0;
matrix next = 0;
matrix transform = 0;
for (int i = 0; i < 4; i++)
{
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[0], animIndex[0], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[0], animIndex[0], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[0], animIndex[0], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[0], animIndex[0], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[0], animIndex[0], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[0], animIndex[0], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[0], animIndex[0], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[0], animIndex[0], 0));
next = matrix(n0, n1, n2, n3);
matrix result = lerp(curr, next, ratio[0]);
// 다음 애니메이션
if (animIndex[1] >= 0)
{
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[1], animIndex[1], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[1], animIndex[1], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[1], animIndex[1], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[1], animIndex[1], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[1], animIndex[1], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[1], animIndex[1], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[1], animIndex[1], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[1], animIndex[1], 0));
next = matrix(n0, n1, n2, n3);
matrix nextResult = lerp(curr, next, ratio[1]);
result = lerp(result, nextResult, TweenFrames[input.instanceID].tweenRatio);
}
transform += mul(weights[i], result);
}
return transform;
}
VS_OUT VS(VS_In input)
{
VS_OUT output;
//output.position = mul(input.position, BoneTransforms[BoneIndex]); // Model Global
matrix m = GetAnimationMatrix(input);
output.position = mul(input.position, m); // W
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)
};
struct InstancedTweenDesc
{ //기존의 TweenDesc 구조체를 배열로 만들어줌
TweenDesc tweens[MAX_MODEL_INSTANCE];
};
class RenderManager
{
DECLARE_SINGLE(RenderManager);
public:
// PushData를 배열단위로 변경
void PushTweenData(const InstancedTweenDesc& desc);
private:
InstancedTweenDesc _tweenDesc;
shared_ptr<ConstantBuffer<InstancedTweenDesc>> _tweenBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _tweenEffectBuffer;
}
void RenderManager::Init(shared_ptr<Shader> shader)
{
/// 위 내용은 생략
_tweenBuffer = make_shared<ConstantBuffer<InstancedTweenDesc>>();
_tweenBuffer->Create();
_tweenEffectBuffer = _shader->GetConstantBuffer("TweenBuffer");
}
void RenderManager::PushTweenData(const InstancedTweenDesc& desc)
{
_tweenDesc = desc;
_tweenBuffer->CopyData(_tweenDesc);
_tweenEffectBuffer->SetConstantBuffer(_tweenBuffer->GetComPtr().Get());
}

https://www.inflearn.com/courses/lecture?courseId=329791&type=LECTURE&unitId=161158