linux+qt使用assimp库进行模型加载

之前学习了window+vs的方式使用assimp库进行模型加载,但是咧,并不支持跨平台使用,所以又搞了一次linux+qt来完成
本章只包含assimp库内容以及assimp与qt交互的问题,其他内容不过多介绍,不熟悉者还是边看learnopengl官网学习边参考

不过大家有问题还是可以问我,我知道的会给大家解答,也可以提出我代码的问题,我会进行修改

1.assimp库导入qt

在此之前需要进行assimp库编译,linux编译都差不多,用3个命令

cmake CMakeLists.txt
make
make install

这里不过多介绍

然后导入我使用一种简单(直接链接)的方法(正式项目不要用哦)
在.pro文件中加入
LIBS +=
/root/assimp/assimp-3.1.1/lib/libassimp.so

这是我assimp库的绝对路径,.so就是linux的库文件,类似window下的.lib文件

2.使用qt封装好的类,而不再使用glew和glut

QOpenGLWidget
用于进行渲染的窗体,我们需要继承并重写3个函数

virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();

这3个函数不需要手动调用,qt会自动调用

QOpenGLFunctions_3_3_Core
版本化的函数包装器,类似glew,用于使用opengl函数,针对给定的opengl版本和配置文件,通常在渲染类(有shader的类中)和main函数中继承或使用
QOpenGLExtraFunctions
函数包装器,对OpenGL ES 3进行跨平台访问,类似QOpenGLFunctions_
3_3_Core,不过是通用的,一般在封装的类中继承,因为封装的类并不进行渲染,而是渲染程序调用时由调用的渲染程序进行渲染

QOpenGLShaderProgram
封装好的着色器程序,我们不用再自己编写shader了
QOpenGLTexture
封装好的纹理,我们不用再自己编写纹理载入代码了

3.渲染窗口

我就不过多介绍了,与window上不同的地方,我会用注释解释

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H


#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTime>
#include <QTimer>
#include <QVector3D>
#include <QKeyEvent>
#include <QMouseEvent>
#include "camera.h"
#include <QDebug>
#include "model.h"
#include <QCoreApplication>
#include <QFileDialog>


#define PI 3.1415926
class MyOpenGLWidget : public QOpenGLWidget,protected QOpenGLFunctions_3_3_Core
{
//继承QOpenGLFunctions_3_3_Core用于使用openGL函数库
//继承QOpenGLWidget用于实现我们自己的渲染窗口
    Q_OBJECT
public:
    explicit MyOpenGLWidget(QWidget *parent = nullptr);
    ~MyOpenGLWidget();

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
    //用于实现camera移动,观看不同的视角
    virtual void keyPressEvent(QKeyEvent *event);
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
//camera,learnopengl教程中的入门就有,不多介绍
    Camera *camera;
//着色器程序
    QOpenGLShaderProgram *colorShader;

    Model ourModel;

    QTimer timer;
    QTime m_time;

    float deltaTime = 0.0f; // 当前帧与上一帧的时间差
    float lastFrame = 0.0f; // 上一帧的时间

    QPoint lastPos;
    bool leftButtonIsPressed = false;
    float sensitivity = 0.2f;

private:

signals:

public slots:
    void on_timer_timeout();
};

#endif // MYOPENGLWIDGET_H

这里先不介绍cpp文件,先实现mesh和model文件,最后再介绍

4.mesh类

头文件

#ifndef MESH_H
#define MESH_H
#include <QString>
#include <QVector3D>
#include <QOpenGLShaderProgram>
#include <QOpenGLExtraFunctions>
#include <QOpenGLTexture>
#include <QDebug>

using std::vector;
struct Vertex {
    QVector3D Position;
    QVector3D Normal;
    QVector3D TexCoords;
};

struct Texture {
    unsigned int num;
    QString type;
    QString filename;
};

class Mesh : protected QOpenGLExtraFunctions
{
//继承QOpenGLExtraFunctions用于使用opengl函数
public:
    vector<Vertex> vertices;
    vector<unsigned int> indices;
    vector<Texture> textures;
    Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures);
    ~Mesh();
    void Draw(QOpenGLShaderProgram *shader,vector<QOpenGLTexture*> textureInstances);
    //draw时传入着色器程序和纹理
private:
       /*  渲染数据  */
     unsigned int VAO, VBO, EBO;
       /*  函数  */
     void setupMesh();
     //用于初始化网格数据
};

#endif // MESH_H

cpp文件

#include "mesh.h"


Mesh::Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures)
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;
    setupMesh();
}

Mesh::~Mesh()
{

}

