在3D模型上计算动画骨骼矩阵的正确方法是什么?

时间:2013-08-30 15:08:47

标签: animation matrix webgl skinning

我正在用WebGL编写一个小游戏来帮助自己理解3D游戏编程中的一些核心概念(不使用现有的引擎)。这个项目就是我在业余时间进行的事情,如果我觉得有必要,那么WebGL版本将成为原型程序的第二个原型。

我目前正处于试图为角色模型制作动画的阶段,并且几天来一直在努力弄清楚它应该如何完成。我发现很难在网上找到任何简单的教程(或者甚至从另一种编程语言翻译),所以我来到这个网站寻求一些指导。如果问题可以在这里得到解决,我希望它能帮助其他人在将来对这个概念提出类似的问题。

我目前的解决方案是这个(对于模型中的每个骨骼):

  1. 翻译到骨骼的原始位置
  2. 转到新请求的位置
  3. 按(现在计算的)父骨矩阵进行变换
  4. 通过原始位置矩阵的倒数进行翻译
  5. 这是到目前为止计算模型矩阵的相关代码。我知道代码不是通过每帧为每个骨骼创建大量新矩阵而完全有效,但我已经这样写了它以便我能够理解它。稍后优化。指示旋转的唯一骨骼是标记为“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;
    

    这是我目前关于如何解决它的想法以及基于上面代码的当前结果的图像。正如你所看到的,手向右延伸到模型脚(只记住肩部实际上是在旋转),所以我假设我写的解决方案存在问题。

    Hand stretches out too far

    感谢任何帮助:)

    使用的实用程序:glMatrix

0 个答案:

没有答案