将 QML 的 ShaderEffect 剪辑为圆形

rsh*_*sht 5 opengl qt qml qt5 qtquick2

ShaderEffect在 QML 中使用一些项目的缩放视觉副本。这个副本应该是可移动的和动态的(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;
                }"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,exampleRect被声明为圆形,但它的孩子正在重叠它。

我已经尝试了所有可能的clip属性组合,并花了一整天的时间尝试使用片段/顶点着色器来做到这一点。正如您可能猜到的那样,没有运气。:)

rsh*_*sht 5

我已经使用图层解决了这个问题,如本答案所示。
感谢@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"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)