Search code examples
qtqmlshaderqtquick2opengl-3

Shading anti-clockwise in QML CircularGaugeStyle using thge fragmentShader property of ShaderEffect


I want to create a CircularGauge in QML in which the needle moves anti-clockwise and the shader is drawn from +145 deg to needle position.

I implemented a CircularGaugeStyle for the style property of CircularGauge. I used the below code to make the shader effect on the gauge:

background: Canvas {
    onPaint: {
        var ctx = getContext("2d");
        ctx.reset();
        paintBackground(ctx);
    }

    ShaderEffect {
        id: gauge
        anchors.fill: parent

        opacity: 0.75

        // Angles measured clockwise from up, in range -pi to pi
        property real angleBase: -2.53073
        property real angle: degToRad(needleRotation)

        vertexShader: "
          uniform highp mat4 qt_Matrix;
          attribute highp vec4 qt_Vertex;
          attribute highp vec2 qt_MultiTexCoord0;
          varying highp vec2 coord;
          void main() {
            coord = qt_MultiTexCoord0;
            gl_Position = qt_Matrix * qt_Vertex;
          }"

        fragmentShader: "
          uniform lowp float qt_Opacity;
          uniform highp float angleBase;
          uniform highp float angle;
          varying highp vec2 coord;
          void main() {
            gl_FragColor = vec4(0.0,0.0,0.0,0.0);
            highp vec2 d=2.0*coord-vec2(1.0,1.0);
            highp float r=length(d);
            if (0.55<=r && r<=0.95) {
              highp float a=atan2(d.x,-d.y);
              if (angleBase<=a && a<=angle) {
                highp float p=(a-angleBase)/(angle-angleBase);
                gl_FragColor = vec4(0.0,0.0,p,p) * qt_Opacity;
              }
            }
          }"
      }
}

In this code the needle moves anti-clock wise setting the min max angle but the shader effect is drawn clock-wise. I changed the following:

property real angleBase: 2.53073 // +145 deg

making the baseAngle to +145 deg.

if (0.55<=r && r<=0.95) { // the area in gauge that should be painted
  highp float a=atan2(d.y,-d.x); //getting the other side of the angle (pi/2 - theta)
  if (a>=angleBase && angle>=a) { //since we want to draw from +2.5 to needle position (in radians)
    highp float p=(angleBase-a)/(angleBase-angle);
    gl_FragColor = vec4(0.0,0.0,p,p) * qt_Opacity;
  }
}

Here is the image of the output with the shader going clock-wise: QML CircularGauge

The change in code to draw anti-clockwise is not working. What am I doing wrong? Is my understanding of calculating the atan2 not correct?


Solution

  • It all depends on how the current angle is computed.

    In your application 0° seems to be at the top. Keep the angle of -145° (-2.53073)

    property real angleBase: -2.53073
    

    In the original code, the angle (a) was computed by a=atan2(d.x,-d.y);. If you want to flip that around the y axis, then you the new computation is:

    highp vec2  d = coord * 2.0 - vec2(1.0);
    highp float r = length(d);
    if (0.55<=r && r<=0.95) {
        
        highp float a = atan2(-d.x, -d.y);
        
        if (angleBase <= a && a <= angle) {
        
            highp float p=(a-angleBase)/(angle-angleBase);
            
            // [...]
        }
    }
    

    In the above algorithm is assumed, that angle is in range [-145°, 145°]. Further more the top left coord is (0, 0) and the bottom right coord is (1, 1)

    If angle is in range [0°, 290°], then alpha has to be suited to the baseAngle:

    if (angleBase <= a && a <= angle) {

    if (angleBase <= a && a <= angle+angleBase ) {
        // [...]
    }