接上次的环形进度条练习:环形进度条
1.实现思路在上次环形进度条的基础上稍加改进,通过调节着色器颜色绘制的波浪进度球。
文本绘制:使用 Qt 自带的 QPainter 绘制,由于是平面图,没有自己做纹理或者字体。
抗锯齿:还是使用的 smoothstep 算过渡值,这也导致 shader 代码有点臃肿。
圆形绘制:根据坐标到中心距离来填充不同的颜色,波浪也是根据幅度来填充不同的颜色,后面打算用几何着色器改一改。
存在的两个问题:
1.1.这里面有个比较麻烦的就是多个色块之间的抗锯齿,目前还没完美解决。因为我只有一个片元着色器程序,绘制也是一次完成的,没法先画到一个图上,别的图层再叠加上去。
1.2.目前很多属性没有导出,比如波浪幅度,周期长度,移动速度等等,也没有可配置的颜色表。
2.实现代码(项目 git 链接:https://github.com/gongjianbo/EasyOpenGL2D)
实现效果(GIF):
主要实现代码:
#ifndef WAVEPROGRESSBAR_H
#define WAVEPROGRESSBAR_H
#include
#include
#include
#include
#include
#include
#include
//龚建波:进度球
class WaveProgressBar : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
Q_OBJECT
Q_PROPERTY(double drawValue READ getDrawValue WRITE setDrawValue)
public:
explicit WaveProgressBar(QWidget *parent = nullptr);
~WaveProgressBar();
void setRange(double min,double max);
void setValue(double value);
double getDrawValue() const;
void setDrawValue(double value);
protected:
//设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
void initializeGL() override;
//渲染OpenGL场景,每当需要更新小部件时使用
void paintGL() override;
//设置OpenGL视口、投影等,每当尺寸大小改变时调用
void resizeGL(int width, int height) override;
private:
//着色器程序
QOpenGLShaderProgram shaderProgram;
//顶点数组对象
QOpenGLVertexArrayObject vao;
//顶点缓冲
QOpenGLBuffer vbo;
//属性动画
QPropertyAnimation *animation;
//进度值
double progressMin=0;
double progressMax=100;
double progressValue=0; //设置的值
double progressDraw=0; //绘制临时值
//定时动画
QTimer *timer;
int timeValue=0;
};
#endif // WAVEPROGRESSBAR_H
#include "WaveProgressBar.h"
#include
#include
WaveProgressBar::WaveProgressBar(QWidget *parent)
: QOpenGLWidget(parent)
{
animation=new QPropertyAnimation(this,"drawValue");
animation->setDuration(2000); //动画持续时间
animation->setEasingCurve(QEasingCurve::OutQuart); //先快后慢
timer=new QTimer(this);
connect(timer,&QTimer::timeout,this,[this]{
if(isHidden())
return;
//暂时没有考虑周期
timeValue+=2;
if(timeValue>=360)
timeValue=0;
update();
});
timer->start(30);
}
WaveProgressBar::~WaveProgressBar()
{
makeCurrent();
vbo.destroy();
vao.destroy();
doneCurrent();
}
void WaveProgressBar::setRange(double min, double max)
{
if(progressMaxsetStartValue(progressDraw);
animation->setEndValue(progressValue);
animation->start();
}
double WaveProgressBar::getDrawValue() const
{
return progressDraw;
}
void WaveProgressBar::setDrawValue(double value)
{
progressDraw=value;
update();
}
void WaveProgressBar::initializeGL()
{
//为当前上下文初始化OpenGL函数解析
initializeOpenGLFunctions();
//着色器代码
//in输入,out输出,uniform从cpu向gpu发送
//[aPos]两个三角的顶点数据
//[thePos]表示当前像素点
const char *vertex_str=R"(#version 330 core
layout (location = 0) in vec2 aPos;
out vec2 thePos;
void main()
{
gl_Position = vec4(aPos, 0.0, 1.0);
thePos = aPos;
})";
//GLSL的atan2也叫atan,不过参数不同,我们封装一个0-360度的归一化值[0,1]的版本
//[FragColor]该点输出颜色,gl_FragColor在3移除了,自己声明一个
//[aValue]进度值
//[aTime]时间偏移
//[aSmoothWidth]平滑过渡宽度
const char *fragment_str=R"(#version 330 core
#define PI 3.14159265
uniform float aValue;
uniform float aTime;
uniform float aSmoothWidth;
in vec2 thePos;
out vec4 FragColor;
float myatan2(float y,float x)
{
float ret_val = 0.0;
if(x != 0.0){
ret_val = atan(y,x);
if(ret_val < 0.0){
ret_val += 2.0*PI;
}
}else{
ret_val = y>0 ? PI*0.5 : PI*1.5;
}
return ret_val/(2.0*PI);
}
void main()
{
float len = abs(sqrt(pow(thePos.x,2.0)+pow(thePos.y,2.0)));
float alpha = smoothstep(0.6+aSmoothWidth,0.6,len);
FragColor = vec4(aValue,0.2,1.0,alpha);
if(alpha0.5) angle_diff=1.0-angle_diff;
float g_smooth = smoothstep(0,aSmoothWidth/2.0,abs(angle_diff-0.25));
if(angle_diff0&&ySmooth20) FragColor.g += 0.2*ySmooth2;
}else if(ySmooth2>0){
FragColor.g += 0.6*ySmooth2;
}
}
})";
//将source编译为指定类型的着色器,并添加到此着色器程序
if(!shaderProgram.addCacheableShaderFromSourceCode(
QOpenGLShader::Vertex,vertex_str)){
qDebug()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?