# Skinning and weights

Hello,

I made an animation in Blender 2.60 and successfully exported it to POD. It uses weight based skinning.

I loaded it into the Skinning Demo in the TrainingCourse folder.

Everything works fine but I am having some oddities in the vertex positions. Vertexes positions are near to be correct.

To obtain some detail I rotated some bones around their own axis. I’m not completely sure but it seems that this rotation is not well calculated by the vertex shader, or maybe ignored at all actually.

Any suggestions?

Thanks in advance for any help.

R

With a better observation I saw that the problem might be where more than one bone has influence. I think the sum of the weighted bone matrices is not done properly in the vertex shader. Is this possible?

My weights are normalized so that for every vertex the sum of bones’ influences is always 1.0.

Hi,

Does your POD file look correct in PVRShaman? Also, how many bone influences do you have per-vertex? The OGLES2 Skinning example is setup for 3 bone influences. On a similar note what matrix palette size did you use? Again, the example expects a maximum size of 8. If you have more influences or more matrices in your palette the example will need to be tweaked to support all the skinning information.

Thanks,

Scott

Here’s the code of the slightly modified vertex shader of the Skinning demo.

See the two bold lines before, where the vertex position and normal is calculated from all the bones influencing it.

/

If the current vertex is affected by bones then the vertex position and

normal will be transformed by the bone matrices. Each vertex wil have up

to 4 bone indices (inBoneIndex) and bone weights (inBoneWeights).

The indices are used to index into the array of bone matrices

(BoneMatrixArray) to get the required bone matrix for transformation. The

amount of influence a particular bone has on a vertex is determined by the

weights which should always total 1. So if a vertex is affected by 2 bones

the vertex position in world space is given by the following equation:

position = (BoneMatrixArray[Index0] * inVertex) * Weight0 +

Â (BoneMatrixArray[Index1] * inVertex) * Weight1

The same proceedure is applied to the normals but the translation part of

the transformation is ignored.

After this the position is multiplied by the view and projection matrices

only as the bone matrices already contain the model transform for this

particular mesh. The two-step transformation is required because lighting

will not work properly in clip space.

/

attribute highp   vec3 inVertex;

attribute mediump vec3 inNormal;

attribute mediump vec2 inTexCoord;

attribute mediump vec4 inBoneIndex;

attribute mediump vec4 inBoneWeights;

uniform highp   mat4 MVPMatrix;

uniform highp   mat4 ViewProjMatrix;

uniform mediump vec3 LightDirModel;

uniform mediump vec3 LightDirWorld;

uniform mediump     int      BoneCount;

uniform highp   mat4 BoneMatrixArray;

uniform highp   mat3 BoneMatrixArrayIT;

varying lowp    float LightIntensity;

varying mediump vec2 TexCoord;

varying mediump vec3 RSFactor;

void main()

{

// On PowerVR SGX it is possible to index the components of a vector

// with the [] operator. However this can cause trouble with PC

// emulation on some hardware so we “rotate” the vectors instead.

mediump ivec4 boneIndex = ivec4(inBoneIndex);

mediump vec4 boneWeights = inBoneWeights;

if (BoneCount > 0)

{

highp mat4 boneMatrix = BoneMatrixArray[boneIndex.x];

mediump mat3 normalMatrix = BoneMatrixArrayIT[boneIndex.x];

highp vec4 position = boneMatrix * vec4(inVertex, 1.0) * boneWeights.x;

mediump vec3 worldNormal = normalMatrix * inNormal * boneWeights.x;

// PowerVR SGX supports uniforms in the for loop and nested conditionals.

// For performance reasons, the code below should be like this:

for (lowp int i = 1; i < BoneCount; ++i)

{

boneIndex = boneIndex.yzwx;

boneWeights = boneWeights.yzwx;

boneMatrix = BoneMatrixArray[boneIndex.x];

normalMatrix = BoneMatrixArrayIT[boneIndex.x];

if (boneWeights.x > 0.0)

{

position += boneMatrix * vec4(inVertex, 1.0) * boneWeights.x;

worldNormal += normalMatrix * inNormal * boneWeights.x;

// position += boneMatrix * vec4(inVertex, 1.0) * boneWeights.x; // ORIGINAL

// worldNormal += normalMatrix * inNormal * boneWeights.x; //ORIGINAL

Ã‚Â }

}

// However this code causes a severe crash on PCEmulation

// in some ATI hardware due to a very limited loop support.

// If you are targeting SGX, please, modify the code below.

gl_Position = ViewProjMatrix * position;

// Simple diffuse lighting

LightIntensity = max(0.0, dot(normalize(worldNormal), -LightDirWorld));

}

else

{

gl_Position = MVPMatrix * vec4(inVertex, 1.0);

LightIntensity = max(0.0, dot(inNormal, -LightDirModel));

}

// Pass through texcoords

TexCoord = inTexCoord;

RSFactor = inNormal; // Test pass through

}riccardo_CS2012-03-21 16:52:37

I am using 3 bones influences. Also when I export I specify 3.

In PVRShaman I don’t see the model but I see it almost perfectly with animations and textures in the modded Skinning demo.

I’m not sure I understood what the limitation of 8 refers to. Does that mean I cannot have more than 8 bones in my armature/skeleton?riccardo_CS2012-03-21 16:59:49

When you say you specifiy 3 is this via the "Matrix palette size" field in the "Skin" box? If so, this is the field where you should put the limit of 8. You're correct in a way that this limits the number of bones that affects a mesh to be 8 but your armature/skeleton can have more than that in Blender. The exporter will just split the mesh into batches.

Glad to say I found the problem. At one point during the my work I read around to try to export weights as RGBA and it kind of worked. I realized now that to export successfully from Blender (after having normalized weights) you need to export weights as unsigned bytes, normalized.

I attach the TXT resulting from saving the POD file exporter parameters (using PVRGeoPOD 1.27 beta) that work perfectly for me with Blender 2.60.

bFixedPoint=0

bFlipTextureV=0

bIndexed=1

bUnpackMatrix=0

bInterleaved=1

bSortVtx=1

bTangentSpace=0

cS=2

dwBoneLimit=3

ePrimType=0

eTriSort=1

exportBoneGeometry=0

exportControllers=1

bIndexAnimation=0

exportGeom=1

exportMatrices=1

exportMappingChannel=1

exportMaterials=1

bMerge=0

exportNormals=1

exportObjectSpace=1

exportSkin=1

exportSplines=0

exportVertexColor=1

fTangentSpaceVtxSplit=0.000000e+00

bAlignData=0

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

psVcOptUVW.eType=1

psVcOptUVW.nEnable=131

staticFrame=0

sVcOptBin.eType=1

sVcOptBin.nEnable=135

sVcOptBoneInd.eType=13

sVcOptBoneInd.nEnable=15

sVcOptBoneWt.eType=15

sVcOptBoneWt.nEnable=15

sVcOptCol.eType=4

sVcOptCol.nEnable=15

sVcOptNor.eType=1

sVcOptNor.nEnable=135

sVcOptPos.eType=1

sVcOptPos.nEnable=135

sVcOptTan.eType=1

sVcOptTan.nEnable=135

Hope this helps someone. Cheers!!

It’s indeed helped ! I came to face the exact same problem,

<unsigned bytes, normalized>

Thanks riccardo_CS !