我正在用 WebGL 编写一个小游戏,以帮助自己理解 3D 游戏编程中的一些核心概念(不使用现有引擎)。这个项目只是我在业余时间做的事情,如果我觉得有必要,WebGL 版本将是一个原型,其次是原生程序。
我目前正处于尝试为角色模型制作动画的阶段,并且几天来一直在努力弄清楚应该如何完成。我发现很难在网上找到任何我可以通过的简单教程(或者甚至从另一种编程语言翻译),所以我来到这个网站寻求一些指导。如果问题可以在这里解决,我希望它也能帮助其他人在未来对这个概念有类似的问题。
我目前的解决方案是这样的(对于模型中的每个骨骼):
- 平移到骨骼的原始位置
- 旋转到新请求的位置
- 通过(现在计算的)父骨骼矩阵进行变换
- 通过原始位置矩阵的逆进行平移
这是迄今为止计算模型矩阵的相关代码。我知道通过为每一帧的每个骨骼创建大量新矩阵,代码效率并不高,但我已经这样写了,这样我就可以理解它了。优化稍后进行。唯一被指示旋转的骨骼是标记为“Bip01 L UpperArm”的骨骼,即角色的右肩。
var temp = 0.0;
ModelInstance.prototype.Tick = function(step, name, hasArmature, bones) {
temp -= 0.0001*step;
if(temp>1.0)
temp -= 2.0;
else if(temp<-1.0)
temp += 2.0;
// Calculate world matrix for overall model
var rotation = quat.fromValues(this.rX, this.rY, this.rZ, 1.0);
quat.calculateW(rotation, rotation);
mat4.fromRotationTranslation(this.worldMatrix, rotation, [this.x, this.y, this.z]);
mat4.scale(this.worldMatrix, this.worldMatrix, [this.sX, this.sY, this.sZ]);
mat4.transpose(this.worldMatrix, this.worldMatrix);
// Calculate bone matrices
if(hasArmature) {
for(var i=0; i<bones.length && i<40; i++) {
// Rotate just the right shoulder for this example
var boneRotate = bones[i].name == "Bip01 L UpperArm" ? quat.fromValues(0.0, 0.0, temp, 1.0) : quat.fromValues(0.0, 0.0, 0.0, 1.0);
quat.calculateW(boneRotate, boneRotate);
// Original Position
// Translate to the position of the bone
var translationMatrix = mat4.create();
mat4.translate(translationMatrix, translationMatrix, [bones[i].pos[0], bones[i].pos[1], bones[i].pos[2]]);
// Rotation Matrix
// Amount to rotate the bone by
var rotationMatrix = mat4.create();
mat4.fromQuat(rotationMatrix, boneRotate);
// New Position
// Rotate the bone around the bones origin by the requested amount
var boneMatrix = mat4.create();
mat4.multiply(boneMatrix, translationMatrix, rotationMatrix);
// Apply parent transformations
if(bones[i].parent>=0) {
var parentID = bones[i].parent;
while(parentID>=0) {
mat4.multiply(boneMatrix, boneMatrix, this.boneMatrices[parentID]);
parentID = bones[parentID].parent;
}
}
// Remove original translation
var inverseMatrix = mat4.create();
mat4.invert(inverseMatrix, translationMatrix);
mat4.multiply(boneMatrix, boneMatrix, inverseMatrix);
// Save matrix
this.boneMatrices[i] = boneMatrix;
}
}
}
一些相关的骨骼数据如下所示:
{
"name":"Bip01",
"parent":-1,
"pos":[0.00000000,2.48177004,0.03703270]
},
/*....*/
{
"name":"Bip01 Pelvis",
"parent":0,
"pos":[0.00000000,2.48177004,0.03141975]
},
{
"name":"Bip01 Spine",
"parent":2,
"pos":[0.00000000,2.73970008,0.03122885]
},
{
"name":"Bip01 Spine1",
"parent":3,
"pos":[0.00000000,2.97957993,0.03618195]
},
{
"name":"Bip01 Neck",
"parent":4,
"pos":[0.00000000,3.83638000,0.00980067]
}
/*....*/
{
"name":"Bip01 L Clavicle",
"parent":5,
"pos":[0.07849900,3.83639002,0.00993846]
},
{
"name":"Bip01 L UpperArm",
"parent":10,
"pos":[0.47621104,3.63395000,-0.04814709]
},
{
"name":"Bip01 L Forearm",
"parent":11,
"pos":[0.93108791,3.14579010,-0.13482325]
},
{
"name":"Bip01 L Hand",
"parent":12,
"pos":[1.32678998,2.72037005,-0.01493155]
}
/*....*/
最后,顶点着色器的相关部分如下所示:
mat4 skinMatrix;
skinMatrix = uBoneMatrices[int(aBoneIndex.x)] * aBoneWeight.x +
uBoneMatrices[int(aBoneIndex.y)] * aBoneWeight.y +
uBoneMatrices[int(aBoneIndex.z)] * aBoneWeight.z +
uBoneMatrices[int(aBoneIndex.w)] * aBoneWeight.w;
gl_Position = (((vec4(aPosition, 1.0) * skinMatrix) * uWMatrix) * uVMatrix) * uPMatrix;
vLightViewPosition = (((vec4(aPosition, 1.0) * skinMatrix) * uWMatrix) * uLVMatrix) * uLPMatrix;
这是我目前对如何解决它的想法以及基于上面代码的当前结果的图像。如您所见,手直接伸到模特的脚上(请记住,实际上只有肩膀在旋转),所以我假设我编写的解决方案存在问题。
任何帮助表示赞赏:)
使用的工具:glMatrix