Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. How can I address the anti-aliasing effect in QML when using a shader effect?
Forum Updated to NodeBB v4.3 + New Features

How can I address the anti-aliasing effect in QML when using a shader effect?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
qmlshadereffectantialiasing
1 Posts 1 Posters 255 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    AROH
    wrote on last edited by
    #1

    Hi,

    I'm using a shader effect for my speed gauge implementation, and it works perfectly on the desktop application without any anti-aliasing issues. However, when I run the same code on an embedded device (like a touchscreen), I notice pixel breaks at the edges in certain areas. How can I fix this? Below is a version of my code for reference.

    main.qml

    import QtQuick 2.15
    import QtQuick.Window
    import QtQuick.Shapes 1.7
    
    Window {
        id: root
    
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    
        property int rpm: 0
        property real normalizedRPMValue: (root.rpm <= 10000 ? root.rpm : 10000) / 10000
    
        Behavior on rpm {
            NumberAnimation {
                duration: 200
            }
        }
    
        Timer {
            interval: 1000
            running: true
            repeat: true
            onTriggered: {
                if (rpm < 10000) {
                    rpm = rpm + 1000
                } else {
                    rpm = 0
                }
            }
        }
    
        // Main background
        Image {
            source: "qrc:/bkgd.png"
            anchors.centerIn: parent
    
        }
        // Blue fill
        Image {
            id: blueFill
            source: "qrc:/sweep.png"
            anchors.centerIn: parent
            visible: false
        }
        // Filled marks
        Image {
            id: rpmMarksFilled
            source: "qrc:/marks-filled.png"
            anchors.centerIn: parent
            visible: false
        }
        // Unfilled marks
        Image {
            id: rpmMarksUnfilled
            source: "qrc:/marks-unfilled.png"
            anchors.centerIn: parent
            visible: false
            width: implicitWidth
            height: implicitHeight
        }
    
        // Layer Effect
        // Note: Hide all the images that are being rendered by Shader
        Item {
            id: fillRough
    
            width: blueFill.width
            height: blueFill.height
            layer.enabled: true
    
            anchors.centerIn: parent
    
            layer.effect: ShaderEffect {
                readonly property real degrees: Math.PI / 180 // used for deg -> rad translations
                readonly property var colorSource: blueFill
                readonly property var unfilledMarksSource: rpmMarksUnfilled
                readonly property var filledMarksSource: rpmMarksFilled
    
                // Properties needed / defined in Fragment shader
                // Shader - Not rotating any images, Cutting the images at startCutAngleRad
                // 219 - c - Angle of 0, -258 - m, y - startCutAngleRad, x -normalizedRPMValue
                // Formula - y = mx + c
                // m is calculated by (y2 - y1)/(x2 - x1) => (-39 - 219) / (1-0) = -258
                readonly property real startCutAngleRad: (219 + ((-258) * root.normalizedRPMValue)) * degrees
                readonly property real textureSizePx: blueFill.width
                readonly property real startCutRadius: 0.45
                readonly property real s: Math.sin(0)
                readonly property real c: Math.cos(0)
                readonly property bool fillVisible: root.rpm > 0
                fragmentShader: "qrc:/tachometerShader.frag.qsb"
            }
        }
    
        // Needle
        Image {
    
            source: "qrc:/sweep-line.png"
            anchors.centerIn: parent
            // Rotate the image to pint to the correct value. y = mx + c
            // -258 - c - Angle of 0, -258 - m, y - rotation, x -normalizedRPMValue
            // Formula - y = mx + c
            // m is calculated by (y2 - y1)/(x2 - x1) => (0 - -258) / (1-0) = 258
            rotation: -258 + (258 * root.normalizedRPMValue)
            visible: root.rpm > 0
        }
    
        // Gradient overlayed on top
        Image {
            source: "qrc:/sweep-fade.png"
            anchors.centerIn: parent
        }
    
        Text {
            anchors.centerIn: parent
            font.pixelSize: 65
            font.bold: true
            text: root.rpm
            color: "black"
        }
    }
    

    Shader.frag

    uniform lowp sampler2D colorSource;
    uniform lowp sampler2D unfilledMarksSource;
    uniform lowp sampler2D filledMarksSource;
    varying highp vec2 qt_TexCoord0;
    uniform lowp float s;
    uniform lowp float c;
    uniform lowp float startCutRadius;
    uniform lowp float textureSizePx;
    uniform lowp float startCutAngleRad;
    uniform lowp float endCutAngleRad;
    uniform lowp float rotRad;
    uniform bool fillVisible;
    
    lowp float atanApprox(lowp float y, lowp float x) {
        lowp float absX = abs(x);
        lowp float absY = abs(y);
        lowp float a = min(absX, absY) / max(absX, absY);
        lowp float s = pow(a, 2.0);
        lowp float r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
        if (absY > absX)
            r = 1.57079637 - r;
        if (x < 0.0)
            r = 3.14159274 - r;
        if (y < 0.0)
            r = -r;
        return r;
    }
    
    void main() {
        // background
        gl_FragColor = texture2D(unfilledMarksSource, qt_TexCoord0);
    
        if (!fillVisible) {
            return;
        }
    
        lowp vec2 srcCoord11 = vec2(
                    qt_TexCoord0.x * 2. - 1.,
                    1. - qt_TexCoord0.y * 2.
                    );
    
        // get polar coordinates of the current pixel
        lowp float curAngle = atanApprox(srcCoord11.y, srcCoord11.x);
        lowp float curRadius = sqrt(srcCoord11.x * srcCoord11.x + srcCoord11.y * srcCoord11.y) + 0.05;
    
        // make bottom-left quadrant compatible with all others
        if(curAngle < -2.) {
            curAngle += 6.28;
        }
    
        // cut out what's not needed
        if(curAngle > startCutAngleRad
                && curRadius < 1.0
                && curRadius > startCutRadius
                && fillVisible) {
            lowp mat2 rotationMatrix2x2 = mat2(c, -s,  s, c);
            lowp vec2 destCoord = srcCoord11 * rotationMatrix2x2;
    
            // convert to back to 0..1 system
            destCoord = vec2(
                        .5 + .5 * destCoord.x,
                        .5 - .5 * destCoord.y);
    
            lowp vec4 fill_color = texture2D(colorSource, destCoord);
    
            // Antialiasing logic
            lowp float edgeDist = 0.05; // Adjust this value for antialiasing strength
            lowp float alpha = smoothstep(4.0, edgeDist, fill_color.a);
    
            gl_FragColor = mix(gl_FragColor, fill_color, alpha);
            lowp vec4 marks_color = texture2D(filledMarksSource, qt_TexCoord0);
    
            gl_FragColor = mix(gl_FragColor, marks_color, marks_color.a * fill_color.a);
        }
    }
    

    I have even tried adding Antialiasing logic inside the shader code but nothing worked
    Please find the reference image for the pixel breaking
    Screenshot from 2024-11-20 12-58-13.png
    Thanks in advance !!
    Regards

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved