Qt和OpenGL:使用Open Asset Import Library(ASSIMP)加载3D模型-第2部分

Qt和OpenGL:使用Open Asset Import Library(ASSIMP)加载3D模型-第2部分

翻译自:https://www.ics.com/blog/qt-and-opengl-loading-3d-model-open-asset-import-library-assimp-part-2

By John Stone Wednesday, May 18, 2016

Twitter LinkedIn Facebook Reddit

欢迎回来。这是我们基于Eric Stone的文章 “Qt和OpenGL:使用Open Asset Import Library(ASSIMP)加载3D模型”的后续文章。上一次我们介绍了如何使用ASSIMP将模型加载到我们的数据结构中,以便可以使用OpenGL进行渲染。这次,我们将介绍如何编写程序来实际渲染这些模型。

显然,我们将使用Qt作为平台层(就像此博客的标题中写的那样),我们将在可将OpenGL与Qt集成的各种方法中选择QOpenGLWindow。 (有关的详细信息,请参见此网络研讨会:http://www.ics.com/webinars/state-art-opengl-and-qt)。

这是我们项目骨架的清单:

#include <QtGui/QGuiApplication>
#include <QtGui/QKeyEvent>

#include <QtGui/QOpenGLWindow>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLVertexArrayObject>

#include "modelloader.h"

static QString vertexShader =
        "#version 330 core\n"
        "\n"
        "layout(location = 0) in vec3 vertexPosition;\n"
        "layout(location = 1) in vec3 vertexNormal;\n"
        "\n"
        "out vec3 normal;\n"
        "out vec3 position;\n"
        "\n"
        "uniform mat4 MV;\n"
        "uniform mat3 N;\n"
        "uniform mat4 MVP;\n"
        " \n"
        "void main()\n"
        "{\n"
        "    normal = normalize( N * vertexNormal );\n"
        "    position = vec3( MV * vec4( vertexPosition, 1.0 ) );\n"
        "    gl_Position = MVP * vec4( vertexPosition, 1.0 );\n"
        "}\n"
        ;

static QString fragmentShader =
        "#version 330 core\n"
        "\n"
        "in vec3 normal;\n"
        "in vec3 position;\n"
        "\n"
        "layout (location = 0) out vec4 fragColor;\n"
        "\n"
        "struct Light\n"
        "{\n"
        "    vec4 position;\n"
        "    vec3 intensity;\n"
        "};\n"
        "uniform Light light;\n"
        "\n"
        "struct Material {\n"
        "    vec3 Ka;\n"
        "    vec3 Kd;\n"
        "    vec3 Ks;\n"
        "    float shininess;\n"
        "};\n"
        "uniform Material material;\n"
        "\n"
        "void main()\n"
        "{\n"
        "    vec3 n = normalize( normal);\n"
        "    vec3 s = normalize( light.position.xyz - position);\n"
        "    vec3 v = normalize( -position.xyz);\n"
        "    vec3 h = normalize( v + s);\n"
        "    float sdn = dot( s, n);\n"
        "    vec3 ambient = material.Ka;\n"
        "    vec3 diffuse = material.Kd * max( sdn, 0.0);\n"
        "    vec3 specular = material.Ks * mix( 0.0, pow( dot(h, n), material.shininess), step( 0.0, sdn));\n"
        "    fragColor = vec4(light.intensity * (ambient + diffuse + specular), 1);\n"
        "}\n"
        ;

struct Window : QOpenGLWindow, QOpenGLFunctions
{
    Window() :
        m_vbo(QOpenGLBuffer::VertexBuffer),
        m_nbo(QOpenGLBuffer::VertexBuffer),
        m_ibo(QOpenGLBuffer::IndexBuffer)
    {
    }

    void createShaderProgram()
    {
        if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Vertex, vertexShader)) {
            qDebug() << "Error in vertex shader:" << m_pgm.log();
            exit(1);
        }
        if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Fragment, fragmentShader)) {
            qDebug() << "Error in fragment shader:" << m_pgm.log();
            exit(1);
        }
        if ( !m_pgm.link() ) {
            qDebug() << "Error linking shader program:" << m_pgm.log();
            exit(1);
        }
    }

    void createGeometry()
    {
    }

    void initializeGL()
    {
        QOpenGLFunctions::initializeOpenGLFunctions();
        createShaderProgram(); m_pgm.bind();

        m_pgm.setUniformValue("light.position",   QVector4D( -1.0f,  1.0f, 1.0f, 1.0f ));
        m_pgm.setUniformValue("light.intensity",  QVector3D(  1.0f,  1.0f, 1.0f  ));

        createGeometry();
        m_view.setToIdentity();
        m_view.lookAt(QVector3D(0.0f, 0.0f, 1.2f),    // Camera Position
                      QVector3D(0.0f, 0.0f, 0.0f),    // Point camera looks towards
                      QVector3D(0.0f, 1.0f, 0.0f));   // Up vector

        glEnable(GL_DEPTH_TEST);

        glClearColor(.9f, .9f, .93f ,1.0f);
    }

    void resizeGL(int w, int h)
    {
        glViewport(0, 0, w, h);
        m_projection.setToIdentity();
        m_projection.perspective(60.0f, (float)w/h, .3f, 1000);
        update();
    }

    void draw()
    {
    }

    void paintGL()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        m_pgm.bind();
        m_vao.bind();
        draw();
        m_vao.release();

        update();
    }

    void keyPressEvent(QKeyEvent * ev)
    {
        if (ev->key() == Qt::Key_Escape) exit(0);
    }
 
    ModelLoader              m_loader;
    QMatrix4x4 m_projection, m_view;
    QOpenGLShaderProgram     m_pgm;
    QOpenGLVertexArrayObject m_vao;
    QOpenGLBuffer            m_vbo, m_nbo;
    QOpenGLBuffer            m_ibo;
    GLsizei                  m_cnt;
};

