c++ - 如何使用Assimp获取模型动画?

标签 c++ animation assimp

目前,我正在尝试使用C++和OpenGL制作游戏引擎,并希望使3D动画正常工作。建议我使用Assimp,并能够找到一个使静态模型起作用的教程,但是我不知道从哪里开始动画。我一直在尝试使用Google,但找不到任何有效的方法。如何修改代码以获取动画?建议使用哪种文件格式?

这是我目前拥有的代码:

//Mesh.h    
#include <string>

#include "glut\include\GL\glew.h"
#include "glut\include\GL\glut.h"

#include <assimp/Importer.hpp>      // C++ importer interface
#include <assimp/scene.h>           // Output data structure
#include <assimp/postprocess.h>     // Post processing fla

//textures
#include <SOIL.h>

class Mesh
{
public:
    Mesh(void);
    Mesh(std::string filename, std::string textureFilename, float x, float y, float z, float width, float height, float depth, float rotX, float rotY, float rotZ);
    ~Mesh(void);

    void Init(std::string filename);
    void LoadTexture(std::string textureName);
    void Draw();

private:
    GLfloat *vertexArray;
    GLfloat *normalArray;
    GLfloat *uvArray;

    GLint numVerts;

    GLuint m_Texture[1];

    float m_CenterX, m_CenterY, m_CenterZ, m_Width, m_Height, m_Depth;
    float m_XRotation, m_YRotation, m_ZRotation;
};

//Mesh.cpp
#include "Mesh.h"

Mesh::Mesh(void)
{
}

Mesh::Mesh(std::string filename, std::string textureFilename, float x, float y, float z, float width, float height, float depth, float rotX, float rotY, float rotZ)
{
    //fills in variables
    Init(filename);
    LoadTexture(textureFilename);
}

Mesh::~Mesh(void)
{

}

void Mesh::Init(std::string filename)
{
    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(filename,aiProcessPreset_TargetRealtime_Fast);//aiProcessPreset_TargetRealtime_Fast has the configs you'll need

    aiMesh *mesh = scene->mMeshes[0]; //assuming you only want the first mesh

    numVerts = mesh->mNumFaces*3;

    vertexArray = new float[mesh->mNumFaces*3*3];
    normalArray = new float[mesh->mNumFaces*3*3];
    uvArray = new float[mesh->mNumFaces*3*2];

    for(unsigned int i=0;i<mesh->mNumFaces;i++)
    {
        const aiFace& face = mesh->mFaces[i];

        for(int j=0;j<3;j++)
        {
            aiVector3D uv = mesh->mTextureCoords[0][face.mIndices[j]];
            memcpy(uvArray,&uv,sizeof(float)*2);
            uvArray+=2;

            aiVector3D normal = mesh->mNormals[face.mIndices[j]];
            memcpy(normalArray,&normal,sizeof(float)*3);
            normalArray+=3;

            aiVector3D pos = mesh->mVertices[face.mIndices[j]];
            memcpy(vertexArray,&pos,sizeof(float)*3);
            vertexArray+=3;
        }
    }

    uvArray-=mesh->mNumFaces*3*2;
    normalArray-=mesh->mNumFaces*3*3;
    vertexArray-=mesh->mNumFaces*3*3;
}

void Mesh::LoadTexture(std::string textureName)         
{
    glGenTextures(1, &m_Texture[0]);
    glBindTexture(GL_TEXTURE_2D, m_Texture[0]);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  // NOTE the GL_NEAREST Here! 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  // NOTE the GL_NEAREST Here! 

    m_Texture[0] = SOIL_load_OGL_texture // load an image file directly as a new OpenGL texture
    (
        textureName.c_str(),
        SOIL_LOAD_AUTO,
        SOIL_CREATE_NEW_ID,
        SOIL_FLAG_MIPMAPS | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT
    );
}

void Mesh::Draw()
{
    glPushMatrix();
        glTranslatef(m_CenterX, m_CenterY, m_CenterZ);

        glRotatef(m_XRotation, 1, 0, 0);
        glRotatef(m_YRotation, 0, 1, 0);
        glRotatef(m_ZRotation, 0, 0, 1);

        glScalef(m_Width, m_Height, m_Depth);

        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnableClientState(GL_VERTEX_ARRAY);

            glNormalPointer(GL_FLOAT,0,normalArray);
            glTexCoordPointer(2,GL_FLOAT,0,uvArray);
            glVertexPointer(3,GL_FLOAT,0,vertexArray);

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, m_Texture[0]);
            glDrawArrays(GL_TRIANGLES,0,numVerts);

        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
    glPopMatrix();
}

最佳答案

这是一个古老的问题,但是我相信它可能会在将来被其他人使用,因此我将尝试概述使用Assimp库进行动画处理时可以使用的一些选项。

首先,我想提一提,您可以在没有Assimp库的情况下进行动画处理。该库只是为您提供了一种加载模型的好方法,但是您发现它不会为您做动画。

