Search code examples
qtopenglqmlglslfragment-shader

How to slowly show a ShaderEffect in QML?


I have a ShaderEffect here with a red tint, and I want to enable/disable the shader with the click of a button. My background is green, so when the shader is enabled, I want to see a green color with the "Hello" text clearly visible. But from what I've tried, I cannot get there. Either the screen goes completely black when the shader is enabled, or the text is not visible. How can I enable the shader in a way where I see both the green color and the text?

import QtQuick 2.15
import QtQuick.Window 2.2
import QtQuick.Controls 2.15

Window {

    width: 1024
    height: 600
    visible: true

    Rectangle {
        id: rect
        anchors.fill: parent
        color: "green"
        anchors.centerIn: parent

        Text {
            anchors.centerIn: parent
            text: qsTr("Hello")
            font.pixelSize: 20
            color: "black"
        }

        MouseArea {
            anchors.fill: rect
            onClicked: {
                if(shader.opacity > 0) {
                    shader.opacity = 0
                    shader.visible = false
                    console.log("Hiding")
                }
                else {
                    shader.opacity = 1
                    shader.visible = true
                    console.log("Showing")
                }
            }
        }

        ShaderEffect {
            id: shader
            anchors.fill: parent
            property variant source: rect
            Behavior on opacity { PropertyAnimation {} }
            visible: false
            opacity: 0
            fragmentShader: "
                            varying highp vec2 qt_TexCoord0;
                            uniform sampler2D source;
                            uniform lowp float qt_Opacity;
                            void main() {
                                gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(1.0, 0.0, 0.0, 1.0) * qt_Opacity;
                            }"
        }
    }
}


Solution

  • I made some adjustments to your code mainly the nesting that wasn't clear to me. But your issue is that you multiply green vec4(0.0, 1.0, 0.0, 1.0) with red vec4(1.0, 0.0, 0.0, 1.0) which results in black vec4(0.0, 0.0, 0.0, 1.0). This applies also for the font color, black * red = black.

    I also needed to add layer.enabled: true to the Rectangle in order to make it work.

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.15
    
    Window {
        width: 1024
        height: 600
        visible: true
    
        Rectangle {
            id: rect
            anchors.fill: parent
            color: "gray"
            layer.enabled: true
    
            Text {
                anchors.centerIn: parent
                text: qsTr("Hello")
                font.pixelSize: 40
                color: "blue"
            }
        }
    
        MouseArea {
            anchors.fill: parent
            onClicked: {
                if (shader.opacity > 0) {
                    shader.opacity = 0
                    console.log("Hiding")
                } else {
                    shader.opacity = 1
                    console.log("Showing")
                }
            }
        }
    
        ShaderEffect {
            id: shader
            anchors.fill: parent
            property variant source: rect
            Behavior on opacity { PropertyAnimation {} }
            opacity: 0
            fragmentShader: "
                varying highp vec2 qt_TexCoord0;
                uniform sampler2D source;
                uniform lowp float qt_Opacity;
                void main() {
                    gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(1.0, 1.0, 0.0, 1.0) * qt_Opacity;
                }"
        }
    }