以前的项目里,我曾经用qwt绘制曲线.qwt是一种基于qt的第三方插件。用qwt绘制的好处是,在待绘制的样本数较多时(〉=1000),仍然可以在很短的时间内完成绘制。本篇博客介绍一种办法,不再利用qwt,而是直接利用GLSL来实现高速绘制曲线。这个功能封装在类GLCurve中。
头文件:
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QTimer>
class GLCurve : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
GLCurve(QWidget *parent = 0);
~GLCurve();
GLuint m_uiVertLoc;
QOpenGLShaderProgram * m_pProgram;
GLfloat * m_pVertices;
int m_iCount;//数据样本数
bool m_bVisibility;
unsigned int m_uiColor;
double m_dUpperBound;
double m_dLowerBound;
double m_dLeftBound;
double m_dRightBound;
void vSetXBounds(double, double);
void vSetYBounds(double, double);
void vSetVertices(unsigned char *, int);
protected:
void initializeGL();
void paintGL();
void resizeGL(int w, int h);
public slots:
};
cpp文件:
#include "GLCurve.h"
GLCurve::GLCurve(QWidget *parent)
: QOpenGLWidget(parent)
{
m_bVisibility = true;
m_uiColor = 0x000000;
m_iCount = 0;
m_pVertices = NULL;
m_dUpperBound = 255;
m_dLowerBound = 0;
}
GLCurve::~GLCurve()
{
delete m_pProgram;
if(m_pVertices)
{
delete [] m_pVertices;
}
}
void GLCurve::initializeGL()
{
initializeOpenGLFunctions();
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"#version 330\n"
"in vec3 pos;\n"
"uniform mat4 mat4MVP;\n"
"void main()\n"
"{\n"
" gl_Position = mat4MVP * vec4(pos, 1.0);\n"
//纹理的Y方向是从下向上的,而pos.y的正方向是从上向下,所以是1.0 - pos.y / 180.0
"}\n";
vshader->compileSourceCode(vsrc);
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"#version 330\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
" color = vec4(0,0,0,0);\n"//注意,texCoord的值域在0-1之间
"}\n";
fshader->compileSourceCode(fsrc);
m_pProgram = new QOpenGLShaderProgram;
m_pProgram->addShader(vshader);
m_pProgram->addShader(fshader);
m_pProgram->link();
m_pProgram->bind();
m_uiVertLoc = m_pProgram->attributeLocation("pos");
m_pProgram->enableAttributeArray(m_uiVertLoc);
glEnable(GL_DEPTH_TEST);
glClearColor(0.4,0.4,0.4,1);
}
void GLCurve::paintGL()
{
//QMatrix4x4在声明时被默认为单位矩阵
QMatrix4x4 m1, m2, m3, m;
double dYRange = m_dUpperBound - m_dLowerBound;
double dYMid = (m_dUpperBound + m_dLowerBound) / 2;
double dXRange = m_dRightBound - m_dLeftBound;
double dXMid = (m_dLeftBound + m_dRightBound) / 2;
//注释:
// m1.ortho(-dXRange / 2 * 1.3, /*从摄像机开始,沿X轴负方向运动dXRange / 2 * 1.3的距离(世界坐标),作为视景体的左边界*/
// dXRange / 2 * 1.3, /*从摄像机开始,沿X轴正方向运动 dXRange / 2 * 1.3的距离(世界坐标),作为视景体的右边界*/
// 0 - dYRange / 2 * 1.3, /*从摄像机开始,沿Y轴负方向运动dYRange / 2 * 1.3的距离(世界坐标),作为视景体的下边界*/
// dYRange / 2 * 1.3, /*从摄像机开始,沿Y轴正方向运动dYRange / 2 * 1.3的距离(世界坐标),作为视景体的上边界*/
// 0.0f, /*从摄像机开始,沿Z轴正方向运动0距离(世界坐标),作为视景体的前边界*/
// 15.0f/*从摄像机开始,沿Z轴负方向运动15.0的距离(世界坐标),作为视景体的后边界*/);
m1.ortho(-dXRange / 2 * 1.3, dXRange / 2 * 1.3, 0 - dYRange / 2 * 1.3, dYRange / 2 * 1.3, 0.0f, 15.0f);
//注释:
// m2.lookAt(QVector3D(dXMid,dYMid,10)/*摄像机的世界坐标*/, QVector3D(dXMid,dYMid,0)/*图片中心的世界坐标*/, QVector3D(0,10,0));
m2.lookAt(QVector3D(dXMid,dYMid,10), QVector3D(dXMid,dYMid,0), QVector3D(0,10,0));
m = m1 * m2 * m3;
m_pProgram->setUniformValue("mat4MVP", m);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(m_iCount >0)
{
m_pProgram->setAttributeArray(m_uiVertLoc, m_pVertices, 3, 0);
glDrawArrays(GL_LINE_STRIP, 0, m_iCount);
}
}
void GLCurve::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void GLCurve::vSetVertices(unsigned char * arrVertices, int iCount)
{
m_iCount = iCount;
m_pVertices = new GLfloat[iCount * 3];
for(int k = 0; k < iCount; k++)
{
m_pVertices[3* k] = k;
m_pVertices[3* k + 1] = arrVertices[k];
m_pVertices[3 * k + 2] = 0;
}
}
void GLCurve::vSetXBounds(double dLeftBound, double dRightBound)
{
m_dLeftBound = dLeftBound;
m_dRightBound = dRightBound;
}
void GLCurve::vSetYBounds(double dLowerBound, double dUpperBound)
{
m_dUpperBound = dUpperBound;
m_dLowerBound = dLowerBound;
}
调用时,要先设置横向和纵向的表数范围vSetX(Y)Bounds(),然后把原始数据通过vSetVertices()送入GLCurve类:
#include "GLCurve.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GLCurve w;
int iLen = 256;
unsigned char * arr = new unsigned char[iLen];
for(int k = 0; k < iLen; k++)
{
arr[k] = k;
}
w.vSetXBounds(0, iLen - 1);
w.vSetYBounds(0, iLen - 1);
w.vSetVertices(arr, iLen);
w.show();
return a.exec();
}
效果:
程序员可以进一步的在图片上添加X轴和Y轴,方法参见:
https://blog.csdn.net/liji_digital/article/details/78473568
代码可以在我的资源下载https://download.csdn.net/download/liji_digital/10722512