QML使用ShaderEffect绘制环形进度条

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;
            }"

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值