QT OpenGL加载STL模型文件并旋转放缩

QT OpenGL加载STL模型文件并旋转放缩_第1张图片

加载模型文件

模型文件有很多种格式,有一种方法是利用Assimp库来加载各类格式的模型,也就是众生平等!
但是本人太菜,网上搜索的各种方法我都没能成功在QT中实现,而且本人项目需求不高,所以我选择了简单的方法来加载模型文件——STL模型文件加载,也就是说我这个只能加载一个格式的模型文件,但对我来说够用了。

STL文件格式

QT OpenGL加载STL模型文件并旋转放缩_第2张图片
文件格式如上图:STL格式是将模型保存为一个又一个的小三角形,精度越高,三角形数量越多。
其中fact-endfact之间是一个三角形的信息,包括facet normal后面的是该面的法向量,用于光照。
三个vertex是三角形三个顶点。通过如下函数可将该模型文件的信息读入数组之中。

vertices = loadAscllStl("C:\\Users\\86132\\Desktop\\STL_model_ascll\\test1.STL",1);

ratio是放大系数,用于模型文件尺寸数据过大或者过小时,可用该系数调整。

QVector<float> Widget::loadAscllStl(QString filename,int ratio)
{
    QVector<float> vertices_temp;
   qDebug() << "load text file:" << filename;

  QFile file(filename);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
      qDebug() << "Open stl_file failed." << endl;
  }
    while (!file.atEnd())//文件一行一行读,未读到最后一行,就执行循环体
    {
      QString line = file.readLine().trimmed(); // trimmed去除了开头和结尾的空白字符串
      QStringList  words = line.split(' ', QString::SkipEmptyParts);

      if (words[0] == "facet") {
      Normal = {ratio*words[2].toFloat(), ratio*words[3].toFloat(),ratio*words[4].toFloat()};//获取法向量
      }
      else if (words[0] == "vertex") {
        Position = {ratio*words[1].toFloat(), ratio*words[2].toFloat(),ratio*words[3].toFloat()};//获取顶点位置
        vertices_temp.append(Position);
        vertices_temp.append(Normal);
      }
      else
      {
          continue;
      }

      }

    qDebug() << "write vertice_temp success!" << filename;
    return vertices_temp;
 //   file.close();
}

这里填写模型文件地址时,我始终加载不成功,最后找到个说法,得用双斜杠在地址中才能加载成功!

SolidWorks绘制模型保存时要注意的地方

首先,最好以原点为模型的中心,这样旋转的时候才会以模型的中心旋转,而不是以某一顶点旋转。
其次,保存文件时,在选项中可选择单位,精度,以及勾选不将坐标转为正空间。如果不勾选,软件回默认将你的模型坐标全部转为正坐标,这样,即使你画模型时,将模型中心放在原点,也会全部被转为正坐标,到时候出来的模型旋转会以一个顶点而非中心旋转。
精度选取可自行选取,看上面的GIF,中间圆柱很明显像是很多长方形连起来的,这是因为我选的精度不高。

鼠标事件实现旋转放缩

这部分原理与坐标变换那部分相同。主要是获取鼠标的操作信息,来获取xtrans,ytrans,ztrans。然后改变model矩阵,通过该矩阵与模型顶点数据相乘,即可得到旋转放缩的模型。之前的旋转的立方体的model是随时间变化,这里只不过改成了随鼠标的输入变化而已。

鼠标事件代码

void Widget::mousePressEvent(QMouseEvent *event)//鼠标事件获取
{
    mousePos = QVector2D(event->pos());
    event->accept();
}

void Widget::mouseMoveEvent(QMouseEvent *event)//鼠标移动
{
    if(event->buttons() == Qt::LeftButton)
    {
        QVector2D newPos = (QVector2D)event->pos();
        QVector2D diff = newPos - mousePos;
        qreal angle = (diff.length())/3.6;
        // Rotation axis is perpendicular to the mouse position difference
        // vector
        QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();
        rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * rotation;
        mousePos = newPos;
        this->update();
    }
    event->accept();
}

void Widget::wheelEvent(QWheelEvent *event)//滚轮事件
{
    QPoint numDegrees = event->angleDelta() / 8;

    if (numDegrees.y() > 0) {
        ztrans += 0.25f;
    } else if (numDegrees.y() < 0) {
        ztrans -= 0.25f;
    }
    this->update();
    event->accept();
}

model改变

    model.setToIdentity();
    model.translate(xtrans, ytrans, ztrans);
    model.rotate(rotation);
    shaderprogram.setUniformValue("model", model);

