필요한 개념
- Global, Relative-Local 개념

- 50, 20 이동 후에는 Global이 된다. 여기서 C로 30이동은 Relative이다. 다시 C로 30을 이동하면(G = R * G) Global이 된다.
- 이와 마찬가지로 bone(Releative)에다가 Parent(Global)을 곱해주면 bone에서 부모 공간으로 이동한 bone(Global)이 나온다. 이 값을 다시 bone(Global)에 저장한다.
- Model Animator Class를 상속으로 만들어도 된다. 그러나 여기선 따로 만들었다.
*CBuffer로 넘기는 건 불가
250(MAX_MODEL_TRANSFORMS, 본 관절의 최대 개수) * 500(MAX_MODEL_KEYFRAMES, 키 프레임 최대 개수)
하면 125,000개가 된다.(행렬의 개수)
CBuffer는 4996개 까지 넘길 수 있다.
-> 그래서 텍스쳐를 보낸다.
Clip하나당
byte수로 하면 125,000 * 64 = 8,000,000(약 8메가)
ex) 애니메이션 5개 쓰면 40메가 정도 나옴
애니메이션까지 계산된 행렬을 넘긴다.
행 - KeyFrame(행마다 0번 프레임 1번 프레임 ..)
열 - Bone이 들어감
면 - Clip
최대값 125,000으로 잡고 안쓰는 나머지 부분은 안쓴다.
한면의 크기는 125,000
구조체 배열로 동적할당하는 방식으로(Clip이 몇개가 될지 모르니까)
- Texture
X - Transform(열)
Y - KeyFrame(행)
Z - Clip(면)
Texture의 포멧은 일반적으로 R8G8B8A8 -> 총 4byte
dx10부터는 R32B32G32A32_FLOAT(총 16바이트) UNORM(0 ~ 1), NORM(-1 ~ 1)
R32B32G32A32_FLOAT 이 포맷이 픽셀하나를 저장하는데 최대로 쓸수 있는 크기
픽셀 하나당 16byte를 쓸 수 있다.(But 문제 생김)
Bone하나는 64바이트(Matrix) 근데 픽셀하나는 최대 16바이트이다.
그래서 행렬 하나를 4개로 쪼개서 쓴다.(픽셀 4개로)
쪼개서 가져오는 것은 Shader에서 할것이다.
- Texture 개념(리소스)
Texture포맷들은 그에 상응하는 Array가 있다.
Texture1D - Array
Texture2D - Array
Texture3D - Array
(*TextureCube도 Array가 있다.)
ex)
desc.ArraySize에 값을 주면(2 이상이면)
Texture2DArray가 된다.
* Array가 붙는 것은 D3D11_USAGE_DYNAMIC으로 사용할 수 없다.(x) -> 즉 Array에서는 Dynamic이 걸리지 않음
(그러나 예외적으로 BIND에 D3D11_BIND_SHADER_RESOURCE를 주게 되면 DYNAMIC이 허용될 수 있도록 할 수는 있다.)
만약 애니메이션을 편집하고 싶다면 한단계 올라가서 Texture2D.. 등에서 DYNAMIC을 주면된다.(얘는 걸린다.)
Texture2D나 Texture3D나 차이는 거의없다. MipMap방식이 조금 다를뿐...(약간만 고민하면 됨)
*중요)
스택 메모리의 크기는 윈도우에서 일반적으로 2MB 정도로 할당된다. memcpy의 크기도 스택 메모리에 의존해서 2MB 이상 사용할 수 없으므로 VirtualAlloc()을 사용함
*Tip)
어셈블리어를 보면 현재 실행중인 것들을 볼 수 있는데, 변수 명이나 함수나 다 노출된다. 그래서 현업에서는 사용자에게
배포 되기전에 코드 난독화를 거침(변수명들을 알 수 없는 문자로 바꿈)
요약
스택 프레임의 크기는 약 2MB
Memcpy 불가, VirtualAlloc 사용
램에 먼저 메모리를 할당 후 텍스처에 데이터를 복사해 준다.
면 - Clip
행 - KeyFrame(행마다 0번 프레임 1번 프레임 ..)
열 - Bone이 들어감
- Create SRV, Shader
ShaderResourceView(srv) : GPU 메모리에 복사된 리소스 데이터를 셰이더에서 어떤 형식으로 다룰지를 알려줌
초반에 CurrFrame = 0, NextFrame = 0을 줘서 T-Pose가 아닌, 0번 프레임 동작이 나옴
관련 함수
- VirtualAlloc() 참고
https://cpplab.tistory.com/45?category=957393
VirtualAlloc() 함수
VirtualAlloc() -> 가상메모리를 사용하기 위해 가장 많이 사용, malloc() 보다 발전된 형태 Reserve(예약), Commit(확정)이 가능 * 예약의 경우 물리적 메모리는 전혀 소요되지 않는다. 확정에만 가상메모리
cpplab.tistory.com
ModelAnimator.h
#pragma once
class ModelAnimator
{
public:
ModelAnimator(Shader* shader);
~ModelAnimator();
void Update();
void Render();
public:
void ReadMesh(wstring file);
void ReadMaterial(wstring file);
void ReadClip(wstring file);
Transform* GetTransform() { return transform; }
Model* GetModel() { return model; }
void Pass(UINT pass);
private:
void CreateTexture();
// index는 클립의 번호
void CreateClipTransform(UINT index);
private:
// ClipTransform는 최종 애니메이션이 결합된 행렬
struct ClipTransform
{
// 2차원 행렬
Matrix** Transform;
ClipTransform()
{
// 행은 키프레임 수 만큼
Transform = new Matrix*[MAX_MODEL_KEYFRAMES];
// 그 안(행)에서 열들로 동적할당
for (UINT i = 0; i < MAX_MODEL_KEYFRAMES; i++)
Transform[i] = new Matrix[MAX_MODEL_TRANSFORMS];
}
~ClipTransform()
{
for (UINT i = 0; i < MAX_MODEL_KEYFRAMES; i++)
SafeDeleteArray(Transform[i]);
SafeDeleteArray(Transform);
}
};
// 클립 마다 얘가 동적할당 받아야 한다.
// 3차원 배열은 복잡하니까 2차원 배열로 처리하고 얘를 동적할당 받아서 처리
ClipTransform* clipTransforms = nullptr;
// 텍스쳐로 Shader로 넘겨주기위해
// 얘도 리소스(ID3D11Resource)를 상속 받고 있다.
ID3D11Texture2D* texture = nullptr;
// 리소스(ID3D11Resource)들은 전부 얘로 넘어가게 할 수 있다.
// 특이점은 버퍼도 리소스를 상속받고 있다. 즉, 버퍼도 렌더링 파이프라인을 타지 않더라도 ID3D11ShaderResourceView로 넘길 수 있다.
ID3D11ShaderResourceView* srv;
private:
// 프레임 정보에 대한 구조체
struct KeyframeDesc
{
int Clip = 0; // 현재 플레이하려는 애니메이션의 클립 번호
UINT CurrFrame = 0; // 현재 프레임 번호
UINT NextFrame = 0; // 다음 프레임 번호
float Time = 0.0f; // 현재 플레이되는 시간(0 ~ 1)로 정규화될 것임
float RunningTime = 0.0f; // 애니메이션 시작했을때 부터 시간
float Speed = 1.0f; // 애니메이션 속도
Vector2 Padding;
} keyframeDesc;
ConstantBuffer* frameBuffer;
ID3DX11EffectConstantBuffer* sFrameBuffer;
private:
Shader* shader;
Model* model;
Transform* transform;
};
ModelAnimator.Cpp
#include "Framework.h"
#include "ModelAnimator.h"
ModelAnimator::ModelAnimator(Shader* shader)
: shader(shader)
{
model = new Model();
transform = new Transform(shader);
frameBuffer = new ConstantBuffer(&keyframeDesc, sizeof(KeyframeDesc));
sFrameBuffer = shader->AsConstantBuffer("CB_AnimationFrame");
}
ModelAnimator::~ModelAnimator()
{
SafeDelete(model);
SafeDelete(transform);
SafeDeleteArray(clipTransforms);
SafeRelease(texture);
SafeRelease(srv);
SafeDelete(frameBuffer);
}
void ModelAnimator::Update()
{
ImGui::InputInt("Clip", &keyframeDesc.Clip);
keyframeDesc.Clip %= model->ClipCount();
ImGui::InputInt("CurrFrame", (int*)&keyframeDesc.CurrFrame);
keyframeDesc.CurrFrame %= model->ClipByIndex(keyframeDesc.Clip)->FrameCount();
// Texture가 null이었다면 Texture를 세팅해서
// 넘길 수 있도록 세팅
if (texture == NULL)
{
for (ModelMesh* mesh : model->Meshes())
mesh->SetShader(shader);
CreateTexture();
}
for (ModelMesh* mesh : model->Meshes())
mesh->Update();
}
void ModelAnimator::Render()
{
frameBuffer->Render();
sFrameBuffer->SetConstantBuffer(frameBuffer->Buffer());
for (ModelMesh* mesh : model->Meshes())
{
mesh->SetTransform(transform);
mesh->Render();
}
}
void ModelAnimator::ReadMesh(wstring file)
{
model->ReadMesh(file);
}
void ModelAnimator::ReadMaterial(wstring file)
{
model->ReadMaterial(file);
}
void ModelAnimator::ReadClip(wstring file)
{
model->ReadClip(file);
}
void ModelAnimator::Pass(UINT pass)
{
for (ModelMesh* mesh : model->Meshes())
mesh->Pass(pass);
}
void ModelAnimator::CreateTexture()
{
// 즉, 클립의 면의 개수(클립의 갯수)
clipTransforms = new ClipTransform[model->ClipCount()];
// 각 클립별로 행렬(구조체 ClipTransform)을 만들어 줌
for (UINT i = 0; i < model->ClipCount(); i++)
CreateClipTransform(i);
//Create Texture
{
// X - Transform(열)
// Y - KeyFrame(행)
// Z - Clip(면)
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
/*
Texture의 포멧은 일반적으로 R8G8B8A8 -> 총 4byte
dx10부터는 R32B32G32A32_FLOAT(총 16바이트) UNORM(0 ~ 1), NORM(-1 ~ 1)
R32B32G32A32_FLOAT 이 포맷이 픽셀하나를 저장하는데 최대로 쓸수 있는 크기
픽셀 하나당 16byte를 쓸 수 있다.(But 문제 생김)
Bone하나는 64바이트(Matrix) 근데 픽셀하나는 최대 16바이트이다.
그래서 행렬 하나를 4개로 쪼개서 쓴다.(픽셀 4개로)
-> Width = Transform * 4
픽셀하나당 16바이트이므로 행렬을 저장하려면 * 4를 해서 64로 사용
*/
desc.Width = MAX_MODEL_TRANSFORMS * 4; // 가로
desc.Height = MAX_MODEL_KEYFRAMES;
// 면의 배열의 개수(면의 개수는 clip)
desc.ArraySize = model->ClipCount();
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; // 16Byte * 4 = 64Byte
// 들어가서 변할일이 없다.
desc.Usage = D3D11_USAGE_IMMUTABLE;
// ShaderResourceView로 해서 Shader에 연결시킬 것임
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.MipLevels = 1;
// MSAA와 관련 있다.
desc.SampleDesc.Count = 1;
// 한 면의 바이트 수
UINT pageSize = MAX_MODEL_TRANSFORMS * 4 * 16 * MAX_MODEL_KEYFRAMES; // 행렬이니까 곱하기 16
// Malloc은 생성자 호출x new 키워드는 생성자 호출 o
// Malloc은 길이만 잡는다.(스텍 프레임에 한계가 있다. 스텍프레임은 크기는 2MB이다, 그래서 Malloc은 최대 2MB까지 가능하다.)
// 우리는 하나에 8메가이다.
// 스텍이 깨져버림(깨지면 StackOverFlow가 나옴)
// 텍스쳐 메모리 전체 크기를 잡아놓고 메모리들을 텍스쳐를 만들어서
// 초기값으로 해줌
// 스택 메모리의 크기는 윈도우에서 일반적으로 2MB 정도로 할당된다. memcpy의 크기도 스택 메모리에 의존해서 2MB 이상
// 사용할 수 없으므로 VirtualAlloc을 사용함
// malloc이라면 void* p = malloc(pageSize * model->ClipCount());로 하면 되지만, 2MB를 넘어서 불가
// 가상메모리에 할당, 예약을 건다는 개념, 실제 메모리에 할당해서 쓸 수 있도록 함
// 게임업계에서 많이 씀
// 애니메이션 전체의 텍스쳐 크기 : pageSize * model->ClipCount()
// MEM_RESERVE : 내가 저정도를 쓰겠다 예약
// PAGE_READWRITE : 예약한 곳을 읽고 쓸 수 있게 열어 두겠다.
void* p = VirtualAlloc(NULL, pageSize * model->ClipCount(), MEM_RESERVE, PAGE_READWRITE);
// 예약한 메모리 사이즈를 알아 낼 수 있다.
// VirtualQuery()라는 명령어가 있다. MEMORY_BASIC_INFORMATION를 리턴함
for (UINT c = 0; c < model->ClipCount(); c++)
{
// 각 클립의 시작 번호, 주소
// ex) 0번의 시작.. 1번의 시작.. 이됨
UINT start = c * pageSize;
for (UINT k = 0; k < MAX_MODEL_KEYFRAMES; k++)
{
// 줄단위로 써감
// 시작주소
// 시작 부터 한줄 씩 점프 뛸 수 있다.
// 포인터는 자신의 자료형의 크기만큼 점프하므로 (BYTE*)로 캐스팅한다.
// 얘는 바이트 크기만큼 잡는 거라
// 주소는 바이트 만큼 점프뜀(int는 4바이트)
// why? p는 void라 몇 바이트 점프 뛸지 모름
// 그래서 BYTE*를 줘서 1 BYTE만큼 점프뜀
void* temp = (BYTE*)p + MAX_MODEL_TRANSFORMS * k * sizeof(Matrix) + start;
// 실제 메모리에 씀
// 위에서는 예약을 했다면 여기서는 실제로 씀
// 1) 할당할 주소 2) 할당할 사이즈
// 3) MEM_COMMIT : 메모리 확정시키겠다.(사용하겠다는 의미)
VirtualAlloc(temp, MAX_MODEL_TRANSFORMS * sizeof(Matrix), MEM_COMMIT, PAGE_READWRITE);
// 이제 메모리 사용가능
// 한줄의 사이즈를 씀
memcpy(temp, clipTransforms[c].Transform[k], MAX_MODEL_TRANSFORMS * sizeof(Matrix));
}
}//for(c)
// array를 한장썼을때랑 여러장 썼을 때랑 달라짐
// SubResource할때 맴버로 아래 2개가 더 있다.
// SysMemPitch : 넓이(한줄의 크기)
// SysMemSlicePitch : 한면의 사이즈(한장의 크기)
// 여러장을 사용할 것이어서 SubResource가 배열로 되어야 한다.
D3D11_SUBRESOURCE_DATA* subResources = new D3D11_SUBRESOURCE_DATA[model->ClipCount()];
for (UINT c = 0; c < model->ClipCount(); c++)
{
// 페이지의 시작 위치
void* temp = (BYTE*)p + c * pageSize;
subResources[c].pSysMem = temp;
// 가로줄의 크기
subResources[c].SysMemPitch = MAX_MODEL_TRANSFORMS * sizeof(Matrix);
// 한면의 크기
subResources[c].SysMemSlicePitch = pageSize;
}
// 텍스쳐를 생성
Check(D3D::GetDevice()->CreateTexture2D(&desc, subResources, &texture));
SafeDeleteArray(subResources);
// 가상 메모리를 자유롭게 풀어줘야함
// MEM_RELEASE : 지우겠다.
VirtualFree(p, 0, MEM_RELEASE);
}
//Create SRV
{
D3D11_SHADER_RESOURCE_VIEW_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
// 텍스쳐를 셰이더에서 어떠한 포맷으로 다룰것인지(텍스쳐의 포맷과 동일)
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
// 리소스의 형식을 알려주는 것
// 우리는 TEXTURE2DArray를 사용해서
desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
// 여러개의 맴버변수가 보이는데, 우리가 ViewDimension에서 세팅한
// 리소스타입의 변수에만 넣어줘야 한다.
desc.Texture2DArray.MipLevels = 1;
desc.Texture2DArray.ArraySize = model->ClipCount();
// 1번인자 : 연결한 리소스, 2번 : desc, 3번 : 생성할 객체
Check(D3D::GetDevice()->CreateShaderResourceView(texture, &desc, &srv));
}
// srv를 mesh에 전달함
for (ModelMesh* mesh : model->Meshes())
mesh->TransformsSRV(srv);
}
void ModelAnimator::CreateClipTransform(UINT index)
{
Matrix* bones = new Matrix[MAX_MODEL_TRANSFORMS];
ModelClip* clip = model->ClipByIndex(index);
for (UINT f = 0; f < clip->FrameCount(); f++)
{
for (UINT b = 0; b < model->BoneCount(); b++)
{
ModelBone* bone = model->BoneByIndex(b);
Matrix parent;
Matrix invGlobal = bone->Transform();
D3DXMatrixInverse(&invGlobal, NULL, &invGlobal);
int parentIndex = bone->ParentIndex();
if (parentIndex < 0)
D3DXMatrixIdentity(&parent);
else
parent = bones[parentIndex];
Matrix animation;
ModelKeyframe* frame = clip->Keyframe(bone->Name());
// 이전에 이름이 매칭이 안되는 경우 자기 boneTrnasform을 넣어놨었다.
// 없는 경우
if (frame != nullptr)
{
// 데이터를 가져옴
ModelKeyframeData& data = frame->Transforms[f];
Matrix S, R, T;
D3DXMatrixScaling(&S, data.Scale.x, data.Scale.y, data.Scale.z);
// 쿼터니언으로 가져와야 한다.
D3DXMatrixRotationQuaternion(&R, &data.Rotation);
D3DXMatrixTranslation(&T, data.Translation.x, data.Translation.y, data.Translation.z);
animation = S * R * T;
}
// 애니메이션의 키 데이터가 없을 경우
// 자신의 본 노드 정보를 저장해놓은 것에 identity된 행렬을 곱한다.
// 이후 이 행렬을 부모랑 곱해서 자신의 움직임은 그냥 부모를 따르도록 한다.
else
{
D3DXMatrixIdentity(&animation);
}
// 부모껄 가져와서 animation과 결합후 bones에 다시 넣어놓는다.
// 부모 자식관계를 맺는것과 동일
// animation 행렬은 해당 프레임에 해당 본이 얼마만큼 이동할지를 결정
// *왜 다시 Global로 넣어놨냐면 이 다음에 이걸 parent로 사용하려고
bones[b] = animation * parent;
// 우리가 원하는 건 Global을 뺸값을 넣는 것
// invGlobal도 G이고 bones[b]도 G이다.
// 그래서 역행렬로 없애주고 넣어줌
// Inverse를 만든 이유가 Relative를 만들려고 그랬던 것(G->L)
// 최종 행렬
clipTransforms[index].Transform[f][b] = invGlobal * bones[b];
}//for(f)
}
}
ModelClip.h
#pragma once
// 애니메이션 데이터를 저장할 클래스
struct ModelKeyframeData
{
float Time;
Vector3 Scale;
Quaternion Rotation;
Vector3 Translation;
};
struct ModelKeyframe
{
wstring BoneName;
vector<ModelKeyframeData> Transforms;
};
class ModelClip
{
public:
friend class Model;
private:
ModelClip();
~ModelClip();
public:
float Duration() { return duration; }
float FrameRate() { return frameRate; }
UINT FrameCount() { return frameCount; }
ModelKeyframe* Keyframe(wstring name);
private:
wstring name;
float duration;
float frameRate;
UINT frameCount;
// 맵의 경우 균형 트리로 키를 검색하며 unorderedmap은 해싱으로
// 검색해서 대량의 키가 있는 경우 해싱이 훨씬 빠르다.
unordered_map<wstring, ModelKeyframe*> keyframeMap;
};
ModelClip.cpp
#include "Framework.h"
#include "ModelClip.h"
ModelClip::ModelClip()
{
}
ModelClip::~ModelClip()
{
}
ModelKeyframe* ModelClip::Keyframe(wstring name)
{
if (keyframeMap.count(name) < 1)
return NULL;
return keyframeMap[name];
}
45_Animation.fx
#include "00_Global.fx"
float3 Direction = float3(-1, -1, 1);
struct VertexModel
{
float4 Position : Position;
float2 Uv : Uv;
float3 Normal : Normal;
float3 Tangent : Tangent;
float4 BlendIndices : BlendIndices;
float4 BlendWeights : BlendWeights;
};
#define MAX_MODEL_TRANSFORMS 250
#define MAX_MODEL_KEYFRAMES 500
cbuffer CB_Bone
{
matrix BoneTransforms[MAX_MODEL_TRANSFORMS];
uint BoneIndex;
// CBuffer 전체가 배열이 되지 않는한, padding은 안 넣어줘도 된다.
};
struct AnimationFrame
{
int Clip;
uint CurrFrame;
uint NextFrame;
float Time;
float Running;
float3 Padding;
};
// 묶어서 전역변수로 쓰기 위해
cbuffer CB_AnimationFrame
{
AnimationFrame Keyframes;
};
Texture2DArray TransformsMap;
struct VertexOutput
{
// SV_Position -> position0번으로 취급됨
float4 Position : SV_Position;
float3 Normal : Normal;
float2 Uv : Uv;
};
// inout : 입력, 출력용으로 사용하겠다.
// in : 입력용으로 사용하겠다. out : 출력용으로 사용하겠다.
// Animation의 world를 계산함
// world는 입력도 하고 출력도 할거라서 inout
void SetAnimationWorld(inout matrix world, VertexModel input)
{
// 각 항목을 for문으로 다루기 위해 배열로 저장
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 clip;
int currFrame;
int nextFrame;
float time;
clip = Keyframes.Clip;
currFrame = Keyframes.CurrFrame;
nextFrame = Keyframes.NextFrame;
time = Keyframes.Time;
// 행단위로 써놨었다. 읽어들임
float4 c0, c1, c2, c3;
matrix curr = 0;
// 최종행렬
matrix transform = 0;
[unroll(4)]
for (int i = 0; i < 4; i++)
{
// 가중치가 4개였다.(스키닝을 하기위한)
// Sample은 확대/축소가 일어나므로, 데이터의 변형이 일어나서
// 사용할 수 없다. 그래서 Load을 사용 -> 행열로 원본데이터를 불러옴
// int4 ? 왜 int인가? -> 마지막 w는 밉맵의 번호이다, 우리는 맵맵을 한장으로 사용하므로 0(우리는 민맵 1로 하나만 사용하니까 0을 줌)
// 4개 픽셀 했었어서 *4 곱함(x는 그렇다.)
// + 0은 각 픽셀의 0행을 위해
// 본 번호(열), 키 프레임(행), clip(면)
c0 = TransformsMap.Load(int4(indices[i] * 4 + 0, currFrame, clip, 0)); // 1행
c1 = TransformsMap.Load(int4(indices[i] * 4 + 1, currFrame, clip, 0)); // 2행
c2 = TransformsMap.Load(int4(indices[i] * 4 + 2, currFrame, clip, 0)); // 3행
c3 = TransformsMap.Load(int4(indices[i] * 4 + 3, currFrame, clip, 0)); // 4행
curr = matrix(c0, c1, c2, c3);
// 최종행렬에 가중치만 누적시키면 된다.
// 곱한걸 계속 누적시키는 것(계속 영향을 받을 수 있도록)
// 초반에 설명한 weights[i] 0.5라면 0.5만큼 curr에 곱해줌(움직여줌)
// transform : 가중치, 프레임까지 결합된 행렬
transform += mul(weights[i], curr);
}
// 최종행렬을 world로 변환
// transform : 애니메이션이 이동할 행렬, world : 모델이 출력할 world
world = mul(transform, world);
}
VertexOutput VS(VertexModel input)
{
VertexOutput output;
//World = mul(BoneTransforms[BoneIndex], World);
SetAnimationWorld(World, input);
// ModelMesh의 Transform이 넘어오나, Bone의 위치로 옮긴 다음, World로 옮겨야함
output.Position = WorldPosition(input.Position);
output.Position = ViewProjection(output.Position);
output.Normal = WorldNormal(input.Normal);
output.Uv = input.Uv;
return output;
}
float4 PS(VertexOutput input) : SV_Target
{
// 음영 넣기 위해
float NdotL = dot(normalize(input.Normal), -Direction);
return DiffuseMap.Sample(LinearSampler, input.Uv) * NdotL;
}
technique11 T0
{
P_VP(P0, VS, PS)
P_RS_VP(P1, FillMode_WireFrame, VS, PS)
}
AnimationDemo.h
#pragma once
#include "Systems/IExecute.h"
class AnimationDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {};
virtual void Destroy() override {};
virtual void Update() override;
virtual void PreRender() override {};
virtual void Render() override;
virtual void PostRender() override {};
virtual void ResizeScreen() override {};
private:
void Kachujin();
private:
Shader* shader;
ModelAnimator* kachujin = nullptr;
Vector3 direction = Vector3(-1, -1, +1);
};
AnimationDemo.cpp
#include "stdafx.h"
#include "AnimationDemo.h"
#include "Converter.h"
void AnimationDemo::Initialize()
{
Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
Context::Get()->GetCamera()->Position(1, 36, -85);
shader = new Shader(L"45_Animation.fx");
Kachujin();
}
void AnimationDemo::Update()
{
if (kachujin != nullptr) kachujin->Update();
}
void AnimationDemo::Render()
{
ImGui::SliderFloat3("Direction", direction, -1, +1);
shader->AsVector("Direction")->SetFloatVector(direction);
static int pass = 0;
ImGui::InputInt("Pass2", &pass);
pass %= 2;
if (kachujin != nullptr)
{
kachujin->Pass(pass);
kachujin->Render();
}
}
void AnimationDemo::Kachujin()
{
kachujin = new ModelAnimator(shader);
kachujin->ReadMesh(L"Kachujin/Mesh");
kachujin->ReadMaterial(L"Kachujin/Mesh");
kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
kachujin->ReadClip(L"Kachujin/Salsa Dancing");
kachujin->GetTransform()->Position(0, 0, -30);
kachujin->GetTransform()->Scale(0.025f, 0.025f, 0.025f);
}
결과
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
<DirectX11 3D> 49 - Animation(Blending) (0) | 2022.02.21 |
---|---|
<DirectX11 3D> 48 - Animation(FrameLerp, Tween) (0) | 2022.02.21 |
<Directx11 3D> 42 - Animation Clip Read & Write (0) | 2022.02.16 |
<DirectX11 3D> 41 - Animation (Mesh Skinning) (0) | 2022.02.15 |
<Directx11 3D> 39 - Model Material Rendering + Write File (0) | 2022.02.10 |