How can I address the anti-aliasing effect in QML when using a shader effect?
Unsolved
QML and Qt Quick
-
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
Thanks in advance !!
Regards