将QML的ShaderEffect剪辑为圆形

时间:2015-07-22 14:35:40

标签: qt opengl qml qt5 qtquick2

我在QML中使用ShaderEffect来获得某个项目的缩放视觉副本。 此副本应该是可移动且动态的(live属性ShaderEffectSource设置为true。)

我的问题是我希望它在一轮Rectangle内,但它不会被它剪掉。 ShaderEffect只是与其父级重叠,并且是二次的。

我编写了一个显示问题的快速QML示例:

import QtQuick 2.4
import QtQuick.Controls 1.3

ApplicationWindow {
    title: qsTr("Hello Shaders")
    width: 640
    height: 480
    visible: true
    color: "green"

    Rectangle {
        id: exampleRect

        property bool redState: false

        anchors.centerIn: parent
        width: parent.width / 3
        height: parent.height / 3
        color: redState ? "red" : "cyan"


        Rectangle {
            anchors.centerIn: parent
            width: parent.width / 2; height: width;
            color: "white"

            Rectangle {
                anchors.centerIn: parent
                width: parent.width / 2; height: width;
                color: "green"

                Rectangle {
                    anchors.centerIn: parent
                    width: parent.width / 2; height: width;
                    color: "yellow"
                }
            }
        }

        Timer {
            interval: 2000
            repeat: true
            running: true

            onTriggered: {
                exampleRect.redState = !exampleRect.redState
            }
        }
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onPositionChanged: {
            shaderEffectContainer.x = mouse.x
            shaderEffectContainer.y = mouse.y
        }
    }

    Rectangle {
        id: shaderEffectContainer

        width: 100; height: width;
        radius: width / 2;
        border.width: 2

        ShaderEffectSource {
            id: source

            sourceItem: exampleRect
            visible: false
        }

        ShaderEffect {
            anchors.fill: parent

            property variant source: source

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

            fragmentShader: "
                varying highp vec2 qt_TexCoord0;
                uniform sampler2D source;
                uniform lowp float qt_Opacity;
                void main() {
                    gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
                }"
        }
    }
}

如您所见,exampleRect被宣布为圆形,但它的孩子与它重叠。

我已经尝试了clip属性的所有可能组合,并且花了一整天时间尝试使用片段/顶点着色器。如你所想,没有运气。 :)

1 个答案:

答案 0 :(得分:4)

我已使用layers解决了问题,如this answer所示。
感谢@DenimPowell的提示。

以下是包含循环ShaderEffect的更新示例代码。

import QtQuick 2.4
import QtQuick.Controls 1.3

ApplicationWindow {
    title: qsTr("Hello Shaders")
    width: 640
    height: 480
    visible: true
    color: "green"

    Rectangle {
        id: exampleRect

        property bool redState: false

        anchors.centerIn: parent
        width: parent.width / 3
        height: parent.height / 3
        color: redState ? "red" : "cyan"


        Rectangle {
            anchors.centerIn: parent
            width: parent.width / 2; height: width;
            color: "white"

            Rectangle {
                anchors.centerIn: parent
                width: parent.width / 2; height: width;
                color: "green"

                Rectangle {
                    anchors.centerIn: parent
                    width: parent.width / 2; height: width;
                    color: "yellow"
                }
            }
        }

        Timer {
            interval: 2000
            repeat: true
            running: true

            onTriggered: {
                exampleRect.redState = !exampleRect.redState
            }
        }
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onPositionChanged: {
            shaderEffectContainer.x = mouse.x
            shaderEffectContainer.y = mouse.y
        }
    }

    Rectangle {
        id: shaderEffectContainer

        width: 100; height: width;

        color: "transparent"

        Rectangle {
            id: rectangleSource

            anchors.fill: parent

            ShaderEffectSource {
                id: source

                sourceItem: exampleRect
                visible: false
            }

            ShaderEffect {
                anchors.fill: parent

                property variant source: source

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

                fragmentShader: "
                    varying highp vec2 qt_TexCoord0;
                    uniform sampler2D source;
                    uniform lowp float qt_Opacity;
                    void main() {
                        gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
                    }"
            }
            visible: false

            layer.enabled: true
        }

        Rectangle {
            id: maskLayer
            anchors.fill: parent
            radius: parent.width / 2

            color: "red"

            border.color: "black"

            layer.enabled: true
            layer.samplerName: "maskSource"
            layer.effect: ShaderEffect {

                property var colorSource: rectangleSource
                fragmentShader: "
                    uniform lowp sampler2D colorSource;
                    uniform lowp sampler2D maskSource;
                    uniform lowp float qt_Opacity;
                    varying highp vec2 qt_TexCoord0;
                    void main() {
                        gl_FragColor =
                            texture2D(colorSource, qt_TexCoord0)
                            * texture2D(maskSource, qt_TexCoord0).a
                            * qt_Opacity;
                    }
                "
            }
        }

        // only draw border line
        Rectangle {
            anchors.fill: parent

            radius: parent.width / 2

            border.color: "black"
            border.width: 1

            color: "transparent"
        }
    }
}