Qt+OpenGL基础教程

基于OpenGL的UR3机械臂仿真(一)

一、在Qt里面创建OpenGL窗口

环境配置

.pro文件

LIBS += -lopengl32 \
        -lglu32 \
        -glut \

LIBS += opengl32.lib \
        glu32.lib \
        glut.lib
        
DISTFILES += \
    coordinate.vert \
    coordinate.frag

上面的库文件需要下载配置,详细步骤参考

Qt5.9.4中配置opengl的glut库(Windows)_明卿的博客-CSDN博客_qt opengl

下面的两个文件是shader文件,如果有的话需要加上。(后续会介绍shader文件)

头文件需要包括以下

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

创建OpenGL窗口

项目代码:opengl_base

大概的逻辑:

1、先创建一个普通的QWidget(这个QWidget用来显示OPenGL)

2、写一个OpenGL的类

3、构造的时候将QWidget传进去

class ROBOT_SIMULATION : public QOpenGLWidget,protected QOpenGLFunctions{
    Q_OBJECT
public:
    ROBOT_SIMULATION(QWidget *parent);
protected:
    //重载几个opengl的重要函数
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;
/*
......
*/
};

//构造函数写法
ROBOT_SIMULATION::ROBOT_SIMULATION(QWidget *parent):QOpenGLWidget(parent)
{
    this->setGeometry(parent->rect().x(),parent->rect().y(),
                      parent->rect().width(),parent->rect().height());
	//设置与控件大小相同
}

最简单的OpenGL类如上述代码块

注意几点:1、这个类必须继承QOpenGLWidget与QOpenGLFunctions

​*2、构造函数中的第一个参数QWidget 必须与用来显示的控件类型相同

3、重载OpenGL的几个成员函数如上述三个函数

创建OPenGL类对象

//widget.h
ROBOT_SIMULATION *robot;
//widget.cpp
robot = (new ROBOT_SIMULATION(ui->widget,this));

重载的三个函数

initializeGL()

void ROBOT_SIMULATION::initializeGL()
{
    initializeOpenGLFunctions();
/*
......
*/
}

这个函数仅会在构造类对象时调用一次

必须initializeOpenGLFunctions();

resizeGL()

void ROBOT_SIMULATION::resizeGL(int w, int h)
{
    this->glViewport(0,0,w,h);
}

该函数稍微没那么重要,调整窗口大小

paintGL()

void ROBOT_SIMULATION::paintGL()
{
	/*
	.......
	*/
}

最重要的函数

每一次调整OpenGL()窗口,如调整视角等操作都会调用一次该函数

可以将其看作是不断调用这个函数刷新窗口的机制,所有需要改变的东西都应该放进这个函数中绘制。

完成上述后可以看到最基础的OpenGL窗口已经创建完毕

Qt+OpenGL基础教程_第1张图片

二、OPenGL基础绘制

①背景上色

只需要在初始化写一句,然后设置对应的颜色即可

void ROBOT_SIMULATION::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
}

背景即可设置成对应的颜色

Qt+OpenGL基础教程_第2张图片

②画一个坐标系

项目代码:draw_axis

需要做UR3机械臂仿真,在机器人领域中坐标系是非常重要的。

画一个坐标系只需要简单的三条直线

//坐标系点
static float vertices[] = {//我们所准备的需要提供给openGL的顶点数据
    // 位置              // 颜色
     0.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f,
     0.1f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f,
     0.0f, 0.0f, 0.0f,  0.0f, 1.0f, 0.0f,
     0.0f, 0.1f, 0.0f,  0.0f, 1.0f, 0.0f,
     0.0f, 0.0f, 0.0f,  0.0f, 0.0f, 1.0f,
     0.0f, 0.0f, 0.1f,  0.0f, 0.0f, 1.0f,
     0.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f,
     100.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f,
     0.0f, 0.0f, 0.0f,  0.0f, 1.0f, 0.0f,
     0.0f, 100.0f, 0.0f,  0.0f, 1.0f, 0.0f,
     0.0f, 0.0f, 0.0f,  0.0f, 0.0f, 1.0f,
     0.0f, 0.0f, 100.0f,  0.0f, 0.0f, 1.0f,
};

先定义一个数组,存放所有我们画坐标系需要用到的数据

一个点需要给出它对应的x、y、z的坐标与这个点对应的r、g、b信息

这里比较简单直接穷举了所有的点,可以发现这里边有重复的点,如果要追求程序简洁的话可以用IBO的方法解决删减点,不过那样编程上稍微会麻烦一点,作为教程还是使用了简单的方法

