9.5 頂點(diǎn)著色器
頂點(diǎn)著色器可用于操縱著色器效果提供的頂點(diǎn)虫碉。在正常情況下,著色效果有 4 個(gè)頂點(diǎn)(左上角 top-left,右上角 top-right咱士,左下角 bottom-left 和右下角 bottom-righ)赃磨。報(bào)告的每個(gè)頂點(diǎn)都是來自 vec4 的類型咬摇。為了可視化頂點(diǎn)著色器,我們將編寫一個(gè)縮放效果煞躬。這種效果通常用于使矩形窗口區(qū)域縮放到一個(gè)點(diǎn)肛鹏。
** 設(shè)置場景 **
首先我們將再次設(shè)置我們的場景。
import QtQuick 2.5
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Image {
id: sourceImage
width: 160; height: width
source: "assets/lighthouse.jpg"
visible: false
}
Rectangle {
width: 160; height: width
anchors.centerIn: parent
color: '#333333'
}
ShaderEffect {
id: genieEffect
width: 160; height: width
anchors.centerIn: parent
property variant source: sourceImage
property bool minimized: false
MouseArea {
anchors.fill: parent
onClicked: genieEffect.minimized = !genieEffect.minimized
}
}
}
這提供了一個(gè)具有深色背景和使用圖像作為源紋理的著色器效果的場景恩沛。原始圖像在我們的縮放效果產(chǎn)生的圖像上不可見在扰。另外,我們?cè)谂c著色器效果相同的幾何體上添加了一個(gè)黑色矩形雷客,因此我們可以更好地感知我們需要點(diǎn)擊以實(shí)現(xiàn)還原效果的位置芒珠。
縮放效果是通過點(diǎn)擊圖像觸發(fā)的,這是在覆蓋縮放效果的鼠標(biāo)區(qū)域中定義的搅裙。在 onClicked 處理方法中皱卓,我們將自定義布爾屬性 minimized。稍后我們將使用此屬性實(shí)現(xiàn)切換縮放的效果部逮。
** 最小化和恢復(fù)原樣 **
在我們?cè)O(shè)置場景之后娜汁,我們定義一個(gè)類型為 real 的屬性稱為 minimize,該屬性將包含我們最小化的當(dāng)前值兄朋。該值將從 0.0 到 1.0 不等掐禁,并由順序動(dòng)畫進(jìn)行控制。
property real minimize: 0.0
SequentialAnimation on minimize {
id: animMinimize
running: genieEffect.minimized
PauseAnimation { duration: 300 }
NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
SequentialAnimation on minimize {
id: animNormalize
running: !genieEffect.minimized
NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
動(dòng)畫由最小化(minimized)屬性的值引發(fā)÷停現(xiàn)在我們已經(jīng)設(shè)置了所有要準(zhǔn)備的環(huán)境傅事,我們終于可以看看我們的頂點(diǎn)著色器了。
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
uniform highp float minimize;
uniform highp float width;
uniform highp float height;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
pos.x = mix(qt_Vertex.x, width, minimize);
gl_Position = qt_Matrix * pos;
}"
在我們的例子中峡扩,為每個(gè)頂點(diǎn)調(diào)用頂點(diǎn)著色器四次蹭越。提供默認(rèn)的 qt 定義參數(shù),如qt_Matrix教届,qt_Vertex响鹃,qt_MultiTexCoord0,qt_TexCoord0巍佑。我們?cè)缫延懻撨^的變量茴迁。 另外,我們將著色器效果的最小化萤衰,寬度和高度變量鏈接到我們的頂點(diǎn)著色器代碼中堕义。在主函數(shù)中,我們將當(dāng)前紋理坐標(biāo)存儲(chǔ)在我們的 qt_TexCoord0 中,使其可用于片段著色器【肼簦現(xiàn)在我們復(fù)制當(dāng)前位置并修改頂點(diǎn)的 x 和 y 位置:
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
pos.x = mix(qt_Vertex.x, width, minimize);
mix(...) 功能在第 3 個(gè)參數(shù)提供的點(diǎn)(0.0-1.0)上的前 2 個(gè)參數(shù)之間提供線性插值洒擦。所以在我們的例子中,我們根據(jù)當(dāng)前最小化值怕膛,在當(dāng)前 y 位置和高度之間插入 y熟嫩,與 x 類似。請(qǐng)記住褐捻,最小值是由我們的連續(xù)動(dòng)畫控制掸茅,并從 0.0 到 1.0(反之亦然)。
上面所產(chǎn)生的效果不是真正的縮放效果柠逞,但已經(jīng)邁出了我們的第一步昧狮。
** 簡單的彎曲 **
至此我們完成了頂點(diǎn)的 x 和 y 分量的最小化。現(xiàn)在我們要稍微修改 x 操作板壮,并根據(jù)當(dāng)前的 y 值進(jìn)行修改逗鸣。 所需的變化相當(dāng)小。 y 位置如前所述計(jì)算绰精。x 位置的插值現(xiàn)在取決于頂點(diǎn) y 位置:
highp float t = pos.y / height;
pos.x = mix(qt_Vertex.x, width, t * minimize);
這導(dǎo)致當(dāng) y 位置較大時(shí)向 x 方向趨向于寬度撒璧。換句話說,上面的 2 個(gè)頂點(diǎn)根本不受影響笨使,因?yàn)樗鼈兊?y 位置為 0卿樱,而較低的兩個(gè)頂點(diǎn) x 位置都朝向?qū)挾葟澢虼怂鼈兂蛳嗤?x 位置彎曲阱表。
import QtQuick 2.5
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Image {
id: sourceImage
width: 160; height: width
source: "assets/lighthouse.jpg"
visible: false
}
Rectangle {
width: 160; height: width
anchors.centerIn: parent
color: '#333333'
}
ShaderEffect {
id: genieEffect
width: 160; height: width
anchors.centerIn: parent
property variant source: sourceImage
property real minimize: 0.0
property bool minimized: false
SequentialAnimation on minimize {
id: animMinimize
running: genieEffect.minimized
PauseAnimation { duration: 300 }
NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
SequentialAnimation on minimize {
id: animNormalize
running: !genieEffect.minimized
NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
vertexShader: "
uniform highp mat4 qt_Matrix;
uniform highp float minimize;
uniform highp float height;
uniform highp float width;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
// M1>>
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
highp float t = pos.y / height;
pos.x = mix(qt_Vertex.x, width, t * minimize);
gl_Position = qt_Matrix * pos;
** 更好的彎曲效果 **
由于目前的彎曲情況并不令人滿意殿如,我們會(huì)增加幾個(gè)部分來改善情況。首先我們?cè)鰪?qiáng)我們的動(dòng)畫來支持自己的彎曲屬性最爬。這是必要的,因?yàn)閺澢鷳?yīng)該立即發(fā)生门岔,并且 y 最小化應(yīng)該被延遲爱致。兩個(gè)動(dòng)畫的總和相同(300 + 700 + 1000 和 700 + 1300)。
property real bend: 0.0
property bool minimized: false
// change to parallel animation
ParallelAnimation {
id: animMinimize
running: genieEffect.minimized
SequentialAnimation {
PauseAnimation { duration: 300 }
NumberAnimation {
target: genieEffect; property: 'minimize';
to: 1; duration: 700;
easing.type: Easing.InOutSine
}
PauseAnimation { duration: 1000 }
}
// adding bend animation
SequentialAnimation {
NumberAnimation {
target: genieEffect; property: 'bend'
to: 1; duration: 700;
easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
}
另外寒随,為了使彎曲成為平滑曲線糠悯,x 位置上的 y 效應(yīng)不會(huì)由 0..1 的彎曲函數(shù)修改,pos.x 現(xiàn)在取決于新的彎曲屬性動(dòng)畫:
highp float t = pos.y / height;
t = (3.0 - 2.0 * t) * t * t;
pos.x = mix(qt_Vertex.x, width, t * bend);
曲線以 0.0 值開始平滑曲線妻往,然后平穩(wěn)地向 1.0 值增長并停止互艾。以下是指定范圍內(nèi)的功能圖。我們只關(guān)心 0..1 的范圍區(qū)間讯泣。
最直觀的變化是增加我們的頂點(diǎn)數(shù)量纫普。可以使用網(wǎng)格來增加使用的頂點(diǎn):
mesh: GridMesh { resolution: Qt.size(16, 16) }
著色器效果現(xiàn)在具有 16x16 頂點(diǎn)的相等分布式網(wǎng)格好渠,而不是之前使用的 2×2 個(gè)頂點(diǎn)昨稼。這使得頂點(diǎn)之間的插值看起來更加平滑节视。
我們可以看到正在使用的曲線的影響,因?yàn)閺澢Y(jié)束時(shí)很好地平滑假栓。這是彎曲效果最強(qiáng)的地方寻行。
** 選擇一邊 **
作為最終的增強(qiáng),我們希望能夠切換側(cè)面匾荆。一方面拌蜘,縮放效果消失了。到目前為止牙丽,它總是朝著 width 方向消失简卧。通過添加一個(gè) side 屬性,我們可以將其修改成 0 和 width 之間的點(diǎn)剩岳。
ShaderEffect {
...
property real side: 0.5
vertexShader: "
...
uniform highp float side;
...
pos.x = mix(qt_Vertex.x, side * width, t * bend);
"
}
** 打包縮放效果 **
最后要做的是很好地打包我們的效果贞滨。為此,我們將縮放效果代碼提取到一個(gè)名為 GenieEffect 的組件中拍棕。它具有著色器作為根元素晓铆。我們刪除鼠標(biāo)區(qū)域,因?yàn)檫@不應(yīng)該在組件內(nèi)绰播,因?yàn)樾Ч挠|發(fā)可以被最小化(minimized)屬性代替骄噪。
import QtQuick 2.5
ShaderEffect {
id: genieEffect
width: 160; height: width
anchors.centerIn: parent
property variant source
mesh: GridMesh { resolution: Qt.size(10, 10) }
property real minimize: 0.0
property real bend: 0.0
property bool minimized: false
property real side: 1.0
ParallelAnimation {
id: animMinimize
running: genieEffect.minimized
SequentialAnimation {
PauseAnimation { duration: 300 }
NumberAnimation {
target: genieEffect; property: 'minimize';
to: 1; duration: 700;
easing.type: Easing.InOutSine
}
PauseAnimation { duration: 1000 }
}
SequentialAnimation {
NumberAnimation {
target: genieEffect; property: 'bend'
to: 1; duration: 700;
easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
}
ParallelAnimation {
id: animNormalize
running: !genieEffect.minimized
SequentialAnimation {
NumberAnimation {
target: genieEffect; property: 'minimize';
to: 0; duration: 700;
easing.type: Easing.InOutSine
}
PauseAnimation { duration: 1300 }
}
SequentialAnimation {
PauseAnimation { duration: 300 }
NumberAnimation {
target: genieEffect; property: 'bend'
to: 0; duration: 700;
easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
}
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
uniform highp float height;
uniform highp float width;
uniform highp float minimize;
uniform highp float bend;
uniform highp float side;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
highp float t = pos.y / height;
t = (3.0 - 2.0 * t) * t * t;
pos.x = mix(qt_Vertex.x, side * width, t * bend);
gl_Position = qt_Matrix * pos;
}"
}
我們現(xiàn)在可以這樣使用該效果:
import QtQuick 2.5
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
GenieEffect {
source: Image { source: 'assets/lighthouse.jpg' }
MouseArea {
anchors.fill: parent
onClicked: parent.minimized = !parent.minimized
}
}
}
我們通過刪除我們的背景矩形來簡化代碼,我們將圖像直接分配給效果蠢箩,而不是將其加載到獨(dú)立的圖像元素中链蕊。
9.6 帷幕效果
在自定義著色效果的最后一個(gè)例子中,我們想給你帶來帷幕效果谬泌。2011年5月首次發(fā)布此效果作為 Qt 著色器效果實(shí)驗(yàn)室 的一部分滔韵。
當(dāng)時(shí)我(原作者)真的很喜歡這些效果,帷幕效果是我最喜歡的掌实。我特別帷幕怎樣打開和怎樣隱藏背景物體陪蜻。
只是一個(gè)機(jī)器人的背景,窗簾實(shí)際上是一個(gè)名為fabric.jpg的圖像贱鼻,它是著色器效果的來源宴卖。效果使用頂點(diǎn)著色器擺動(dòng)窗簾,并使用片段著色器提供一些陰影邻悬。下面是一個(gè)簡單的圖表症昏,讓我們更好地了解代碼。
窗簾的波浪色調(diào)通過簾幕寬度上的 7 個(gè)上/下(7 * PI = 21.99 ...)的 sin 曲線計(jì)算父丰。另一個(gè)重要的部分是秋千肝谭。 當(dāng)窗簾打開或關(guān)閉時(shí),窗簾的頂部寬度是動(dòng)畫的。bottomWidth 遵循 topWidth 與 SpringAnimation分苇。通過這一點(diǎn)添诉,我們創(chuàng)造了窗簾底部擺動(dòng)的效果。計(jì)算的擺動(dòng)提供了在頂點(diǎn)的 y 分量上內(nèi)插的這種擺動(dòng)的強(qiáng)度医寿。
窗簾效果位于 CurtainEffect.qml 組件中栏赴,其中織物圖像用作紋理源。在這里使用著色器沒有任何新意義靖秩,只是在片段著色器中處理頂點(diǎn)著色器中的 gl_Position 和 gl_FragColor 的不同方法须眷。
import QtQuick 2.5
ShaderEffect {
anchors.fill: parent
mesh: GridMesh {
resolution: Qt.size(50, 50)
}
property real topWidth: open?width:20
property real bottomWidth: topWidth
property real amplitude: 0.1
property bool open: false
property variant source: effectSource
Behavior on bottomWidth {
SpringAnimation {
easing.type: Easing.OutElastic;
velocity: 250; mass: 1.5;
spring: 0.5; damping: 0.05
}
}
Behavior on topWidth {
NumberAnimation { duration: 1000 }
}
ShaderEffectSource {
id: effectSource
sourceItem: effectImage;
hideSource: true
}
Image {
id: effectImage
anchors.fill: parent
source: "assets/fabric.png"
fillMode: Image.Tile
}
vertexShader: "
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
uniform highp mat4 qt_Matrix;
varying highp vec2 qt_TexCoord0;
varying lowp float shade;
uniform highp float topWidth;
uniform highp float bottomWidth;
uniform highp float width;
uniform highp float height;
uniform highp float amplitude;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 shift = vec4(0.0, 0.0, 0.0, 0.0);
highp float swing = (topWidth - bottomWidth) * (qt_Vertex.y / height);
shift.x = qt_Vertex.x * (width - topWidth + swing) / width;
shade = sin(21.9911486 * qt_Vertex.x / width);
shift.y = amplitude * (width - topWidth + swing) * shade;
gl_Position = qt_Matrix * (qt_Vertex - shift);
shade = 0.2 * (2.0 - shade ) * ((width - topWidth + swing) / width);
}"
fragmentShader: "
uniform sampler2D source;
varying highp vec2 qt_TexCoord0;
varying lowp float shade;
void main() {
highp vec4 color = texture2D(source, qt_TexCoord0);
color.rgb *= 1.0 - shade;
gl_FragColor = color;
}"
}
該效果用于 curtaindemo.qml 文件。
import QtQuick 2.5
Item {
id: root
width: background.width; height: background.height
Image {
id: background
anchors.centerIn: parent
source: 'assets/background.png'
}
Text {
anchors.centerIn: parent
font.pixelSize: 48
color: '#efefef'
text: 'Qt5 Cadaques'
}
CurtainEffect {
id: curtain
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: curtain.open = !curtain.open
}
}
窗簾通過窗簾效果的定制 open 屬性打開沟突。我們使用 MouseArea 來觸發(fā)窗簾的打開和關(guān)閉花颗。
9.7 Qt GraphicsEffect 庫
圖形效果庫是著色器效果的集合。準(zhǔn)備由 Qt 開發(fā)商制作惠拭。這是一個(gè)很好的工具集扩劝,可用于我們的應(yīng)用程序,但也是學(xué)習(xí)如何構(gòu)建著色器的重要來源职辅。
圖形效果庫帶有一個(gè)所謂的手動(dòng)測試平臺(tái)棒呛,這是一個(gè)交互式發(fā)現(xiàn)不同效果的好工具。
測試程序位于 $QTDIR/qtgraphicaleffects/tests/manual/testbed 目錄下域携。
效果庫包含約 20 種效果簇秒。效果列表和簡短描述可以在下面找到。
** 圖形效果列表 **
以下是使用 Blur 類別中的 FastBlur 效果的示例:
import QtQuick 2.5
import QtGraphicalEffects 1.0
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 16
Image {
id: sourceImage
source: "assets/tulips.jpg"
width: 200; height: width
sourceSize: Qt.size(parent.width, parent.height)
smooth: true
}
FastBlur {
width: 200; height: width
source: sourceImage
radius: blurred?32:0
property bool blurred: false
Behavior on radius {
NumberAnimation { duration: 1000 }
}
MouseArea {
id: area
anchors.fill: parent
onClicked: parent.blurred = !parent.blurred
}
}
}
}
左邊的圖像是原始圖像秀鞭。單擊右側(cè)的圖像會(huì)切換模糊屬性趋观,并在 1 秒內(nèi)將模糊半徑從 0 到 32 的動(dòng)畫效果。
本章使用的圖片資源:
本章完锋边,歡迎提出建議和指正翻譯問題皱坛。