#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函数中不断刷新眼图数据,实现眼图的动态效果。