int main(int argc, char *argv[])
{
    QGuiApplication a(argc,argv);
    QSurfaceFormat f;
    f.setMajorVersion( 3 );
    f.setMinorVersion( 3 );
    f.setProfile( QSurfaceFormat::CoreProfile );
    Window w;
    w.setFormat(f);
    w.setWidth(800); w.setHeight(600);
    w.show();
    return a.exec();
}

使用此代码,我们几乎可以绘制您可以给我们的任何几何图形,因此我们的任务将变成如何填充空的createGeometry() 方法和空的 draw() 方法。让我们首先解决创建几何图形的问题。如果您还记得我们以以下代码结束了我们的第一篇博客文章:

	ModelLoader model;

    if (!model.Load("head.3ds"))
    {
        m_error = true;
        return;
    }

    QVector<float> *vertices;
    QVector<float> *normals;
    QVector<unsigned int=""> *indices;

    model.getBufferData(&vertices, &normals, &indices);

    m_rootNode = model.getNodeData();

我们说:“到此,您已经拥有了使用OpenGL显示模型所需的所有数据。”让我们看看如何将其转换为代码。

void createGeometry()
    {
        if(!m_loader.Load("velociraptor_mesh_materials.dae", ModelLoader::RelativePath)) {
            qDebug() << "ModelLoader failed to load model" << m_pgm.log();
            exit(1);
        }

        // Get the loaded model data from the model-loader: (v)ertices, (n)ormals, and (i)ndices
        QVector<glfloat> *v, *n; QVector<gluint> *i; m_loader.getBufferData(&v, &n, &i);

        // Initialize and bind the VAO that's going to capture all this vertex state
        m_vao.create();
        m_vao.bind();

        // Put all the vertex data in a FBO
        m_vbo.create();
        m_vbo.setUsagePattern( QOpenGLBuffer::StaticDraw );
        m_vbo.bind();
        m_vbo.allocate(&(*v)[0], v->size() * sizeof((*v)[0]));

        // Configure the attribute stream
        m_pgm.enableAttributeArray(0);
        m_pgm.setAttributeBuffer(0, GL_FLOAT, 0, 3);

        // Put all the normal data in a FBO
        m_nbo.create();
        m_nbo.setUsagePattern( QOpenGLBuffer::StaticDraw );
        m_nbo.bind();
        m_nbo.allocate(&(*n)[0], n->size() * sizeof((*n)[0]));

        // Configure the attribute stream
        m_pgm.enableAttributeArray(1);
        m_pgm.setAttributeBuffer(1, GL_FLOAT, 0, 3);

        // Put all the index data in a IBO
        m_ibo.create();
        m_ibo.setUsagePattern( QOpenGLBuffer::StaticDraw );
        m_ibo.bind();
        m_ibo.allocate(&(*i)[0], i->size() * sizeof((*i)[0]));

        // Okay, we've finished setting up the vao
        m_vao.release();
    }

我们正在做的是使用上一篇博客文章中的ModelLoader对象,要求它加载一个“ velociraptor”模型,然后该模型在QVector 对象中为我们提供模型数据。然后,我们获取模型数据,并在显卡上创建单独的Vertex-Buffer和Index-Buffer对象,然后将模型数据上传到显卡。在那里,我们为顶点着色器输入配置属性流,因为它们指向这些缓冲区对象,之后我们准备进行渲染。

现在,让我们进行实际的渲染。回顾第一篇文章,ModelLoader对象返回一棵网格数据树,每个网格对象包含一堆有关如何渲染它的元数据(包括材质颜色数据)。我们要做的是创建一个算法,遍历此“网格树”,依次渲染网格。让我们看看我们如何在代码中做到这一点。

