ModelInstancingDemo를 복사해서 AnimInstancingDemo로 이름을 바꾼다.Init, Update, Render 함수는 뼈대는 동일하되, ModelRenderer → ModelAnimator로 교체한다.Kachujin을 로드하고, ModelAnimator에 세팅한다.obj->AddComponent(make_shared<ModelAnimator>(_shader));
obj->GetModelAnimator()->SetModel(m1);
ModelAnimator는 애니메이션이 적용되는 모델 전용 렌더러이다. 움직이지 않는 모델은ModelRenderer로 처리한다.
21. ModelInstancingDemo.fx를 복사해 22. AnimInstancingDemo.fx로 만들고, Client/Shaders/Week3 폴더에 넣는다.AnimInstancingDemo.fx는 애니메이션 보간 및 트윈 시스템을 처리하는 로직이 들어가야 하므로, 기존 TweenDemo.fx 기반으로 설계한다.cbuffer TweenBuffer
{
TweenFrameDesc TweenFrames[MAX_MODEL_INSTANCE];
};
matrix GetAnimationMatrix(VS_IN input)
{
// instanceID를 통해 각 인스턴스의 TweenFrame 정보를 참조
int animIndex = TweenFrames[input.instanceID].curr.animIndex;
...
}
instanceID를 통해 각 인스턴스의 고유한 애니메이션 프레임 데이터를 셰이더에서 받아온다.
RenderModelRenderer까지만 있었는데, ModelAnimator 전용 처리 함수인 RenderAnimRenderer를 추가한다.ModelAnimator 인스턴스를 분류한 후, 월드 행렬과 Tween 정보를 모아서 한 번에 처리한다.shared_ptr<InstancedTweenDesc> tweenDesc = make_shared<InstancedTweenDesc>();
tweenDesc->tweens[i] = gameObject->GetModelAnimator()->GetTweenDesc();
RENDER->PushTweenData(*tweenDesc.get());
Tween 데이터를 렌더링 전에 한 번에 모아서 GPU에 밀어 넣는다. 이 방식이 성능 면에서 효율적이며, 관리도 용이하다.
UpdateTweenData()로 분리RenderInstancing()에서는 실제 GPU에 데이터를 Push하는 역할만 수행void UpdateTweenData()
{
// 프레임 전환, 보간 비율 갱신, 트윈 처리 등
}
void RenderInstancing(shared_ptr<InstancingBuffer>& buffer)
{
// SRV 전달, 본 데이터, 메시 렌더링
}
TweenDesc는 애니메이션 정보를 담은 구조체TweenDesc[MAX_MODEL_INSTANCE] 형태로 묶어서 GPU로 전달RenderManager에서 관리하도록 설계struct InstancedTweenDesc
{
TweenDesc tweens[MAX_MODEL_INSTANCE];
};
void RenderManager::PushTweenData(const InstancedTweenDesc& desc)
{
_tweenBuffer->CopyData(desc);
_tweenEffectBuffer->SetConstantBuffer(_tweenBuffer->GetComPtr().Get());
}
CreateTexture()에서 3차원 텍스처 (Texture2DArray)로 만들어 TransformMap으로 셰이더에 연결_shader->GetSRV("TransformMap")->SetResource(_srv.Get());
정점 셰이더에서는 해당 텍스처를 통해 본 행렬을 읽어오고, 스키닝된 최종 애니메이션 위치를 계산한다.
instanceID를 통해 해당 인스턴스의 Tween 정보를 가져온다.TransformMap에서 현재 프레임과 다음 프레임의 본 행렬을 가져온다.