OpenGL的渲染逻辑

Qt+OpenGL基础教程_第3张图片

What is VBO?

顶点缓冲对象(Vertex Buffer Objects, VBO)
顶点着色器它会在GPU上创建内存用于储存我们的顶点数据,还要配置OpenGL如何解释这些内存,并且指定其如何发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶点。通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这个内存,它会在GPU内存(通常被称为显存)中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问顶点,这是个非常快的过程。

VBO是CPU与GPU传递数据的桥梁,将数据存到VBO中(这步在CPU中完成),VBO会将数据送到GPU中。

//robot_simulation.h
QOpenGLBuffer m_vbo;
//robot_simulation.cpp在initializeGL()中完成
//创建并绑定VBO
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(vertices, sizeof(vertices));//向VBO传递我们准备好的数据(本文件起始部分的静态数组)

顶点着色器

coordinate.vert

渲染坐标输入

in声明输入顶点属性

out声明输出顶点属性

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}

coordinate.frag

片段着色器

颜色输出

#version 330 core
in vec3 ourColor;
void main()
{
     gl_FragColor = vec4(ourColor,1.0);
}

着色器程序

链接上述两个着色器为一个着色器程序对象,在渲染对象时激活这个着色器程序,已激活着色器程序的着色器将在发送渲染调用时被使用。

//robot_simulation.h
QOpenGLShaderProgram* program;   //着色器程序对象

//robot_simulation.cpp在initializeGL()中完成
void ROBOT_SIMULATION::initializeGL(){
    initializeOpenGLFunctions();
    program = new QOpenGLShaderProgram;
    program->bind();
    //添加顶点着色器
    program->addShaderFromSourceFile(QOpenGLShader::Vertex,"./coordinate.vert");
    //添加片段着色器
    program->addShaderFromSourceFile(QOpenGLShader::Fragment,"./coordinate.frag");
    program->link();

    //创建并绑定VBO
    m_vbo.create();
    m_vbo.bind();
    m_vbo.allocate(vertices, sizeof(vertices));//向VBO传递我们准备好的数据(本文件起始部分的静态数组)

    //向顶点着色器传递其中定义为"aPos"的变量所需的数据
    program->setAttributeBuffer(program->attributeLocation("aPos"),GL_FLOAT, 0, 3,6*sizeof(GLfloat));
    program->enableAttributeArray(program->attributeLocation("aPos"));

    //向顶点着色器传递其中定义为"aColor"的变量所需的数据
    program->setAttributeBuffer(program->attributeLocation("aColor"),GL_FLOAT,3*sizeof(GLfloat),3,6*sizeof(GLfloat));
    program->enableAttributeArray(program->attributeLocation("aColor"));
    program->release();//解绑程序

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);  //背景上色
}

//robot_simulation.cpp(使用)
void ROBOT_SIMULATION::paintGL(){
    program->bind();
    glDrawArrays(GL_LINES, 0, 2);//绘制基坐标系
    glDrawArrays(GL_LINES, 2, 2);
    glDrawArrays(GL_LINES, 4, 2);
    program->release();//解绑程序
}

Qt+OpenGL基础教程_第4张图片

③画一个三维坐标系

项目代码:

上面我们给的顶点数据是个三维的,但是因为没有加入鼠标交互(动态相机)的效果,物体映射到图片上是二维的。为了显示出这是一个三维的物体,我们重写Qt的鼠标事件以控制相机的移动。

**鼠标交互的效果:**按下鼠标左键后移动鼠标,视角沿着鼠标移动方向旋转。滚动鼠标滚轮,能够放大/缩小视角。

//robot_simulation.h
//鼠标变量
    bool changeview_ = false;            //改变视角的标志位
    float last_x;               //记录鼠标按下时鼠标的位置用以作视角偏移
    float last_y;
    float yaw = 0.0;            //偏航角
    float pitch = 0.0;          //俯视角
    float fov = 60.0f;          //视野范围
    float sensitivity = 1.5;    //鼠标灵敏度
	QVector3D cameraDirection = QVector3D(0.0f,0.0f,1.5f);  //摄像机方向
    QVector3D cameraUp = QVector3D(0.0f,1.0f,0.0f);         //摄像机上向量
//鼠标交互事件重写
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;


//robot_simulation.cpp
//鼠标交互事件
void ROBOT_SIMULATION::mousePressEvent(QMouseEvent *event){
    if (event->button()==Qt::LeftButton ){  //鼠标左键按下允许改变相机视角
        changeview_ = true;
    }
}

