Two methods to load stl 3D model using OpenGL library in Qt

OpenGL (Open Graphics Library) is a cross-language and cross-platform application program interface for rendering 2D and 3D vector graphics. The implementation of OpenGL utilizes graphics acceleration hardware, and these implementations are generally provided by display device manufacturers. However, in actual use, a third-party library based on gl is generally used to determine whether the current hardware supports related extensions during the runtime of the program to prevent program crashes or even hardware damage. At present, the third-party libraries I know about include glad, glew, glfw, freeglut, etc. The following figure reflects the relationship between them.

In addition, after installing Qt, Qt itself will also encapsulate the gl library to a certain extent, namely qopengl.h and QOpenGLFunctions. Qt is currently a very good tool for UI. Combined with the examples it gives, such as cube, hellogl2, etc., it can imitate many better human-computer interaction interfaces.

For three-dimensional models, commonly used software includes solid work, AutoCAD, etc., which can be used to draw and export stl model files. There are 2 types of STL files: text files (ASCII format) and binary files (BINARY). Among them, the text format can be opened with notepad++, which contains the definition of multiple triangles. The definition of each triangle includes the three-dimensional coordinates of each fixed point of the triangle and the normal vector of the triangle. The order of the triangle vertices follows the right-hand rule. Therefore, for rendering the stl file with opengl, we have to load it first, and https://free3d.com can also download many model files.

1. Use Qt's own library

Using Qt's own library, the window can inherit public QOpenGLWidget, protected QOpenGLFunctions (a set of OpenGL ES2.0 API is provided, eliminating the need for developers to manually parse these function symbols). However, I am still a little confused about the writing of OpenGL ES, and I haven't seen a more systematic introduction. Generally, the examples given are using ready-made templates. I use the method in the Qt example here. There are too many ways to load stl text files on the Internet.

bool QObjLoad::load(QString fileName, QVector<float>& vPoints)
{
    if (fileName.mid(fileName.lastIndexOf('.')) != ".obj" && fileName.mid(fileName.lastIndexOf('.')) != ".OBJ")
    {
        qDebug() << "file is not a obj file.";
        return false;
    }

    QFile objFile(fileName);
    if (!objFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "open" << fileName << "failed";
        return false;
    }
    else
    {
        qDebug() << "open" << fileName << "success!";
    }

    QVector<float> vertextPoints, texturePoints, normalPoints;
    QVector<int> facesIndexs;
    while (!objFile.atEnd())
    {
        QByteArray lineData = objFile.readLine();

        QList<QByteArray> strValues = lineData.trimmed().split(' ');
        QString dataType = strValues.takeFirst();
        if (dataType == "v")
        {
            std::transform(strValues.begin(), strValues.end(), std::back_inserter(vertextPoints), [](QByteArray& str) {
                return str.toFloat();
            });
        }
        else if (dataType == "vt")
        {
            std::transform(strValues.begin(), strValues.end(), std::back_inserter(texturePoints), [](QByteArray& str) {
                return str.toFloat();
            });
        }
        else if (dataType == "vn")
        {
            std::transform(strValues.begin(), strValues.end(), std::back_inserter(normalPoints), [](QByteArray& str) {
                return str.toFloat();
            });
        }
        else if (dataType == "f")
        {
            facesIndexs << strValues.at(0).toInt() << strValues.at(1).toInt() << strValues.at(2).toInt();
        }
    }
    objFile.close();

    for (auto& verFaceInfo : facesIndexs)
    {
        int vIndex = verFaceInfo - 1;

        vPoints << vertextPoints.at(vIndex * 3);
        vPoints << vertextPoints.at(vIndex * 3 + 1);
        vPoints << vertextPoints.at(vIndex * 3 + 2);
    }

    vertextPoints.clear();
    texturePoints.clear();
    normalPoints.clear();
    facesIndexs.clear();

    return true;
}

The main purpose here is to obtain the vertex coordinates, and then it is displayed on the Qt window. Three functions need to be re-implemented: void initializeGL() override, void paintGL() override and void resizeGL(int width, int height) override.

void GLWindow::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0, 0, 0, 0);

    m_program = new QOpenGLShaderProgram;
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceCore);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceCore);
    if (m_program->link())
    {
        qDebug() << "link success!";
    }
    else
    {
        qDebug() << "link failed";
    }
    m_program->bindAttributeLocation("vertex", 0);
    m_program->bindAttributeLocation("normal", 1);
    m_program->link();
    m_program->bind();
    m_projMatrixLoc = m_program->uniformLocation("projMatrix");
    m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
    m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
    m_lightPosLoc = m_program->uniformLocation("lightPos");

    // Setup our vertex buffer object.
    m_Vbo.create();
    m_Vbo.bind();
    m_Vbo.allocate(m_vPoints.data(), m_vPoints.size() * sizeof(float));

    // Store the vertex attribute bindings for the program.
    setupVertexAttribs();

    // Light position is fixed.
    m_program->setUniformValue(m_lightPosLoc, QVector3D(10, 10, 10));

    m_program->release();
}

void GLWindow::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    m_world.setToIdentity();
    m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
    m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
    m_world.rotate(m_zRot / 16.0f, 0, 0, 1);

    m_camera.setToIdentity();
    m_camera.lookAt(m_camera_pos, QVector3D(0, 0, 0), QVector3D(1, 0, 0));

    m_program->bind();
    m_program->setUniformValue(m_projMatrixLoc, m_proj);
    m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
    QMatrix3x3 normalMatrix = m_world.normalMatrix();
    m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);

    glDrawArrays(GL_TRIANGLES, 0, m_vPoints.size()/3);

    m_program->release();
}

