将QML的ShaderEffect剪辑成圆形
Clip QML's ShaderEffect to circular shape
我在 QML 中使用 ShaderEffect
来获得某些项目的缩放视觉副本。
此副本应该是可移动和动态的(ShaderEffectSource
的 live
属性 设置为 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
属性 的所有可能组合,并花了一整天时间尝试使用 fragment/vertex 着色器来完成此操作。正如您可能猜到的那样,没有运气。 :)
我已经使用 layers as shown in 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"
}
}
}
我在 QML 中使用 ShaderEffect
来获得某些项目的缩放视觉副本。
此副本应该是可移动和动态的(ShaderEffectSource
的 live
属性 设置为 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
属性 的所有可能组合,并花了一整天时间尝试使用 fragment/vertex 着色器来完成此操作。正如您可能猜到的那样,没有运气。 :)
我已经使用 layers as shown in 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"
}
}
}