Hmm.. Ako bi postovao screenshot mogu ti pomoci...
HTR je jednostavan za parsiranje. Binarni format za sada ne postoji... Mogao bi da ga izmislis :)
A sad skinning...
Vertex pored svojih standardnih atributa (position, color, normal, texcoord) moze da ima i dodatne atribute. Definisimo vertex na sledeci nacin:
Code:
typedef struct tagVertex
{
// standardni atributi
float pos[3];
float norm[3];
float texcoord[2];
float color[4];
// dodatni atributi
float boneWeights[4];
float boneIndices[4];
} tVertex;
boneWeights i je niz od max 4 elementa u kome se nalaze tezinski koeficijenti, tj. koliko neki bone utice na doticni vertex. Ove informacije mozes izvuci iz omiljenog programa (Maya, MAX ili nesto trece). Suma svih koeficijenata MORA biti 1.0!!!
boneIndices je niz od max 4 elementa (mora biti iste duzine kao i boneWeights za isti vertex) u tu su smesteni indexi u nizu matrica koje predstavlja bonse u nekom frejmu.
Kako se predstavlja bone kao matrica? Jednostavno. to je matrica 4x4 ali u world space-u, tj. kada se rekurzivno kreces kroz hierarhiju skeleta, akumuliraj transformacije i to smesti u niz. Dakle i-ti bone zauzima i-to mesto u nizu matrica 4x4.
A sad matematika!
Bind poza je predstavlja poziciju skeleta i mesha u 3D programu (Maya ili MAX). Skinovan vertex predstavlja sumu svih pozicija transformisanog pocetnog vertexa i pomnozenog sa odgovarajucim tezinskim koeficijentom. Posto se T-poza u HTR-u nemora poklapati sa bind pozom u Mayi ili MAX-u potrebno je uvesti jos i bidn_pose_matrix_inv, tj. inverznu matricu bone-a u bind pozi.
bind_pose_vertex je u stvari pos u nasoj Vertex strukturi.
skinned_vertex = sum{j in boneInidices} (boneWeights[j] * bone_matrix[j] * bind_pose_matrix_inv[j] *bind_pose_vertex)
Formula se moze dalje optimizovati:
bone_matrix_composition = bone_matrix[j] * bind_pose_matrix_inv[j]
bone_matrix_composition se racuna na CPU u svakom frejmu. Nova formula izgleda:
skinned_vertex = sum{j in boneIndices} (boneWeights[j] * bone_matrix_composition[j] * bind_pose_vertex)
Ako model NEMA nonuniform scale (tj. neproporcionalno skaliranje) dalja optimizacija bi izgledala ovako:
skinned_vertex = sum{j in boneIndices} (boneWeights[j] * bone_matrix_composition[j]) * bind_pose_vertex
Zato moze da se izracuna skin matrica:
skin_matrix = sum{j in boneIndices} (boneWeights[j] * bone_matrix_composition[j])
Najzad, formula izgleda ovako:
skinned_vertex = skin_matrix * bind_pose_vertex
Ova matrica se moze primeniti i na ostale atribute (normalu, tangentu i binormalu)
skinned_normal = skin_matrix * vec4(bind_pose_normal, 0.0)
skinned_tangent = skin_matrix * vec4(bind_pose_tangent, 0.0)
skinned_binormal = skin_matrix * vec4(bind_pose_binormal, 0.0)
Ovde cu se orijentisati na vertex shader i GLSL implementaciju istog.
GLSL shader izgleda ovako:
Code:
attribute vec4 boneWeights; // vec4 je niz od 4 float-a
attribute vec4 boneIndices;
uniform mat4 bones[30]; // max 30 kostiju.
varying vec4 col;
mat4 BuildSkinMatrix()
{
vec4 b = boneIndices;
vec4 w = boneWeights;
mat4 result;
int i;
for (i=0; i<4; i++)
{
result = result + (w[i] * bones[int(b[i])]);
}
return result;
}
void main(void)
{
vec4 vtx;
vec4 nrm;
mat4 skinmatrix = BuildSkinMatrix();
vtx = skinmatrix * gl_Vertex;
gl_Position = gl_ModelViewProjectionMatrix * vtx;
}
Da bi iskoristili gornji code uradite sledece:
1. ucitajte mesh u niz vertexa. Vertexe smestite u strukuru na pocetku.
2. kreirajte VBO i tu iskopirajte sve vertexe.
3. postavite standardne pointere na vertexe u okviru VBO-a
4. postavite pointere na dodatne atribute koriscenjem glVertexAttribPointer().
5. Aktivirajte GLSL vertex shader
6. Za neki frejm izracunajte bone_matrix_composition za sve bonse. Rezultat smestite u uniform varijablu bones.
7. pozovite glDrawElements i posaljite na iscrtavanje sve trouglice iz mesh-a.
Znam da je ovaj post zesci udar na mozak, pogotovu za one koji nisu familijarni sa GLSL shaderima i VBO. Rado bih postovao i neki primer, ali mi je poprilicno nezgodno da to iscupam iz mog programa. Zato... pitajte sve sto vam nije jasno.
yooyo