void GLWindow::resizeGL(int w, int h)
{
    m_proj.setToIdentity();
    m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}

Finally, set the viewing angle through lookAt to display. I adjusted the viewing angle so that it can be viewed from different perspectives. The effect is as follows.

 

2. Through the combination of freeglut and Qt

Freeglut is an open source alternative to GLUT. There are many functions in it, and most people are familiar with it, so this may be more popular. Freeglut can be downloaded from http://freeglut.sourceforge.net . After downloading, the vs project is generated by cmake, and then freeglut.lib and freeglut.dll libraries can be used. At the same time, the header files in the GL directory need to be copied to the corresponding Under contents.

Still the same, load the stl file and display it. However, this method is very troublesome for the processing of mouse or keyboard operations. And I don’t know why, the colors and angles I displayed are a bit strange. There is still a lot to be clarified about the position of the light, the angle of view, the rendering, and various matrix transformations.

typedef struct Vertex
{
    //定义三维图形的
    //用于face结构体中
    float x, y, z;
} Vertex;

typedef struct Face
{
    //多边形(三角形)面的结构体
    Face(void) : vert_number(0), verts(0) {};
    int vert_number;        //记录顶点的个数
    Vertex** verts;          //这是一个面的所有 顶点数组(含有坐标)
    float normal[3];         //记录点的法向量,分别是x,y,z三个方向
    //注意点的法向量通过顶点的信息计算得到!
    //对于obj模型如果我们已经得到了法线的信息
    //那么就直接拿来用就好!
} Face;

typedef struct myMesh
{
    //自定义mesh的结构体
    myMesh(void) : vert_number(0), verts(0), face_number(0), faces(0) {};
    //自定义构造器
    int vert_number;        //总的顶点个数
    Vertex* verts;          //定点数组
    int face_number;        //面的数目
    Face* faces;
    vector<Vertex>point;
} myMesh;

void GLWin::drawObj()
{
    if (m_mesh->face_number == 0)
        return;
    qDebug() << m_mesh->face_number;

    float bbox[2][3] = { { 1.0E30F, 1.0E30F, 1.0E30F }, { -1.0E30F, -1.0E30F, -1.0E30F } };

    for (int i = 0; i < m_mesh->vert_number; i++)
    {
        Vertex& vert = m_mesh->verts[i];
        if (vert.x < bbox[0][0])
            bbox[0][0] = vert.x;
        else if (vert.x > bbox[1][0])
            bbox[1][0] = vert.x;

        if (vert.y < bbox[0][1])
            bbox[0][1] = vert.y;
        else if (vert.y > bbox[1][1])
            bbox[1][1] = vert.y;

        if (vert.z < bbox[0][2])
            bbox[0][2] = vert.z;
        else if (vert.z > bbox[1][2])
            bbox[1][2] = vert.z;
    }

    // Setup initial viewing scale
    float dx = bbox[1][0] - bbox[0][0];
    float dy = bbox[1][1] - bbox[0][1];
    float dz = bbox[1][2] - bbox[0][2];
    scale = 2.0 / sqrt(dx * dx + dy * dy + dz * dz);


    glPushMatrix();
    glColor3f(0.5, 0.5, 0.5);
    glScalef(scale, scale, scale);

    glRotatef(rotation[0], 1.0, 0.0, 0.0);
    glRotatef(rotation[1], 0.0, 1.0, 0.0);
    glRotatef(rotation[2], 0.0, 0.0, 1.0);

    for (int i = 0; i < m_mesh->face_number; i++)
    {
        Face& face = m_mesh->faces[i];
        glBegin(GL_TRIANGLES);
        glNormal3fv(face.normal);
        for (int j = 0; j < face.vert_number; j++)
        {
            Vertex* vert = face.verts[j];
            glVertex3f(vert->x, vert->y, vert->z);
        }
        glEnd();
    }

    glPopMatrix();
}

void GLWin::initializeGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glEnable(GL_NORMALIZE);
    glEnable(GL_DEPTH_TEST);
    glutInitWindowSize(600, 600);
}

void GLWin::paintGL()
{
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glLightfv(GL_LIGHT0, GL_POSITION, light0_position);

    gluLookAt(1, 1, 1, 0, 0, 0, 0, 1, 0);

    //draw axis
    glPushMatrix();
    glLineWidth(5);
    glBegin(GL_LINES);
    glColor3f(1, 0, 0);
    glVertex3f(0.0f, 0, 0);
    glVertex3f(10, 0, 0);
    glEnd();

    glBegin(GL_LINES);
    glColor3f(0, 1, 0);
    glVertex3f(0.0f, 0, 0);
    glVertex3f(0, 10, 0);
    glEnd();

    glBegin(GL_LINES);
    glColor3f(0, 0, 1);
    glVertex3f(0, 0, 0);
    glVertex3f(0, 0, 10);
    glEnd();
    glPopMatrix();

    CLoadStlObj stlobj;
    m_mesh = stlobj.ReaderOBj("head.obj");
    drawObj();

    // 设置光照信息
    static GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    static GLfloat light0_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    设置满散射
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glEnable(GL_LIGHT0);
    static GLfloat light1_diffuse[] = { 0.5, 0.5, 0.5, 1.0 };
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHTING);
}

void GLWin::resizeGL(int width, int height)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (GLfloat)width / (GLfloat)height, 0.1, 100.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

 

The engineering code of Method 1 can be found in https://github.com/WelinLee/QtOpenGLDemo , enjoy!

 

 

Guess you like

Origin blog.csdn.net/u014610460/article/details/111949545