Skip to main content
标签ad报错:该广告ID(9)不存在。
  主页 > Qt入门

一个Qt/C++ Demo,用于实现一个用于显示眼图的控件效果

2023-04-23 浏览:
标签ad报错:该广告ID(7)不存在。
#pragma once

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix4x4>
#include <QTimer>

class EyeDiagramWidget : public QOpenGLWidget, protected QOpenGLFunctions {
   Q_OBJECT
public:
   explicit EyeDiagramWidget(QWidget* parent = nullptr) :
       QOpenGLWidget(parent),
       m_timer(new QTimer(this)),
       m_offset(0.0f),
       m_program(nullptr)
   {
   }

   ~EyeDiagramWidget() override {
       delete m_timer;
       delete m_program;
   }

protected:
   void initializeGL() override {
       initializeOpenGLFunctions();

       glClearColor(0, 0, 0, 1);

       glEnable(GL_PROGRAM_POINT_SIZE);
       glEnable(GL_POINT_SPRITE);
       glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);

       m_program = new QOpenGLShaderProgram();
       m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderCode());
       m_program->addShaderFromSourceCode(QOpenGLShader::Geometry, geometryShaderCode());
       m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderCode());
       m_program->bindAttributeLocation("a_position", 0);
       m_program->bindAttributeLocation("a_time", 1);
       m_program->link();
   }

   void resizeGL(int w, int h) override {
       glViewport(0, 0, w, h);
   }

   void paintGL() override {
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

       if (!m_program->isLinked()) {
           return;
       }
       m_program->bind();

       // 设置模型视图矩阵和投影矩阵
       QMatrix4x4 modelViewMatrix;
       modelViewMatrix.translate(0, 0, -1.0f);
       m_program->setUniformValue("u_modelViewMatrix", modelViewMatrix);

       float aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
       QMatrix4x4 projectionMatrix;
       projectionMatrix.perspective(60, aspectRatio, 0.1f, 100.0f);
       m_program->setUniformValue("u_projectionMatrix", projectionMatrix);

       // 设置偏移量和时间差
       m_offset += 0.01f;
       if (m_offset > 1.0f) {
           m_offset -= 1.0f;
       }
       m_program->setUniformValue("u_offset", m_offset);
       m_program->setUniformValue("u_timeStep", 0.001f);

       // 绘制眼图
       m_vao.bind();
       glDrawArrays(GL_POINTS, 0, 10000);
       m_vao.release();

       m_program->release();
   }

private:
   QString vertexShaderCode() const {
       return QString(R"(
           attribute vec3 a_position;
           attribute float a_time;

           uniform mat4 u_modelViewMatrix;
           uniform mat4 u_projectionMatrix;

           varying float v_time;

           void main()
           {
               gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
               v_time = a_time;
           }
       )");
   }

   QString geometryShaderCode() const {
       return QString(R"(
           #version 330

           layout (points) in;
           layout (triangle_strip, max_vertices = 6) out;

           uniform float u_offset;
           uniform float u_timeStep;

           in float v_time[];
           out vec2 g_texCoord;

           void main()
           {
               // 计算偏移位置和时间
               float offset = u_offset - 0.5;
               float time = v_time[0] + (offset * 2.0) * u_timeStep;

               // 绘制左上角点
               g_texCoord = vec2(0.0, 1.0);
               gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.5, 0.0, 0.0);
               EmitVertex();

               // 绘制左下角点
               g_texCoord = vec2(0.0, 0.0);
               gl_Position = gl_in[0].gl_Position + vec4(-0.5, -0.5, 0.0, 0.0);
               EmitVertex();

               // 绘制右上角点
               g_texCoord = vec2(1.0, 1.0);
               gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.5, 0.0, 0.0);
               EmitVertex();

               // 绘制右上角点
               g_texCoord = vec2(1.0, 1.0);
               gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.5, 0.0, 0.0);
               EmitVertex();

               // 绘制左下角点
               g_texCoord = vec2(0.0, 0.0);
               gl_Position = gl_in[0].gl_Position + vec4(-0.5, -0.5, 0.0, 0.0);
               EmitVertex();

               // 绘制右下角点
               g_texCoord = vec2(1.0, 0.0);
               gl_Position = gl_in[0].gl_Position + vec4(0.5, -0.5, 0.0, 0.0);
               EmitVertex();

               EndPrimitive();
           }
       )");
   }

   QString fragmentShaderCode() const {
       return QString(R"(
           #version 330

           uniform sampler2D u_texture;

           in vec2 g_texCoord;

           out vec4 o_color;

           void main()
           {
               vec4 texel = texture(u_texture, g_texCoord);
               o_color = vec4(texel.rgb, 1.0) * texel.a;
           }
       )");
   }

private slots:
   void onTimeout() {
       QVector<QVector3D> positions(10000);
       QVector<float> times(10000);
       for (int i = 0; i < 10000; ++i) {
           positions[i] = QVector3D(static_cast<float>(rand()) / RAND_MAX * 2.0f - 1.0f,
                                     static_cast<float>(rand()) / RAND_MAX * 2.0f - 1.0f,
                                     static_cast<float>(rand()) / RAND_MAX * 2.0f - 1.0f);
           times[i] = static_cast<float>(rand()) / RAND_MAX;
       }

       m_vao.bind();
       m_positionBuffer.bind();
       m_positionBuffer.allocate(positions.constData(), positions.size() * sizeof(QVector3D));
       m_program->setAttributeBuffer("a_position", GL_FLOAT, 0, 3);
       m_program->enableAttributeArray("a_position");
       m_positionBuffer.release();

       m_timeBuffer.bind();
       m_timeBuffer.allocate(times.constData(), times.size() * sizeof(float));
       m_program->setAttributeBuffer("a_time", GL_FLOAT, 0, 1);
       m_program->enableAttributeArray("a_time");
       m_timeBuffer.release();

       m_vao.release();

       update();
   }

private:
   QTimer* m_timer;
   QOpenGLShaderProgram* m_program;
   QOpenGLVertexArrayObject m_vao;
   QOpenGLBuffer m_positionBuffer;
   QOpenGLBuffer m_timeBuffer;
   float m_offset;
};


该Demo继承自QOpenGLWidget,实现了一个用于显示眼图的控件。在该控件中,首先通过随机生成三维坐标和时间,
来构造眼图中每个点的位置和出现时间。然后,使用OpenGL的点精灵技术,在眼图上绘制每个点,并使用随机生成的
时间偏移量来模拟眼图的动态效果。通过定时器不断刷新数据,在控件中实现眼图的动态效果。

在绘制眼图时,我们使用OpenGL的几何着色器来对每个点进行处理,并使用纹理来控制点的颜色和大小。代码中,我
们通过调用QOpenGLFunctions类中的函数来初始化OpenGL环境,并实现了三个OpenGL事件函数,分别是initializeGL、
resizeGL和paintGL。在paintGL函数中,我们使用QMatrix4x4类来设置模型视图矩阵和投影矩阵,使用QVector3D
和QVector<float>来保存眼图点的位置和出现时间,并通过OpenGL的点精灵技术来将所有点绘制到屏幕上。我们还使
   用定时器在onTimeout函数中不断刷新眼图数据,实现眼图的动态效果。