스키닝은 정점(Vertex)이 어떤 뼈대(Bone)에 얼마나 영향을 받는지를 정의하는 기술입니다.
그래서 정점은 뼈대의 움직임을 따라가야 하며, 이 영향 비율을 스키닝 데이터로 저장해야 합니다.
blendIndices: 이 정점이 영향을 받는 뼈의 번호blendWeights: 해당 뼈들로부터 받는 영향력의 가중치 (0.0 ~ 1.0)정점에 뼈 정보(번호/가중치)를 저장하고 쉐이더에 넘길 수 있도록 하기 위한 구조체입니다.
struct asBlendWeight
{
Vec4 indices = Vec4(0, 0, 0, 0);
Vec4 weights = Vec4(0, 0, 0, 0);
void Set(uint32 index, uint32 boneIndex, float weight);
};
뼈 인덱스와 가중치 목록을 저장하는 중간 구조체:
struct asBoneWeights
{
using Pair = pair<int32, float>; // (boneIndex, weight)
vector<Pair> boneWeights;
void AddWeights(uint32 boneIndex, float weight);
void Normalize();
asBlendWeight GetBlendWeights();
};
Assimp에서는 정점이 아닌 뼈에 연결된 정점 정보를 제공하므로, 이를 역으로 정점 기준으로 재정렬해야 합니다.
blendIndices, blendWeights에 저장void Converter::ReadSkinData()
{
for (uint32 i = 0; i < _scene->mNumMeshes; i++)
{
if (!_scene->mMeshes[i]->HasBones()) continue;
auto mesh = _meshes[i];
vector<asBoneWeights> tempVertexBoneWeights(mesh->vertices.size());
for (uint32 b = 0; b < srcMesh->mNumBones; b++)
{
aiBone* bone = srcMesh->mBones[b];
uint32 boneIndex = GetBoneIndex(bone->mName.C_Str());
for (uint32 w = 0; w < bone->mNumWeights; w++)
{
uint32 vertexId = bone->mWeights[w].mVertexId;
float weight = bone->mWeights[w].mWeight;
tempVertexBoneWeights[vertexId].AddWeights(boneIndex, weight);
}
}
for (uint32 v = 0; v < tempVertexBoneWeights.size(); v++)
{
tempVertexBoneWeights[v].Normalize();
asBlendWeight blendWeight = tempVertexBoneWeights[v].GetBlendWeights();
mesh->vertices[v].blendIndices = blendWeight.indices;
mesh->vertices[v].blendWeights = blendWeight.weights;
}
}
}
모든 정점의 위치 + 뼈 인덱스 + 가중치를 Vertices.csv로 출력합니다.
::fprintf(file, "%f,%f,%f,", p.x, p.y, p.z);
::fprintf(file, "%f,%f,%f,%f,", indices.x, indices.y, indices.z, indices.w);
::fprintf(file, "%f,%f,%f,%f\n", weights.x, weights.y, weights.z, weights.w);
엑셀로 열어보면 정점마다 어떤 뼈에 얼마만큼 영향을 받는지 쉽게 확인할 수 있습니다.
Kachujin/Mesh.fbx 모델을 불러와서 ReadModelData와 ReadSkinData를 순차적으로 호출합니다.
void AssimpTool::Init()
{
auto converter = make_shared<Converter>();
converter->ReadAssetFile(L"Kachujin/Mesh.fbx");
converter->ExportMaterialData(L"Kachujin/Kachujin");
converter->ExportModelData(L"Kachujin/Kachujin");
}
모델에서 정점 - 뼈대 매핑 관계를 완전히 추출하게 됩니다.
| 개념 | 설명 |
|---|---|
| 스키닝 | 정점이 어떤 뼈의 영향을 얼마나 받는지를 표현하는 기법 |
| blendIndices | 영향을 받는 뼈의 번호들 (최대 4개) |
| blendWeights | 뼈의 영향력 비율 (합 = 1) |
| asBoneWeights | 뼈번호-가중치 쌍을 정점 기준으로 모은 구조체 |
| ReadSkinData | Assimp에서 정점-뼈대 매핑을 파싱해서 정점 구조체에 세팅 |