当前位置:网站首页>QT and OpenGL: loading 3D models using the open asset import library (assimp) - Part 2

QT and OpenGL: loading 3D models using the open asset import library (assimp) - Part 2

2022-07-07 23:53:00 Raring_ Ringtail

Qt and OpenGL: Use Open Asset Import Library(ASSIMP) load 3D Model - The first 2 part

Translated from :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

TwitterLinkedInFacebookReddit

welcome back . This is based on Eric Stone The article “Qt and OpenGL: Use Open Asset Import Library(ASSIMP) load 3D Model ” Follow up article of . Last time we introduced how to use ASSIMP Load the model into our data structure , So that you can use OpenGL Rendering . This time, , We will show you how to write a program to actually render these models .

obviously , We will use Qt As a platform layer ( As the title of this blog says ), We will be in Kejiang OpenGL And Qt Choose from various methods of integration QOpenGLWindow. ( More about , See this webinar :http://www.ics.com/webinars/state-art-opengl-and-qt).

This is the list of our project skeleton :

#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();
}

Use this code , We can draw almost any geometry you can give us , So our task will become how to fill the empty createGeometry() Methods and empty draw() Method . Let's first solve the problem of creating geometry . If you remember, we ended our first blog post with the following code :

	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();

We said :“ Here we are , You have used OpenGL Display all the data required by the model .” Let's see how to turn it into code .

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();
    }

What we are doing is to use the ModelLoader object , Ask it to load a “ velociraptor” Model , Then the model is in QVector Object to provide us with model data . then , We get the model data , And create a separate Vertex-Buffer and Index-Buffer object , Then upload the model data to the graphics card . There? , We configure the attribute flow for vertex shader input , Because they point to these buffer objects , Then we are ready to render .

Now? , Let's do the actual rendering . Review the first article ,ModelLoader Object returns a grid data tree , Each mesh object contains a bunch of metadata about how to render it ( Including material color data ). What we need to do is create an algorithm , Traverse this “ Grid tree ”, Render the mesh in turn . Let's see how we do this in our code .

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());        
    }

As you can see , We have built an elegant recursive algorithm to traverse the grid tree . Please note that , In each recursive step , How does the current local transformation matrix become the parent transformation matrix of the next level . in addition , Notice how we update the lighting parameters with the lighting metadata of each mesh object , In order to render the mesh object correctly .

this is it , We are using Qt,OpenGL and ASSIMP Render Raptor !

Thanks to the contributing author :Eric Stone,ICS Software Engineer of .

 Velociraptor

About author

John Stone

John Stone

John Stone Is an experienced software developer , With a wide range of computer science education and entrepreneurial background , And in graphic visualization and virtual reality (VR) Have rich practical experience in . As the University of Texas at Austin VR Former technical director and researcher of the Laboratory , He spent many years honing his skills , use OpenGL Develop for scientific experiments and other projects based on VR Data acquisition system .

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


Welcome to my official account. Notes on Jiangda

原网站

版权声明
本文为[Raring_ Ringtail]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130554126053.html