两种方法在Qt中使用OpenGL库加载stl三维模型

OpenGL(Open Graphics Library)是一种用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序接口。OpenGL的实现利用了图形加速硬件,这些实现一般由显示设备厂商提供。但真正使用时,一般采用基于gl的第三方库,用于在程序的运行期判断当前硬件是否支持相关的扩展,防止程序崩溃甚至造成硬件损坏。目前我了解到的第三方库就有glad、glew、glfw、freeglut等,下图反映了它们之间的关系。

此外在安装Qt后,Qt本身也会对gl库进行一定的封装,即qopengl.h和QOpenGLFunctions。Qt是目前做UI非常好的一款工具,再结合它给的例子,比如cube、hellogl2等,可以仿照着做出很多比较好的人机交互界面。

对于三维模型,常用的软件有solid work,AutoCAD等,可以采用这些软件画出后导出stl模型文件。 STL 文件有2 种类型:文本文件(ASCII格式)和二进制文件(BINARY)。其中文本格式的可以用notepad++打开,它里面包含多个三角形面片的定义组成,每个三角形的定义包括三角形各个定点的三维坐标及三角形面片的法矢量,三角形顶点的排列顺序遵循右手法则。因此对于用opengl渲染stl文件,我们首先得加载它,https://free3d.com也可下到很多模型文件。

1. 使用Qt自带的库

使用Qt自带的库,窗口可继承public QOpenGLWidget, protected QOpenGLFunctions(提供了一套OpenGL ES2.0 API,免去开发人员手动解析这些函数符号)。不过关于OpenGL ES的写法,我还是有点迷糊,没有看到比较系统性的介绍,一般给的例子都是采用现成的模板,我这里采用的是Qt例子中的方法。关于stl文本文件的加载,网上就有太多方法。

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

这里主要是为了获取顶点坐标,接着就是在Qt的窗口上显示了,要重新实现三个函数void initializeGL() override、void paintGL() override和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);
}

最后通过lookAt设置视角去显示。我把视角进行了可调,这样就能从不同视角来查看,效果如下。

 

2. 通过freeglut和Qt的结合

freeglut是GLUT的开源替代库,里面很多函数,而且大多数人比较熟悉,所以用这个可能更受青睐。freeglut可以在http://freeglut.sourceforge.net下到,下载后通过cmake生成vs工程,然后就可以使用freeglut.lib和freeglut.dll库,同时需将GL目录下的头文件也拷到相应的目录下。

还是一样的,加载stl文件并显示。不过这种方法对于鼠标或者键盘的操作等的处理就非常麻烦。而且不知道为啥,我显示的颜色和角度有点奇怪。关于灯光位置,视角,渲染,各种矩阵变换,还是有很多有待弄清楚的。

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

 

方法1的工程代码可以在https://github.com/WelinLee/QtOpenGLDemo中找到,enjoy!

 

 

  • 8
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
### 回答1: 使用MFC和OpenGL来读取并显示STL三维模型需要以下几个步骤: 1. 创建一个新的MFC项目,选择对话框应用程序作为模板。 2. 将OpenGL文件添加到项目,并进行相关配置,以便在对话框使用OpenGL绘图。 3. 在对话框添加一个静态文本框和一个自定义的OpenGL绘图区域。 4. 创建一个自定义的OpenGL绘图类,用于在绘图区域显示STL模型。在该类,定义一个透视投影矩阵、模型矩阵和视图矩阵,通过OpenGL函数来加载和渲染STL文件的三角形。 5. 在对话框类,添加一个自定义的OpenGL绘图类的成员变量,并在OnInitDialog()函数对其进行初始化。 6. 在OnInitDialog()函数,调用自定义OpenGL绘图类的初始化函数,传入STL模型文件的路径,将模型加载到内存。 7. 在OnPaint()函数,调用自定义OpenGL绘图类的绘制函数,将模型渲染到OpenGL绘图区域。 8. 处理对话框的事件,例如按钮点击事件,调用自定义OpenGL绘图类的相应函数进行操作,如旋转、平移等。 9. 在应用程序类的InitInstance()函数,创建对话框类的对象,并显示对话框。 通过以上步骤,可以实现在MFC应用程序读取并显示STL三维模型。 ### 回答2: MFC是Microsoft Foundation Classes的缩写,是一种用于Windows平台的C++应用程序框架。OpenGL是一种跨平台的图形API,用于开发图形和计算机视觉应用程序。STL(Standard Template Library)是C++的一部分,提供了一组通用的数据结构和算法。 要在MFC应用程序读取并显示STL三维模型,可以按照以下步骤进行操作: 1. 首先,创建一个MFC应用程序项目,并在项目添加OpenGL支持。这可以通过在项目属性启用OpenGL选项来实现。 2. 在MFC应用程序创建一个窗口,用于显示3D模型。这可以通过创建一个自定义的CStatic控件,并在其上绘制OpenGL图形来实现。 3. 接下来,编写代码来读取STL文件的数据。STL文件包含三角形网格的顶点和法线信息。可以使用标准的文件处理函数来读取和解析STL文件的内容,并将其存储在一个适当的数据结构。 4. 通过OpenGL的API函数,将STL模型数据绘制到窗口。可以使用OpenGL的顶点数组和绘制函数来绘制三角形网格的各个面。 5. 最后,将OpenGL绘制的结果显示在MFC窗口。可以通过重载绘图消息处理函数,并在其调用OpenGL的绘制函数来实现。 需要注意的是,由于MFC和OpenGL都是底层的图形,对于初学者来说,可能需要一定的编程经验和对图形编程的理解。这个过程可能需要一些时间和尝试来完善和调试。 ### 回答3: 在 MFC 使用 OpenGL 来读取并显示 STL 三维模型可以通过以下步骤实现: 1. 添加准备创建 OpenGL 窗口的代码。在 MFC 的窗体类,可以使用 `COpenGLControl` 类或 `CView` 类来实现 OpenGL 窗口。 2. 创建一个函数来读取 STL 三维模型文件。你可以使用 STL 文件解析,例如 `std::ifstream` 类来读取 STL 文件的内容。 3. 在创建的 OpenGL 窗口使用 OpenGL 函数来绘制三维模型。你可以使用 `glBegin`、`glEnd` 和 `glVertex3f` 等函数来绘制模型的顶点和三角面片。 4. 在 OpenGL 窗口,将读取到的 STL 三维模型数据传入 OpenGL 函数来进行绘制。你可以使用 `glColor3f` 函数来设置模型的颜色。 5. 在 MFC 的窗体类,实现绘制 OpenGL 窗口的函数。通过重写 `OnDraw` 或 `OnPaint` 函数,调用 OpenGL 窗口的绘制函数。 6. 在 MFC 的窗体类,重写 `OnCreate` 函数来初始化 OpenGL 窗口,调用读取和显示 STL 模型的函数。 7. 在 MFC 的窗体类,为了在窗口显示 OpenGL 窗口,重写 `OnSize` 函数,并调用 OpenGL 窗口的调整大小函数。 通过上述步骤,你可以在 MFC 使用 OpenGL 来读取并显示 STL 三维模型。你可以进一步优化代码,例如添加相机控制、光照设置等来增强模型的显示效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值