顶点着色器改变

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

光照部分

    QVector3D lightColor(1.0f,1.0f,1.0f);
    QVector3D objectColor(1.0f,0.5f,0.31f);
    QVector3D lightPos(0.0f,0.0f,50.0f);//光源位置

    shaderprogram.setUniformValue("objectColor",objectColor);
    shaderprogram.setUniformValue("lightColor",lightColor);
    shaderprogram.setUniformValue("lightPos", lightPos);

片段着色器改变

     float ambientStrength = 0.1;
       vec3 ambient = ambientStrength * lightColor;

       vec3 norm = normalize(Normal);
       vec3 lightDir = normalize(lightPos - FragPos);
       float diff = max(dot(norm, lightDir), 0.0);
       vec3 diffuse = diff * lightColor;

       vec3 result = (ambient + diffuse) * objectColor;
       FragColor = vec4(result, 1.0);

整体代码分享

main.cpp

#include "widget.h"

#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
        w.resize(800, 600);

        return a.exec();
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class Widget : public QOpenGLWidget,public QOpenGLExtraFunctions
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    virtual void initializeGL() override;
    virtual void paintGL() override;
    virtual void resizeGL(int w,int h) override;
    QVector<float> loadAscllStl(QString filename,int ratio);//文件名和放大系数
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void wheelEvent(QWheelEvent *event);


private:
    QVector<float> vertices;
    QVector<float> Position;
    QVector<float> Normal;//读文件时的俩个临时变量顶点位置,法向量
    QOpenGLShaderProgram shaderprogram;
    QOpenGLVertexArrayObject VAO;//声明VAO顶点数组对象
    QOpenGLBuffer VBO;//声明VBO数组缓冲对象

    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    GLfloat xtrans, ytrans, ztrans; // translation on x,y,z-axis
    QVector2D mousePos;
    QQuaternion rotation;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent)
    ,VBO(QOpenGLBuffer::VertexBuffer)
    ,xtrans(0),ytrans(0),ztrans(0.0)
{
    QSurfaceFormat format;
    format.setAlphaBufferSize(24);  //设置alpha缓冲大小
    format.setVersion(3,3);         //设置版本号
    format.setSamples(10);          //设置重采样次数,用于反走样

    this->setFormat(format);

         vertices = loadAscllStl("C:\\Users\\86132\\Desktop\\STL_model_ascll\\test1.STL",1);

}

Widget::~Widget()
{
    makeCurrent();
}

QVector<float> Widget::loadAscllStl(QString filename,int ratio)
{
    QVector<float> vertices_temp;
   qDebug() << "load text file:" << filename;

  QFile file(filename);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
      qDebug() << "Open stl_file failed." << endl;
  }
    while (!file.atEnd())
    {
      QString line = file.readLine().trimmed(); // trimmed去除了开头和结尾的空白字符串
      QStringList  words = line.split(' ', QString::SkipEmptyParts);

      if (words[0] == "facet") {
      Normal = {ratio*words[2].toFloat(), ratio*words[3].toFloat(),ratio*words[4].toFloat()};
      }
      else if (words[0] == "vertex") {
        Position = {ratio*words[1].toFloat(), ratio*words[2].toFloat(),ratio*words[3].toFloat()};
        vertices_temp.append(Position);
        vertices_temp.append(Normal);
      }
      else
      {
          continue;
      }

      }

    qDebug() << "write vertice_temp success!" << filename;
    return vertices_temp;
 //   file.close();
}

void Widget::initializeGL()
{
    this->initializeOpenGLFunctions();//初始化opengl函数
    shaderprogram.create();//生成着色器程序
    if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/stl.vert")){
        qDebug()<<"ERROR:"<<shaderprogram.log();    //如果编译出错,打印报错信息
    }
    if(!shaderprogram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/stl.frag")){
        qDebug()<<"ERROR:"<<shaderprogram.log();    //如果编译出错,打印报错信息
    }
    //将添加到此程序的着色器与addshader链接在一起
     if(!shaderprogram.link()){
         qDebug()<<"ERROR:"<<shaderprogram.log();    //如果链接出错,打印报错信息
     }