void Mesh::Draw(QOpenGLShaderProgram* shader,vector<QOpenGLTexture*> textureInstances)
{
shader->bind();
    for(unsigned int i = 0; i < textures.size(); i++)
    {
    //遍历网格纹理,获取纹理类型,并读出纹理对象
        QString name = textures[i].type;
        if(name == "texture_diffuse")
        {
        //bind(),QOpenGLTexture函数,直接绑定到指定的纹理单元,而不用再先glActiveTexture了,很方便
            textureInstances[textures[i].num]->bind(0);
            //shaderProgram也直接封装了,setUnifromxxx的函数也直接封装成setUnifromValue()这一个函数,很方便
            shader->setUniformValue(("material." + name).toLatin1().data(), 0);
            //toLatin1()从QStirng 转为QByteArray
            //data()转为char *
            //设置着色器中此变量纹理也第0槽纹理,也就是刚bind 的
        }
        else if(name == "texture_specular")
        {
        //同上
            textureInstances[textures[i].num]->bind(1);
            shader->setUniformValue(("material." + name).toLatin1().data(), 1);
        }
    }
    // 绘制网格
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    shader->release();
}

void Mesh::setupMesh()
{
    initializeOpenGLFunctions();
    //类似glew的init函数,初始化opengl函数库,必须在第一步用,否则下面的函数都不能使用
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int),&indices[0], GL_STATIC_DRAW);

    // 顶点位置
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    // 顶点法线
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
    // 顶点纹理坐标
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}

5.model类

头文件

#ifndef MODEL_H
#define MODEL_H
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QImage>
#include "mesh.h"
#include <QString>
#include <string>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <QDebug>
using std::string;

class Model
{
public:
    Model();
    ~Model();
    void loadModel(string path);
    void Draw(QOpenGLShaderProgram *shader);
private:
    /*  模型数据  */
    vector<Mesh> meshes;
    QString directory;
    /*  函数   */
    void processNode(aiNode *node, const aiScene *scene);
    Mesh processMesh(aiMesh *mesh, const aiScene *scene);
    vector<Texture> loadMaterialTextures(aiMaterial *mat, aiTextureType type,QString typeName);
    //用于存放纹理,待传入mesh中使用,由于是指针,放在类私有成员等析构函数delete掉
    vector<QOpenGLTexture *> m_textureInstance;
    //纹理数(不是个数,而是在vector中的索引)
    unsigned int m_num = 0;
};

#endif // MODEL_H

cpp文件

#include "model.h"

Model::Model()
{

}

Model::~Model()
{
//把指针内存释放掉
    for(unsigned int i = 0; i < m_textureInstance.size(); ++i)
    {
        delete m_textureInstance[i];
    }
}



void Model::Draw(QOpenGLShaderProgram *shader)
{
//for循环分别绘制每个网格
    for(unsigned int i = 0; i < meshes.size(); i++)
        meshes[i].Draw(shader,m_textureInstance);
}

//加载模型
//通过assimp::Importer类的ReadFile函数读取模型文件,它返回一个aiScene*是assimp库的类,只要遵循它库的规则,用正确的类接受,这里没啥问题
void Model::loadModel(string path)
{
    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
    //判断是否读取文件成功
    if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
    {
       qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
       return;
    }
    //获取目录,从最后一个“/"往前的所有字符串,一会用来读取其他文件
    directory = QString::fromStdString(path.substr(0, path.find_last_of('/')));
    //遍历节点(简单介绍:scene中存放着root节点,网格数据和metaril材
    //质,root节点指向子节点,每个子节点分别还有自己的子节点,类似树的结
    //构,每个节点下有它所对应的网格数据的索引,通过索引去scene中获取自己		
    //对应的网格数据,挨着递归就可以了,mesh类中存放了顶点数据,法线数
    //据,纹理坐标数据,还有面的数据,以及纹理索引,这里的面是立方体的面的
    //意思,每个面中有它对应顶点的索引,获取索引用于绘制面,最后是纹理索
    //引,通过纹理索引去scene中获取纹理,大体就是这样)
    processNode(scene->mRootNode, scene);
}