void drawNode(const QMatrix4x4& model, const Node *node, QMatrix4x4 parent)
    {
        // Prepare matrices
        QMatrix4x4 local = parent * node->transformation;
        QMatrix4x4 mv = m_view * model * local;

        m_pgm.setUniformValue("MV",  mv);
        m_pgm.setUniformValue("N",   mv.normalMatrix());
        m_pgm.setUniformValue("MVP", m_projection * mv);

        // Draw each mesh in this node
        for(int i = 0; i<node->meshes.size(); ++i)
        {
            const Mesh& m = *node->meshes[i];

            if (m.material->Name == QString("DefaultMaterial")) {
                m_pgm.setUniformValue("material.Ka",        QVector3D(  0.05f, 0.2f, 0.05f ));
                m_pgm.setUniformValue("material.Kd",        QVector3D(  0.3f,  0.5f, 0.3f  ));
                m_pgm.setUniformValue("material.Ks",        QVector3D(  0.6f,  0.6f, 0.6f  ));
                m_pgm.setUniformValue("material.shininess", 50.f);
            } else {
                m_pgm.setUniformValue("material.Ka",        m.material->Ambient);
                m_pgm.setUniformValue("material.Kd",        m.material->Diffuse);
                m_pgm.setUniformValue("material.Ks",        m.material->Specular);
                m_pgm.setUniformValue("material.shininess", m.material->Shininess);
            }

            glDrawElements(GL_TRIANGLES, m.indexCount, GL_UNSIGNED_INT, (const GLvoid*)(m.indexOffset * sizeof(GLuint)));
        }

        // Recursively draw this nodes children nodes
        for(int i = 0; i < node->nodes.size(); ++i)
            drawNode(model, &node->nodes[i], local);
    }

    void draw()
    {
        QMatrix4x4 model;
        model.translate(-0.2f, 0.0f, .5f);
        model.rotate(55.0f, 0.0f, 1.0f, 0.0f);

        drawNode(model, m_loader.getNodeData().data(), QMatrix4x4());        
    }

如您所见,我们已经建立了一个优雅的递归算法来遍历网格树。请注意,在每个递归步骤中,当前的本地转换矩阵是如何成为下一级别的父级转换矩阵的。另外,请注意我们是如何使用每个网格物体的照明元数据更新照明参数,以便正确渲染该网格物体的。

就是这样,我们正在使用Qt,OpenGL和ASSIMP渲染迅猛龙!

感谢特约作者:Eric Stone,ICS的软件工程师。

迅猛龙

关于作者

John Stone

John Stone

John Stone是一位经验丰富的软件开发人员,具有广泛的计算机科学教育和创业背景,并在图形可视化和虚拟现实(VR)方面具有丰富的实践经验。作为德克萨斯大学奥斯汀分校VR实验室的前技术总监和研究员,他花了很多年的时间磨练自己的技能,用OpenGL为科学实验和其他项目开发基于VR的数据采集系统。

原文:https://www.ics.com/blog/qt-and-opengl-loading-3d-model-open-asset-import-library-assimp-part-2


欢迎关注我的公众号 江达小记

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
assimp-vc142-mt.dll是一个动态链接库文件,它是由ASSIMPOpen Asset Import Library)项目生成的。ASSIMP是一个开源的跨平台的模型导入库,可以用于导入各种3D模型文件格式,包括但不限于OBJ、FBX、STL、3DS等。assimp-vc142-mt.dll是该库的一个特定版本的动态链接库文件。 assimp-vc142-mt.dll的文件名中的"vc142"表示该库是与Visual Studio 2019的C++编译器(版本号为14.2)兼容的版本。"mt"表示该库是使用多线程(static multithreaded)模式进行编译的,因此在使用该库时需要注意线程安全性。 如果你在开发过程中遇到assimp-vc142-mt.dll文件缺失的错误,可能是由于该动态链接库文件不存在于你的系统路径中或者文件损坏导致的。解决这个问题的方法是确保assimp-vc142-mt.dll文件存在于你的系统路径中,并且是有效可用的版本。你可以从ASSIMP项目的官方网站或者其他可信的来源下载该文件,然后将它放置在系统路径(如Windows的System32文件夹)或者你的应用程序的工作目录中。 需要注意的是,在使用动态链接库文件时,你的操作系统必须与该文件对应的位数匹配(如32位或64位)。另外,为了正确使用assimp-vc142-mt.dll文件,你可能还需要安装相关的依赖项,如Visual Studio运行库。 总之,assimp-vc142-mt.dll是ASSIMP项目的一个特定版本的动态链接库文件,用于导入各种3D模型文件格式。如果你的应用程序缺少该文件,可以尝试下载并正确配置该文件来解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值