//    QOpenGLVertexArrayObject::Binder{&VAO};

    VAO.create();// 创建一个VAO对象,OpenGL会给它(顶点数组缓存对象)分配一个id
    VAO.bind();//将RC中的当前顶点数组缓存对象Id设置为VAO的id
    VBO.create();
    VBO.bind();
    VBO.allocate(vertices.data(),sizeof(float)*vertices.size());//将顶点数据分配到VBO中,第一个参数为数据指针,第二个参数为数据的字节长度



    shaderprogram.setAttributeBuffer("aPos", GL_FLOAT, 0, 3, sizeof(GLfloat) * 6);
    shaderprogram.enableAttributeArray("aPos");
    shaderprogram.setAttributeBuffer("aNormal", GL_FLOAT,sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 6);
    shaderprogram.enableAttributeArray("aNormal");
     this->glEnable(GL_DEPTH_TEST);
//    VAO.release();//释放
//    VBO.release();

    view.setToIdentity();
    view.lookAt(QVector3D(0.0f, 0.0f, 3.0f), QVector3D(0.0f,0.0f,0.0f), QVector3D(0.0f,1.0f,0.0f));
    //shaderprogram.setUniformValue("view", view);
}
void Widget::resizeGL(int w,int h)
{
    this->glViewport(0,0,w,h);
    projection.setToIdentity();
    projection.perspective(60.0f, (GLfloat)w/(GLfloat)h, 0.001f, 100.0f);
    //shaderprogram.setUniformValue("projection", projection);
}

void Widget::paintGL()
{
    this->glClearColor(0.0f,0.0f,0.0f,1.0f);//设置清屏颜色
    this->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清空颜色缓冲区

    shaderprogram.bind();
    //将此着色器程序绑定到活动的qopenglcontext,并使其成为当前着色器程序。任何先前绑定的着色器程序都将被释放
    //成功绑定返回ture,反之,返回false.
  {
    QVector3D lightColor(1.0f,1.0f,1.0f);
    QVector3D objectColor(1.0f,0.5f,0.31f);
    QVector3D lightPos(0.0f,0.0f,50.0f);

    shaderprogram.setUniformValue("objectColor",objectColor);
    shaderprogram.setUniformValue("lightColor",lightColor);
    shaderprogram.setUniformValue("lightPos", lightPos);


    model.setToIdentity();
    model.translate(xtrans, ytrans, ztrans);
    model.rotate(rotation);
    shaderprogram.setUniformValue("view", view);
    shaderprogram.setUniformValue("projection", projection);
    shaderprogram.setUniformValue("model", model);



    int n = vertices.capacity()/sizeof(float);
    qDebug() << n;
    QOpenGLVertexArrayObject::Binder bind(&VAO);//绑定VAO
    this->glDrawArrays(GL_TRIANGLES,0,n);

    }
}


void Widget::mousePressEvent(QMouseEvent *event)
{
    mousePos = QVector2D(event->pos());
    event->accept();
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() == Qt::LeftButton)
    {
        QVector2D newPos = (QVector2D)event->pos();
        QVector2D diff = newPos - mousePos;
        qreal angle = (diff.length())/3.6;
        // Rotation axis is perpendicular to the mouse position difference
        // vector
        QVector3D rotationAxis = QVector3D(diff.y(), diff.x(), 0.0).normalized();
        rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angle) * rotation;
        mousePos = newPos;
        this->update();
    }
    event->accept();
}

void Widget::wheelEvent(QWheelEvent *event)
{
    QPoint numDegrees = event->angleDelta() / 8;

    if (numDegrees.y() > 0) {
        ztrans += 0.25f;
    } else if (numDegrees.y() < 0) {
        ztrans -= 0.25f;
    }
    this->update();
    event->accept();
}

stl.vert

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aNormal; // 颜色变量的属性位置值为 1

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

out vec3 FragPos;
out vec3 Normal;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    Normal = mat3(model) * aNormal;//用于旋转时,使得法向量一起改变
    FragPos = vec3(model * vec4(aPos, 1.0));
}

stl.frag

#version 330 core
//layout( location = 0 ) out vec4 FragColor;

out vec4 FragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 lightPos;

void main()
{
     float ambientStrength = 0.1;
       vec3 ambient = ambientStrength * lightColor;

       vec3 norm = normalize(Normal);
       vec3 lightDir = normalize(lightPos - FragPos);
       float diff = max(dot(norm, lightDir), 0.0);
       vec3 diffuse = diff * lightColor;

       vec3 result = (ambient + diffuse) * objectColor;
       FragColor = vec4(result, 1.0);

}

QT OpenGL加载STL模型文件并旋转放缩_第3张图片

至此,在Windows下的QT5.9.9下实现了STL模型文件的加载放缩旋转展示。

你可能感兴趣的:(opengl)