2

在我的游戏项目中,我使用的是 MD5 模型文件,但我觉得我做错了什么......

在每一帧我更新近 30~40 个动画网格,(更新每个关节及其各自的顶点)但是这样做我总是使用 25% 的 CPU 速度,我的 FPS 总是保持在 70~80(当我应该有 200~ 300)。

我知道也许我应该使用实例化,但我不知道如何使用动画网格来做到这一点。即使我会使用,据我所知,这只适用于相同的网格,但我需要大约 30 个不同的网格用于场景(这些将使用实例化重复)。

我每帧所做的是,为每个动画网格制作新骨架,将每个关节放在新位置(如果关节需要更新)并更新所有应该更新的顶点。

我的显卡没问题,这是更新代码:

bool AnimationModelClass::UpdateMD5Model(float deltaTime, int animation)
{   
    MD5Model.m_animations[animation].currAnimTime += deltaTime;         // Update the current animation time

    if(MD5Model.m_animations[animation].currAnimTime > MD5Model.m_animations[animation].totalAnimTime)
        MD5Model.m_animations[animation].currAnimTime = 0.0f;

    // Which frame are we on
    float currentFrame = MD5Model.m_animations[animation].currAnimTime * MD5Model.m_animations[animation].frameRate;    
    int frame0 = floorf( currentFrame );
    int frame1 = frame0 + 1;

    // Make sure we don't go over the number of frames  
    if(frame0 == MD5Model.m_animations[animation].numFrames-1)
        frame1 = 0;

    float interpolation = currentFrame - frame0;    // Get the remainder (in time) between frame0 and frame1 to use as interpolation factor

    std::vector<Joint> interpolatedSkeleton;        // Create a frame skeleton to store the interpolated skeletons in

    // Compute the interpolated skeleton
    for( int i = 0; i < MD5Model.m_animations[animation].numJoints; i++)
    {
        Joint tempJoint;
        Joint joint0 = MD5Model.m_animations[animation].frameSkeleton[frame0][i];       // Get the i'th joint of frame0's skeleton
        Joint joint1 = MD5Model.m_animations[animation].frameSkeleton[frame1][i];       // Get the i'th joint of frame1's skeleton

        tempJoint.parentID = joint0.parentID;                                           // Set the tempJoints parent id

        // Turn the two quaternions into XMVECTORs for easy computations
        D3DXQUATERNION joint0Orient = D3DXQUATERNION(joint0.orientation.x, joint0.orientation.y, joint0.orientation.z, joint0.orientation.w);
        D3DXQUATERNION joint1Orient = D3DXQUATERNION(joint1.orientation.x, joint1.orientation.y, joint1.orientation.z, joint1.orientation.w);

        // Interpolate positions
        tempJoint.pos.x = joint0.pos.x + (interpolation * (joint1.pos.x - joint0.pos.x));
        tempJoint.pos.y = joint0.pos.y + (interpolation * (joint1.pos.y - joint0.pos.y));
        tempJoint.pos.z = joint0.pos.z + (interpolation * (joint1.pos.z - joint0.pos.z));

        // Interpolate orientations using spherical interpolation (Slerp)
        D3DXQUATERNION qtemp;
        D3DXQuaternionSlerp(&qtemp, &joint0Orient, &joint1Orient, interpolation);

        tempJoint.orientation.x = qtemp.x;
        tempJoint.orientation.y = qtemp.y;
        tempJoint.orientation.z = qtemp.z;
        tempJoint.orientation.w = qtemp.w;
        // Push the joint back into our interpolated skeleton
        interpolatedSkeleton.push_back(tempJoint);      
    }

    for ( int k = 0; k < MD5Model.numSubsets; k++)
    {
        for ( int i = 0; i < MD5Model.m_subsets[k].numVertices; ++i )
        {
            Vertex tempVert = MD5Model.m_subsets[k].m_vertices[i];

            // Make sure the vertex's pos is cleared first
            tempVert.x = 0;
            tempVert.y = 0;
            tempVert.z = 0;

            // Clear vertices normal
            tempVert.nx = 0;
            tempVert.ny = 0;
            tempVert.nz = 0;

            // Sum up the joints and weights information to get vertex's position and normal
            for ( int j = 0; j < tempVert.WeightCount; ++j )
            {
                Weight tempWeight = MD5Model.m_subsets[k].m_weights[tempVert.StartWeight + j];
                Joint tempJoint = interpolatedSkeleton[tempWeight.jointID];

                // Convert joint orientation and weight pos to vectors for easier computation
                D3DXQUATERNION tempJointOrientation = D3DXQUATERNION(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);
                D3DXQUATERNION tempWeightPos = D3DXQUATERNION(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f);

                // We will need to use the conjugate of the joint orientation quaternion
                D3DXQUATERNION tempJointOrientationConjugate;
                D3DXQuaternionInverse(&tempJointOrientationConjugate, &tempJointOrientation);

                // Calculate vertex position (in joint space, eg. rotate the point around (0,0,0)) for this weight using the joint orientation quaternion and its conjugate
                // We can rotate a point using a quaternion with the equation "rotatedPoint = quaternion * point * quaternionConjugate"
                D3DXVECTOR3 rotatedPoint;
                D3DXQUATERNION qqtemp;

                D3DXQuaternionMultiply(&qqtemp, &tempJointOrientation, &tempWeightPos);
                D3DXQuaternionMultiply(&qqtemp, &qqtemp, &tempJointOrientationConjugate);

                rotatedPoint.x = qqtemp.x;
                rotatedPoint.y = qqtemp.y;
                rotatedPoint.z = qqtemp.z;

                // Now move the verices position from joint space (0,0,0) to the joints position in world space, taking the weights bias into account
                tempVert.x += ( tempJoint.pos.x + rotatedPoint.x ) * tempWeight.bias;
                tempVert.y += ( tempJoint.pos.y + rotatedPoint.y ) * tempWeight.bias;
                tempVert.z += ( tempJoint.pos.z + rotatedPoint.z ) * tempWeight.bias;

                // Compute the normals for this frames skeleton using the weight normals from before
                // We can comput the normals the same way we compute the vertices position, only we don't have to translate them (just rotate)
                D3DXQUATERNION tempWeightNormal = D3DXQUATERNION(tempWeight.normal.x, tempWeight.normal.y, tempWeight.normal.z, 0.0f);

                D3DXQuaternionMultiply(&qqtemp, &tempJointOrientation, &tempWeightNormal);
                D3DXQuaternionMultiply(&qqtemp, &qqtemp, &tempJointOrientationConjugate);

                // Rotate the normal
                rotatedPoint.x = qqtemp.x;
                rotatedPoint.y = qqtemp.y;
                rotatedPoint.z = qqtemp.z;  

                // Add to vertices normal and ake weight bias into account
                tempVert.nx -= rotatedPoint.x * tempWeight.bias;
                tempVert.ny -= rotatedPoint.y * tempWeight.bias;
                tempVert.nz -= rotatedPoint.z * tempWeight.bias;
            }

            // Store the vertices position in the position vector instead of straight into the vertex vector
            MD5Model.m_subsets[k].m_positions[i].x = tempVert.x;    
            MD5Model.m_subsets[k].m_positions[i].y = tempVert.y;    
            MD5Model.m_subsets[k].m_positions[i].z = tempVert.z;    

            // Store the vertices normal
            MD5Model.m_subsets[k].m_vertices[i].nx = tempVert.nx;   
            MD5Model.m_subsets[k].m_vertices[i].ny = tempVert.ny;   
            MD5Model.m_subsets[k].m_vertices[i].nz = tempVert.nz;   

            // Create the temp D3DXVECTOR3 for normalize
            D3DXVECTOR3 dtemp = D3DXVECTOR3(0,0,0);

            dtemp.x = MD5Model.m_subsets[k].m_vertices[i].nx;
            dtemp.y = MD5Model.m_subsets[k].m_vertices[i].ny;
            dtemp.z = MD5Model.m_subsets[k].m_vertices[i].nz;

            D3DXVec3Normalize(&dtemp, &dtemp);

            MD5Model.m_subsets[k].m_vertices[i].nx = dtemp.x;
            MD5Model.m_subsets[k].m_vertices[i].ny = dtemp.y;
            MD5Model.m_subsets[k].m_vertices[i].nz = dtemp.z;

            // Put the positions into the vertices for this subset
            MD5Model.m_subsets[k].m_vertices[i].x = MD5Model.m_subsets[k].m_positions[i].x;
            MD5Model.m_subsets[k].m_vertices[i].y = MD5Model.m_subsets[k].m_positions[i].y;
            MD5Model.m_subsets[k].m_vertices[i].z = MD5Model.m_subsets[k].m_positions[i].z;

        }

        // Update the subsets vertex buffer
        // First lock the buffer
        void* mappedVertBuff;

        bool result;

        result = MD5Model.m_subsets[k].vertBuff->Map(D3D10_MAP_WRITE_DISCARD, 0, &mappedVertBuff);
        if(FAILED(result))
        {
            return false;
        }

        // Copy the data into the vertex buffer.
        memcpy(mappedVertBuff, &MD5Model.m_subsets[k].m_vertices[0], (sizeof(Vertex) * MD5Model.m_subsets[k].numVertices));

        MD5Model.m_subsets[k].vertBuff->Unmap();
    }

    return true;
}

也许我可以在该代码中修复一些东西,但我想知道我是否做得对......

我还想知道是否还有其他更好的方法可以做到这一点,如果其他类型的动画会更好(与 .x 扩展名不同)。

感谢和抱歉我的英语不好:D


在着色器中进行骨骼转换会是一个好的解决方案吗?(像这样

4

1 回答 1

1

视锥体中的所有网格是否同时存在?如果不是,您应该只更新屏幕上您可以看到的对象的动画。如果您要更新场景中的所有网格,无论它们是否在视图中,您都会浪费很多周期。在我看来,您根本没有进行任何截锥体剔除,这可能是最好的起点。

于 2013-02-14T18:24:21.187 回答