从概念上讲,无论您是否使用Assimp,动画都将非常相似。例如;如果您编写了自己的模型加载器,则可以轻松地使用它而不是Assimp并仍然可以进行动画处理。但是,由于制作动画的方法不止一种,因此实现动画的方式可能会受到更多限制,因为在没有Assimp的情况下制作骨骼动画会涉及编写模型加载器,该加载器可以从中获取骨骼变换,权重和各种数据模型文件,这可能需要一段时间。

有多种制作动画的方法。无论是在技术方面,还是您是否想通过硬件加速(在GPU或CPU上)来做到这一点。我要提到的是这里的一些选项,因为大多数人使用Assimp来做骨骼动画,如果您的数学技能不强,只是想要一些易于组合的东西,这可能会令人生畏。

通常,有三种接受的动画制作方法:

  • 关键帧
  • 带有插值的
  • Keframe
  • 骨骼动画(硬件蒙皮)

  • 关键帧

    关键帧动画是当您为动画的每个帧创建单独的模型时,类似于2D Sprite 表。您可以连续渲染模型以产生动画。由于您需要为每个动画加载多个模型,因此该方法可能是最简单但最幼稚的实现。这些框架之间的过渡可能会很明显,具体取决于您生成的框架数量,并且在看起来可接受之前,您可能需要导出几个模型文件。
    这种方法的另一个缺点是您可能需要生成自己的模型。

    带插值的关键帧

    此方法与上述方法类似,但是不是将每个关键帧生成为单独的模型,而是仅生成几个关键帧,并使用内插法由引擎生成“缺失”模型。之所以可以这样做是因为,如果我们知道顶点的起点和终点,就可以进行插值以找出在时间= t处顶点应位于的位置。

    本教程在解释如何进行关键帧动画方面做得非常出色:

    https://www.khronos.org/opengl/wiki/Keyframe_Animation

    同样,它没有讨论Assimp,但是概念是相同的,您仍然可以使用Assimp加载模型。
    这种动画形式实现起来非常简单,对初学者来说非常好。但是,它确实有一些缺点。
    如果您选择这种方式,则可能会受到内存的限制,因为此方法可能会占用VBO的大量内存,这取决于模型的详细程度。
    如果选择创建自己的模型,则还需要保留模型文件中的顶点顺序,以便在一个模型文件(关键帧1)的顶点2与另一个模型文件(关键帧2)的顶点2之间进行插值正确的。

    骨骼动画

    这可能是制作动画最困难的方法,但是它处理了方法1和2中的许多问题。您还会发现,通过执行骨骼动画,您可以加载许多较新的文件格式。用于指定骨骼转换和旋转的文件,而不必为每个关键帧加载新文件。

    这是我认为拥有Assimp会带来极大好处的一种情况。 Assimp非常有能力处理从模型文件中获取所需数据以进行骨骼动画处理。

    如果您对制作骨骼动画感兴趣,那么本教程将是一个绝佳的选择,甚至还可以使用Assimp。

    http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html

    我本人使用本教程在自己的引擎中实现了骨骼动画。因此,如果您决定走这条路,我强烈建议您阅读此内容。

    我要提到的最后一件事,是我发现有些人感到困惑,那就是可以使用硬件加速来完成动画,但这并不是绝对必要的。

    在我提供的以前的教程链接中,这两个都是使用硬件加速进行动画处理的。在这种情况下,这意味着顶点计算是在顶点着色器主体中的GPU上完成的。

    但是我知道很多人可能不熟悉现代OpenGL,在这种情况下,您仍然可以在CPU上进行这些相同的计算。这里的想法是查看顶点着色器中发生的事情,并创建一个为您执行这些计算的函数。

    您还询问了动画的文件格式;这将取决于您执行动画的路线。如果要对.fbx和.md5等格式进行动画处理,则可能正在做骨骼动画处理。如果您选择关键帧动画,我可能会坚持使用.obj,这是我发现最易于使用的格式,因为格式说明非常容易理解。

    在引擎中调试动画时,请确保您拥有一个已知有效的文件。另一个陷阱是,在互联网上下载免费模型可以包含任何旧格式,绝对纹理路径和不同的坐标系(Y向上或Z向上)等。

    关于c++ - 如何使用Assimp获取模型动画?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28595852/

    相关文章:

    jQuery - 没有动画的 ScrollTop

    javascript - 防止动画在初始页面加载时触发

    utf-8 - Assimp 找不到 utf8.h header

    c++ - Assimp 错误地导入了 OBJ 索引?

    c++ - 使用运算符 << 的日志记录功能

    c++ - 我重载了运算符 > 但它仍然说没有运算符匹配操作数

    c++ - OpenCv 3d拼接全景图

    C++:在构造函数中初始化变量的位置

    ios - 仅在从urls数组加载所有图像之后才激事件画

    algorithm - 使用 Assimp 和 OpenMesh 简化网格