void Model::processNode(aiNode *node, const aiScene *scene)
{
    // 处理节点所有的网格(如果有的话)
    for(unsigned int i = 0; i < node->mNumMeshes; i++)
    {
       aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
       meshes.push_back(processMesh(mesh, scene));
    }
    // 接下来对它的子节点重复这一过程
    for(unsigned int i = 0; i < node->mNumChildren; i++)
    {
       processNode(node->mChildren[i], scene);
    }
}

Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene)
{
    vector<Vertex> vertices;
    vector<unsigned int> indices;
    vector<Texture> textures;

    for(unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        Vertex vertex;
        // 处理顶点位置、法线和纹理坐标
        QVector3D vector;
        vector.setX(mesh->mVertices[i].x);
        vector.setY(mesh->mVertices[i].y);
        vector.setZ(mesh->mVertices[i].z);
        vertex.Position = vector;

        vector.setX(mesh->mNormals[i].x);
        vector.setY(mesh->mNormals[i].y);
        vector.setZ(mesh->mNormals[i].z);
        vertex.Normal = vector;

        if(mesh->mTextureCoords[0]) // 网格是否有纹理坐标?
        {
            QVector2D vec;
            vec.setX(mesh->mTextureCoords[0][i].x);
            vec.setY(mesh->mTextureCoords[0][i].y);
            vertex.TexCoords = vec;
        }
        else
            vertex.TexCoords = QVector2D(0.0f, 0.0f);

        vertices.push_back(vertex);
    }
    // 处理索引
    for(unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        for(unsigned int j = 0; j < face.mNumIndices; j++)
            indices.push_back(face.mIndices[j]);
    }
    // 处理材质
    if(mesh->mMaterialIndex >= 0)
    {
        aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
        vector<Texture> diffuseMaps = loadMaterialTextures(material,
                                            aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
        vector<Texture> specularMaps = loadMaterialTextures(material,
                                            aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
    }
    return Mesh(vertices, indices, textures);
}

vector<Texture> Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, QString typeName)
{
//这个函数不多将,learnopengl网站写的很详细
    vector<Texture> textures_temp;
    vector<Texture> textures_loaded;
    for(unsigned int i = 0; i < mat->GetTextureCount(type); i++)
    {
        aiString str;
        mat->GetTexture(type, i, &str);
        QString filename = QString::fromStdString(str.C_Str()); //类型转换aiString转为QString
        bool skip = false;
        for(unsigned int j = 0; j < textures_loaded.size(); j++)
        {
            if(textures_loaded[j].filename == filename)
            {
                textures_temp.push_back(textures_loaded[j]);
                skip = true;
                break;
            }
        }
        if(!skip)
        {   // 如果纹理还没有被加载,则加载它
            Texture texture;
            QString path = directory+"/"+filename;
            //new出纹理对象放在vector中供使用,以及纹理的一些配置,都是
            //qt封装好的,就是st方向的环绕方式和缩小放大筛选器,类似
            //glTexParametri()
            QOpenGLTexture *textureInstance =  new QOpenGLTexture(QImage(path).mirrored());
            textureInstance->setWrapMode(QOpenGLTexture::DirectionS,QOpenGLTexture::Repeat);
            textureInstance->setWrapMode(QOpenGLTexture::DirectionT,QOpenGLTexture::Repeat);
            textureInstance->setMagnificationFilter(QOpenGLTexture::Linear);
            textureInstance->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
            m_textureInstance.push_back(textureInstance);
            texture.num = m_num++;
            texture.type = typeName;
            texture.filename = filename;
            textures_temp.push_back(texture);
            textures_loaded.push_back(texture); // 添加到已加载的纹理中        
        }
    }
    return textures_temp;
}

6.渲染

#include "myopenglwidget.h"


MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setMouseTracking(true);
    connect(&timer, SIGNAL(timeout()), this, SLOT(on_timer_timeout()));
    timer.start(100);
    m_time.start();
}

MyOpenGLWidget::~MyOpenGLWidget()
{
    delete camera;
    delete colorShader;
}

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    camera = new Camera(QVector3D(0.0f,0.0f,3.0f),0.0f,-90.0f,QVector3D(0.0f,1.0f,0.0f));

    glEnable(GL_DEPTH_TEST);

    colorShader = new QOpenGLShaderProgram();
    colorShader->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,":/shader/color.vert");
    colorShader->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,":/shader/color.frag");

    bool success = colorShader->link();
    if(!success)
    {
       qDebug() << colorShader->log();
    }
//linux+qt读文件是读相对路径,记得把模型文件(文件夹)放到编译后的目录中
//由于使用的是assimp的ReadFile,使用qt的.qrc文件我这里没成功
    ourModel.loadModel("./model/nanosuit.obj");
}

void MyOpenGLWidget::resizeGL(int w, int h)
{
    Q_UNUSED(w);
    Q_UNUSED(h);
}

void MyOpenGLWidget::paintGL()
{

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_TEST);

    colorShader->bind();
    colorShader->setUniformValue("viewPos", camera->getPostion());

    QMatrix4x4 projection;
    projection.perspective(45.0f,(float)width()/(float)height(),0.1f, 100.0f);
    QMatrix4x4 view;
    view = camera->getViewMatrix();
    colorShader->setUniformValue("projection", projection);
    colorShader->setUniformValue("view", view);
    QMatrix4x4 model;
    colorShader->setUniformValue("model", model);

    ourModel.Draw(colorShader);

    colorShader->release();
}


