🎨DirectX

[DX] Animation Skin 데이터 : Skinning 기법

공대 컴린이 2023. 5. 11. 11:21
728x90

🦿 Skinning 

애니메이션에서는 Skinning 이라는 기법을 사용한다. Skinning은 단어 그대로 피부를 메꿔주는 기능을 말한다.

허벅지와 종아리가 일자로 있을때는 아무런 문제가 없지만, 캐릭터가 걸을 때 허벅지와 무릎의 관절이 회전되면서 비어있는 Mesh 공간이 생기고, 허벅지와 무릎이 연결된 정점이 비어있게 된다.

이러한 빈 공간을 메꾸기 위한 기법을 Skinning 기법이라고 한다.

본의 각 정점은 위치(Position)본의 번호 BlendIndices(x,y,z,w)와, 본의 가중치 BlendWeights(x,y,z,w) 값을 갖는다.

어떤 모델인지, 어떤 종류인지에 따라 x,y,z,w 4개의 데이터로 이루어질수도 있지만 8개의 데이터로 이루어질수도 있다. (인간형 Bone의 정점이 동시에 4개 이상 연결된 곳은 없기 때문에 데이터를 최대 4개인 x,y,z,w로 사용하였다.)

이러한 가중치의 x,y,z,w 데이터는 4개이건 8개이건 무조건 모든 합의 결과가 1이 도출되어야 한다.

🦿 Read Skin Data

애니메이션을 출력하기 위한 Skin Data를 읽어오기 위해 scene에 접근하였다.

먼저, scene의 mesh를 불러온 뒤 mesh의 정점 개수크기의 vector<asBoneWeight> boneWeights 변수를 초기화하였다.

vector의 assign 함수를 이용하여  백터의 범위 내(mesh->Vertices.size())에서 해당 값(asBoneWeights())으로 초기화하였다.

📄 asBoneWeights 구조체

더보기
struct asBoneWeights
{
private:
	typedef pair<int, float> Pair;
	vector<Pair> BoneWeights;

public:
	void AddWeights(UINT boneIndex, float boneWeights)
	{
		if (boneWeights <= 0.0f) return;

		bool bInsert = false;
		vector<Pair>::iterator it = BoneWeights.begin();
		while (it != BoneWeights.end())
		{
			if (boneWeights > it->second)
			{
				BoneWeights.insert(it, Pair(boneIndex, boneWeights));
				bInsert = true;

				break;
			}

			it++;
		} // while(it)

		if (bInsert == false)
			BoneWeights.push_back(Pair(boneIndex, boneWeights));
	}

	void GetBlendWeights(asBlendWeight& blendWeights)
	{
		for (UINT i = 0; i < BoneWeights.size(); i++)
		{
			if (i >= 4) return;

			blendWeights.Set(i, BoneWeights[i].first, BoneWeights[i].second);
		}
	}
	/* 크기를 1로 만들어주는 함수 */
	void Normalize() 
	{
		float totalWeight = 0.0f;

		int i = 0;
		vector<Pair>::iterator it = BoneWeights.begin();
		while (it != BoneWeights.end())
		{
			if (i < 4) // 4개 이상은 버림
			{
				totalWeight += it->second;
				i++; it++;
			}
			else
				it = BoneWeights.erase(it);
		}

		float scale = 1.0f / totalWeight;

		it = BoneWeights.begin();
		while (it != BoneWeights.end())
		{
			it->second *= scale;
			it++;
		}
	}
};

asBoneWeights 는 Skin Data 중 Bone의 Weights를 수집하기 위한 구조체로, boneIndex에 따른 boneWeights 값을 추가하거나 불러오거나, 총합을 1로 만드는 Normalize 등의 기능을 수행한다.

 

이후에는 Converter 클래스에서 일어온 Mesh들에 존재하는 각 Bone들의 정보를 불러오고, 순서대로 각 본의 Index를 불러온 뒤, Bone이 가지고있는 Weights의 개수만큼 weight값도 불러왔다.

 

마지막으로 Index와 Weight값을 Normalize하여 정리한 뒤, 최종적으로 mesh 별 정점들의 BlendIndices와 BlendWeights에 저장해주었다.

🦿 Read Skin Data

최종적으로, 읽어들인 SkinData를 확인하기 위해 indices와 weights의 정보들을 csv 파일로 출력해보았다.

 

본의 인덱스와 본의 이름이 순서대로 잘 출력되었고,

 

Mesh의 각 정점마다의 위치와, BlendIndex, BlendWeights 값이 정상적으로 출력된것을 확인할 수 있었다.

 

BoneIndex는 각 본이 x,y,z,w 중 연관되어있는 다른 본 Index를 나타내고, BoneWeight는 연관된 다른 본들이 얼마나 연관되어있는지 정도를 가중치로 나타낸 값이다.

728x90