7

プログラムにアニメーションを追加しようとしています。

Blender で骨格アニメーションを使用して人間のモデルを作成しました。キーフレームをスキップして、モデルの歩行を確認できます。

モデルを XML (Ogre3D) 形式にエクスポートしました。この XML ファイルでは、特定の時間 (t=0.00000、t=0.00040、... など) で各ボーンに割り当てられた回転、移動、スケールを確認できます。 .)

私が行ったことは、どの頂点が各ボーンに割り当てられているかを見つけることです。ここで、ボーンに対して定義された変換をこれらの頂点のそれぞれに適用するだけでよいと仮定しています。これは正しいアプローチですか?

私の OpenGL draw() 関数 (大まかな擬似コード) では:

for (Bone b : bones){
    gl.glLoadIdentity();

    List<Vertex> v= b.getVertices();
    rotation = b.getRotation();
    translation = b.getTranslation();
    scale = b.getScale();

    gl.glTranslatef(translation);
    gl.glRotatef(rotation);
    gl.glScalef(scale);

    gl.glDrawElements(v);
 }
4

2 に答える 2

5

通常、頂点は複数のボーンの影響を受けます。リニア ブレンド スキニングを行っているようです。私のコードは残念ながら C++ で書かれていますが、うまくいけばアイデアが得られるでしょう:

void Submesh::skin(const Skeleton_CPtr& skeleton)
{
    /*
    Linear Blend Skinning Algorithm:

    P = (\sum_i w_i * M_i * M_{0,i}^{-1}) * P_0 / (sum i w_i)

    Each M_{0,i}^{-1} matrix gets P_0 (the rest vertex) into its corresponding bone's coordinate frame.
    We construct matrices M_n * M_{0,n}^-1 for each n in advance to avoid repeating calculations.
    I refer to these in the code as the 'skinning matrices'.
    */

    BoneHierarchy_CPtr boneHierarchy = skeleton->bone_hierarchy();
    ConfiguredPose_CPtr pose = skeleton->get_pose();
    int boneCount = boneHierarchy->bone_count();

    // Construct the skinning matrices.
    std::vector<RBTMatrix_CPtr> skinningMatrices(boneCount);
    for(int i=0; i<boneCount; ++i)
    {
        skinningMatrices[i] = pose->bones(i)->absolute_matrix() * skeleton->to_bone_matrix(i);
    }

    // Build the vertex array.
    RBTMatrix_Ptr m = RBTMatrix::zeros();       // used as an accumulator for \sum_i w_i * M_i * M_{0,i}^{-1}

    int vertCount = static_cast<int>(m_vertices.size());
    for(int i=0, offset=0; i<vertCount; ++i, offset+=3)
    {
        const Vector3d& p0 = m_vertices[i].position();
        const std::vector<BoneWeight>& boneWeights = m_vertices[i].bone_weights();
        int boneWeightCount = static_cast<int>(boneWeights.size());

        Vector3d p;
        if(boneWeightCount != 0)
        {
            double boneWeightSum = 0;

            for(int j=0; j<boneWeightCount; ++j)
            {
                int boneIndex = boneWeights[j].bone_index();
                double boneWeight = boneWeights[j].weight();
                boneWeightSum += boneWeight;
                m->add_scaled(skinningMatrices[boneIndex], boneWeight);
            }

            // Note: This is effectively p = m*p0 (if we think of p0 as (p0.x, p0.y, p0.z, 1)).
            p = m->apply_to_point(p0);
            p /= boneWeightSum;

            // Reset the accumulator matrix ready for the next vertex.
            m->reset_to_zeros();
        }
        else
        {
            // If this vertex is unaffected by the armature (i.e. no bone weights have been assigned to it),
            // use its rest position as its real position (it's the best we can do).
            p = p0;
        }

        m_vertArray[offset] = p.x;
        m_vertArray[offset+1] = p.y;
        m_vertArray[offset+2] = p.z;
    }
}

void Submesh::render() const
{
    glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
    glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_DOUBLE, 0, &m_vertArray[0]);

    if(m_material->uses_texcoords())
    {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(2, GL_DOUBLE, 0, &m_texCoordArray[0]);
    }

    m_material->apply();

    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_vertIndices.size()), GL_UNSIGNED_INT, &m_vertIndices[0]);

    glPopAttrib();
    glPopClientAttrib();
}

私の知る限りでは、実際の実装では通常、GPU 上でこの種の処理が行われることに注意してください。

于 2010-11-23T22:57:07.810 に答える
1

コードは、各ボーンに独立した変換行列があることを前提としています(各ループ反復の開始時に行列をリセットします)。しかし実際には、ボーンは階層構造を形成し、レンダリングを行うときに保持する必要があります。上腕が回転すると、前腕が取り付けられているため、前腕がそれに沿って回転することを考慮してください。前腕には独自の回転がある場合がありますが、それは上腕で回転した後に適用されます。

次に、スケルトンのレンダリングが再帰的に実行されます。ここにいくつかの擬似コードがあります:

function renderBone(Bone b) {
    setupTransformMatrix(b);
    draw(b);
    foreach c in b.getChildren()
        renderBone(c);
}

main() {
    gl.glLoadIdentity();
    renderBone(rootBone);
}

これがお役に立てば幸いです。

于 2012-09-03T06:33:59.457 に答える