0.前言
之前用 Qt Widgets 的 QOpenGLWidget 类配合着色器写了下环形进度条(https://blog.csdn.net/gongjianbo1992/article/details/106043023),现在用 QML 的 ShaderEffect 重新写下,用的 Qt 默认的 OpenGL 版本,貌似是 OpenGL ES2.0 。
ShaderEffect 是 QML 中用来对 Item 做着色器效果的组件,允许直接在 QML 中编写诸如阴影,模糊,着色和页面卷曲之类的效果。传递参数时,只需要将 ShaderEffect 的 property 声明在 shader 中,该组件也预定了一些属性,详情参考文档:https://doc.qt.io/qt-5/qml-qtquick-shadereffect.html
(也可以参照 QML Book 进行使用 http://qmlbook.github.io/ )
1.实现思路
1.1 圆环我是通过计算距离圆心的距离来渲染的圆环,( glsl 本来有 distance 内建函数,之前没看到)
void main() {
float len = abs(sqrt(thePos.x*thePos.x+thePos.y*thePos.y));
float alpha = 1.0-smoothstep(0.15,0.20,abs(len-0.75));
gl_FragColor = vec4(0.4,0.1,0.6,alpha);
}
1.2 圆环的抗锯齿我使用的 smoothstep 函数:
(这里还没有去做斜线部分的抗锯齿)
1.3 修改值时的动画效果用的 QML 的属性行为(https://blog.csdn.net/gongjianbo1992/article/details/102135779)。
1.4(2021-6-6补充)参照https://bugreports.qt.io/browse/QTBUG-21550
ShaderEffect的输出颜色需要乘上透明度,即
gl_FragColor.rgb *= gl_FragColor.a;
2.实现代码
实现效果图片:
实现代码:
//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("龚建波 1992")
//进度条
MyProgressBar{
id: progress
width: root.width>root.height?root.height/2:root.width/2
height: progress.width
anchors.centerIn: parent
}
Row{
x:10
y:10
spacing: 10
//数字框
SpinBox{
id: spin
from: 0
to: 100
value: 45
editable: true
}
//按钮
Button{
id: btn
text: "set data"
onClicked: {
progress.value=spin.value
}
}
}
Component.onCompleted: {
//初始值
progress.value=spin.value
}
}
import QtQuick 2.12
//自定义的环形进度条组件
//没设置OpenGL版本,可能默认时OpenGL ES2.0吧
ShaderEffect{
id: control
property double min: 0
property double max: 100
property double value: 0
// 给着色器的
property real aValue: (value-min)/(max-min)
// 修改值后触发动画效果
Behavior on aValue{
NumberAnimation{
duration: 1000
}
}
// 上面的文字
Text{
anchors.centerIn: parent
color: "black"
font.pixelSize: 20
text: Number(aValue*100).toFixed(2)+" %"
}
//mat4 qt_Matrix组合的转换矩阵,从根项到此ShaderEffect的矩阵与正交投影的乘积
//vec4 qt_Vertex顶点位置,左上角顶点的位置为(0,0),右下角顶点的位置为(width,height)
//vec2 qt_MultiTexCoord0纹理坐标,左上角坐标为(0,0),右下角坐标为(1,1)
//thePos用来指定位置,标准化设备坐标
//aSmoothWidth用来指定smoothstep的宽度
vertexShader: "
uniform mat4 qt_Matrix;
attribute vec4 qt_Vertex;
attribute vec2 qt_MultiTexCoord0;
varying vec2 thePos;
varying float aSmoothWidth;
uniform float width;
void main() {
thePos = vec2(qt_MultiTexCoord0.x*2.0-1.0,-qt_MultiTexCoord0.y*2.0+1.0);
aSmoothWidth = 3.0/float(width);
gl_Position = qt_Matrix * qt_Vertex;
}"
//float qt_Opacity从根项到此ShaderEffect的不透明度的乘积。
//es2里字面量也得写成和运算变量一样的类型,如浮点数
//myatan2把角度归一化为[0,1]
fragmentShader: "
#define PI 3.14159265
varying vec2 thePos;
varying float aSmoothWidth;
uniform float qt_Opacity;
uniform float aValue;
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;
}
}
return ret_val/(2.0*PI);
}
void main() {
float len = abs(sqrt(pow(thePos.x,2.0)+pow(thePos.y,2.0)));
float alpha = 1.0-smoothstep(0.15,0.15+aSmoothWidth,abs(len-0.75));
float angle = myatan2(thePos.y,thePos.x);
if(angle<aValue){
gl_FragColor = vec4(1.0,0.1,(1.0-angle),alpha);
}else{
gl_FragColor = vec4(0.4,0.1,0.6,alpha);
}
gl_FragColor.rgb *= gl_FragColor.a;
}"
}
(2020-5-14修复bug及改进)
主要是片段着色器部分,一是之前的圆环顶点 x 为 0 时没进行处理,上下会有竖线;二是圆环色条的抗锯齿现在也处理了下:
fragmentShader: "
#define PI 3.14159265
varying vec2 thePos;
varying float aSmoothWidth;
uniform float qt_Opacity;
uniform float aValue;
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.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 = 1.0-smoothstep(0.15,0.15+aSmoothWidth,abs(len-0.75));
float angle = myatan2(thePos.y,thePos.x);
float angle_smooth=1.0-smoothstep(aValue,aValue+aSmoothWidth/3.0,angle);
if(angle_smooth>0.0){
if(angle_smooth>=1.0){
gl_FragColor = vec4(1.0,0.1,(1.0-angle),alpha);
}else{
gl_FragColor = vec4(mix(vec3(1.0,0.1,(1.0-angle)),vec3(0.4,0.1,0.6),1.0-angle_smooth),alpha);
}
}else{
gl_FragColor = vec4(0.4,0.1,0.6,alpha);
}
gl_FragColor.rgb *= gl_FragColor.a;
}"