void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_W:
        camera->speedZ = 1.0f* deltaTime;
        break;
    case Qt::Key_S:
        camera->speedZ = -1.0f * deltaTime;
        break;
    case Qt::Key_D:
        camera->speedX = 1.0f * deltaTime;
        break;
    case Qt::Key_A:
        camera->speedX = -1.0f * deltaTime;
        break;
    case Qt::Key_Up:
        camera->speedY = 1.0f * deltaTime;
            break;
    case Qt::Key_Down:
        camera->speedY = -1.0f * deltaTime;
            break;
    case Qt::Key_Escape:
        this->parentWidget()->setFocus();
    default:
        break;
    }
    camera->updataCameraPos();
    update();
}

void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(leftButtonIsPressed)
    {
        QPoint currentPos = event->pos();
        QPoint deltaPos = currentPos - lastPos;
        lastPos = currentPos;
        deltaPos *= sensitivity;
        camera->processMouseMove(deltaPos.x(),-deltaPos.y());
        update();
    }
}

void MyOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        lastPos = event->pos();
        leftButtonIsPressed = true;
    }
}

void MyOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        leftButtonIsPressed = false;
    }
}



void MyOpenGLWidget::on_timer_timeout()
{
    float currentFrame = m_time.elapsed()/1000.0f;
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
}

vertex着色器文件和fragment着色器和learnopengl上的一模一样

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out vec2 TexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;


void main()
{

    TexCoords = aTexCoords;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

#version 330 core
in vec2 TexCoords;

out vec4 FragColor;

struct Material {
    sampler2D texture_diffuse;
    sampler2D texture_specular;
};

uniform Material material;


void main()
{
    vec4 diffuse = texture2D(material.texture_diffuse, TexCoords);
    FragColor = diffuse;
}


最后在ui界面添加一个QOpenGLWidget,然后promote为自己定义的myOpenGLWidget

并在mianWindow中设置中心组件

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setCentralWidget(ui->openGLWidget);
    showFullScreen ();
}

成果展示:
在这里插入图片描述
最后总结一下用到的assimp库的东西
Assimp::Importer
importer.ReadFile
importer.GetErrorString

aiProcess_Triangulate
aiProcess_FlipUVs

aiScene
scene->mRootNode
scene->mMeshes
scene->mMaterials

aiNode
node->mNumMeshes
node->mNumChildren
node->mChildren

aiMesh
mesh->mVertices
mesh->mNormals
mesh->mTextureCoords
mesh->mNumFaces
mesh->mFaces
mesh->mMaterialIndex

aiFace
face.mNumIndices
face.mIndices

aiMaterial
mat->GetTextureCount
mat->GetTexture

aiTextureType_DIFFUSE
aiTextureType_SPECULAR

aiString
str.C_Str()

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
Qt、Assimp和OpenGL是三种常用的工具,在三维模型设计中发挥着重要的作用。Qt是一个跨平台的C++应用程序开发框架,它为用户提供了GUI和其他一些基本的用户界面组件。Assimp是一个允许用户读取和写入多种3D文件格式的,包括OBJ、FBX、DAE等。OpenGL是一种用于创建高性能的计算机图形的API。因此,将这三种工具结合使用,可以方便地进行三维模型开发。 在使用Qt、Assimp和OpenGL进行三维模型开发时,Qt主要负责提供GUI界面和基本的用户界面组件。Assimp则用于读取和写入不同格式的三维模型,然后通过OpenGL进行渲染展示。OpenGL作为一个底层API,提供了强大的图形处理能力,允许用户在计算机上创建复杂的3D场景和动画。 具体地说,使用这三种工具进行三维模型开发的步骤通常如下:首先用Assimp读入各种格式的三维模型文件,然后通过OpenGL进行渲染显示。Qt可以用于创建一个GUI界面,方便用户进行模型读取、渲染、缩放、旋转、材质设定等基本的操作。同时,用户可以通过Qt的事件机制来和OpenGL进行交互:比如通过鼠标事件、键盘事件等,实现对三维模型的选择、移动等控制。在此基础上,用户还可以通过OpenGL的扩展和着色器功能等,使三维模型的渲染更加复杂和高效。 总之,Qt、Assimp和OpenGL是三种常用的工具,它们的结合使用可以让用户方便地进行三维模型的设计和开发。在实际应用中,用户可以根据实际需求选择不同的工具和技术,实现更加复杂和高效的三维模型

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值