I'm using ShaderEffect
in QML to have a scaled visual copy of some Item.
This copy should be moveable and dynamic (live
property of ShaderEffectSource
set to true
).
My problem is that I want it to be inside of a round Rectangle
but it won't be clipped by it. ShaderEffect
just overlaps its parent and is quadratic.
I've coded a quick QML example that shows the problem:
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;
}"
}
}
}
As you can see, exampleRect
is declared to be round, but it's child is overlapping it.
I have already tried all possible combinations of clip
property and have spent all day trying to do this with fragment/vertex shaders. With no luck, as you might guess. :)
I've solved the problem using layers as shown in this answer.
Thanks @DenimPowell for the hint.
Below is an updated example code with circular 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"
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With