Animation Blend
게임 엔진을 만지다 보면 각각 모션을 이동할때 현재의 상태에 따라 애니메이션을 섞에서 보여주는 기능이 있다.
블렌딩을 할 애니메이션을 현재 상테에 때라 보간해서 바꿔주면 된다.
ModelAnimator.h
private:
struct BlendDesc
{
UINT Mode = 0;
float Alpha = 0;
Vector2 Padding;
KeyframeDesc Clip[3];
} blendDesc;
ConstantBuffer* blendBuffer;
ID3DX11EffectConstantBuffer* sBlendBuffer;
GPU에 넘길 BlendDesc를 만들고
ModelAnimator.cpp
void ModelAnimator::UpdateBlendMode()
{
BlendDesc& desc = blendDesc;
for (UINT i = 0; i < 3; i++)
{
ModelClip* clip = model->ClipByIndex(desc.Clip[i].Clip);
desc.Clip[i].RunningTime += Time::Delta();
float time = 1.0f / clip->FrameRate() / desc.Clip[i].Speed;
if (desc.Clip[i].Time >= 1.0f)
{
desc.Clip[i].RunningTime = 0;
desc.Clip[i].CurrFrame = (desc.Clip[i].CurrFrame + 1) % clip->FrameCount();
desc.Clip[i].NextFrame = (desc.Clip[i].CurrFrame + 1) % clip->FrameCount();
}
desc.Clip[i].Time = desc.Clip[i].RunningTime / time;
}
}
계속해서 업데이트를 한다. 여기까진 간단한데…
Shader
void SetBlendWorld(inout matrix world, VertexModel 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 };
float4 c0, c1, c2, c3;
float4 n0, n1, n2, n3;
matrix curr = 0, next = 0;
matrix currAnim[3];
matrix anim = 0;
matrix transform = 0;
[unroll(4)]
for (int i = 0; i < 4; i++)
{
[unroll(4)]
for (int k = 0; k < 3; k++)
{
c0 = TransformsMap.Load(int4(indices[i] * 4 + 0, BlendFrames.Clip[k].CurrFrame, BlendFrames.Clip[k].Clip, 0));
c1 = TransformsMap.Load(int4(indices[i] * 4 + 1, BlendFrames.Clip[k].CurrFrame, BlendFrames.Clip[k].Clip, 0));
c2 = TransformsMap.Load(int4(indices[i] * 4 + 2, BlendFrames.Clip[k].CurrFrame, BlendFrames.Clip[k].Clip, 0));
c3 = TransformsMap.Load(int4(indices[i] * 4 + 3, BlendFrames.Clip[k].CurrFrame, BlendFrames.Clip[k].Clip, 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformsMap.Load(int4(indices[i] * 4 + 0, BlendFrames.Clip[k].NextFrame, BlendFrames.Clip[k].Clip, 0));
n1 = TransformsMap.Load(int4(indices[i] * 4 + 1, BlendFrames.Clip[k].NextFrame, BlendFrames.Clip[k].Clip, 0));
n2 = TransformsMap.Load(int4(indices[i] * 4 + 2, BlendFrames.Clip[k].NextFrame, BlendFrames.Clip[k].Clip, 0));
n3 = TransformsMap.Load(int4(indices[i] * 4 + 3, BlendFrames.Clip[k].NextFrame, BlendFrames.Clip[k].Clip, 0));
next = matrix(n0, n1, n2, n3);
currAnim[k] = lerp(curr, next, BlendFrames.Clip[k].Time);
}
int clipA = (int) BlendFrames.Alpha;
int clipB = clipA + 1;
float alpha = BlendFrames.Alpha;
if(alpha > 1.0f)
{
alpha = BlendFrames.Alpha - 1.0f;
if(BlendFrames.Alpha >= 2.0f)
{
clipA = 1;
clipB = 2;
}
}
anim = lerp(currAnim[clipA], currAnim[clipB], alpha);
transform += mul(weights[i], anim);
}
world = mul(transform, world);
}
지금은 0,1,2만 해야 하기 때문에 애니메이션들을 보간 해주는데 각각 값에 따라 보간 해준다.
값에 따라 애니메이션이 섞여서 나온다.