void ROBOT_SIMULATION::mouseReleaseEvent(QMouseEvent *event){  //鼠标左键松开禁止改变相机视角
    changeview_ = false;
}

//控制相机变换位置
void ROBOT_SIMULATION::mouseMoveEvent(QMouseEvent *event){
    if (changeview_ == false)
        return;
    //如果鼠标左键按下且未松开,允许视角发生变化
    float offset_x = event->x() - last_x;
    float offset_y = event->y() - last_y;
    if (offset_x>0){  //鼠标向右走offset_x>0
        yaw += sensitivity*1*3.14/180;
    }
    else if (offset_x<0){
        yaw -= sensitivity*1*3.14/180;
    }
    if (offset_y>0){  //鼠标向上走offest_y<0
        pitch += sensitivity*1*3.14/180;
    }
    else if (offset_y<0){
        pitch -= sensitivity*1*3.14/180;
    }
    if(yaw>6.28 || yaw<-6.28)
        yaw = 0;
    if (pitch>3.14/2){
        pitch = 3.13/2;
    }
    if (pitch < -3.14/2){
        pitch = -3.13/2;
    }

    float x_change = 1.5*sin(yaw)*cos(pitch);
    float y_change = 1.5*sin(pitch);
    float z_change = 1.5*cos(yaw)*cos(pitch);

    cameraDirection.setX(x_change);
    cameraDirection.setY(y_change);
    cameraDirection.setZ(z_change);
    cameraUp = QVector3D(0.0f,1.0f,0.0f);
    this->repaint();
    last_x = event->x();
    last_y = event->y();
}

//控制相机缩放
void ROBOT_SIMULATION::wheelEvent(QWheelEvent *event){
    if (event->delta() > 0)
        fov-=2.0f;
    else
        fov+=2.0f;
    if (fov<1.0f)
        fov = 1.0f;
    if (fov>=90.f)
        fov = 90.0;
    this->repaint();
}

上述代码仅仅追踪了鼠标的位置,通过鼠标的交互改变了一些变量,但此时的视角不会产生任何变换,需要对顶点的坐标进行一定的变换

首先介绍几个矩阵:

1、view矩阵,这个矩阵代表了当前相机的位置观察点之间的关系

2、projection矩阵,这个矩阵决定了相机在x,y,z三个方向上能看到的范围

3、common_mat矩阵,这个矩阵不是必要的,我们观察上一个项目坐标系的z轴指向了屏幕外,为了改成符合我们观察习惯(z轴朝上)定义的一个变换矩阵。

//robot_simulation.h
	//改变视角变量
    QMatrix4x4 view;  //观察矩阵
    QMatrix4x4 projection ;
    QMatrix4x4 common_mat;  //按照平常的习惯变换视角

//robot_simulation.cpp
ROBOT_SIMULATION::ROBOT_SIMULATION(QWidget *parent,Widget *data):QOpenGLWidget(parent)
{
    /*
    ......
    */
    QMatrix4x4 mat1 = QMatrix4x4();
    mat1.rotate(-90,0,1,0);
    QMatrix4x4 mat2 = QMatrix4x4();
    mat2.rotate(-90,1,0,0);
    common_mat = mat1*mat2;
}

void ROBOT_SIMULATION::paintGL(){
    program->bind();
	/*
    ......
    */
    view.setToIdentity();
    view.lookAt(cameraDirection, QVector3D(0.0f, 0.0f, 0.0f), cameraUp);
    projection.setToIdentity();
    projection.perspective(fov, (float)width() / (float)height(), 0.01f, 10.0f);
    program->setUniformValue("projection", projection);
    program->setUniformValue("view", view);
    program->setUniformValue("common_view",common_mat);
    /*
    ......
    */
    program->release();//解绑程序
}

顶点着色器coordinate.vert

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 common_view;

void main()
{
    gl_Position = projection *view *common_view*vec4(aPos, 1.0);
    ourColor = aColor;
}

通过左乘变化矩阵,改变点的映射到屏幕的位置从而造成视角的变换。如下图

mon_view",common_mat);
/*

*/
program->release();//解绑程序
}


顶点着色器coordinate.vert

```glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 common_view;

void main()
{
    gl_Position = projection *view *common_view*vec4(aPos, 1.0);
    ourColor = aColor;
}

通过左乘变化矩阵,改变点的映射到屏幕的位置从而造成视角的变换。如下图

Qt+OpenGL基础教程_第5张图片

三、工程代码

上述完整代码

你可能感兴趣的:(qt,c++)