剧幕效果(Curtain Effect)

在最后的自定义效果例子中,我们将带来一个剧幕效果。这个效果是2011年5月Qt实验室发布的着色器效果中的一部分。目前网址已经转到blog.qt.digia.com,不知道还能不能找到。

剧幕效果(Curtain Effect) - 图1

当时我非常喜欢这些效果,剧幕效果是我最喜爱的一个。我喜欢剧幕打开然后遮挡后面的背景对象。

我将代码移植适配到Qt5上,这非常简单。同时我做了一些简化让它能够更好的展示。如果你对整个例子有兴趣,可以访问Qt实验室的博客。

只有一个小组件作为背景,剧幕实际上是一张图片,叫做fabric.jpg,它是ShaderEffect的资源。整个效果使用顶点着色器来摆动剧幕,使用片段着色器提供阴影的效果。下面是一个简单的图片,让你更加容易理解代码。

剧幕效果(Curtain Effect) - 图2

剧幕的波形阴影通过一个在剧幕宽度上的sin曲线使用7的振幅来计算(7*PI=221.99..)另一个重要的部分是摆动,当剧幕打开或者关闭时,使用动画来播放剧幕的topWidth。bottomWidth使用SpringAnimation来跟随topWidth变化。这样我们就能创建出底部摆动的剧幕效果。计算得到的swing提供了摇摆的强度,用来对顶点的y值进行插值。

剧幕效果放在CurtainEffect.qml组件中,fabric图像作为纹理资源。在阴影的使用上没有新的东西加入,唯一不同的是在顶点着色器中操作gl_Postion和片段着色器中操作gl_FragColor。

  1. import QtQuick 2.0
  2. ShaderEffect {
  3. anchors.fill: parent
  4. mesh: GridMesh {
  5. resolution: Qt.size(50, 50)
  6. }
  7. property real topWidth: open?width:20
  8. property real bottomWidth: topWidth
  9. property real amplitude: 0.1
  10. property bool open: false
  11. property variant source: effectSource
  12. Behavior on bottomWidth {
  13. SpringAnimation {
  14. easing.type: Easing.OutElastic;
  15. velocity: 250; mass: 1.5;
  16. spring: 0.5; damping: 0.05
  17. }
  18. }
  19. Behavior on topWidth {
  20. NumberAnimation { duration: 1000 }
  21. }
  22. ShaderEffectSource {
  23. id: effectSource
  24. sourceItem: effectImage;
  25. hideSource: true
  26. }
  27. Image {
  28. id: effectImage
  29. anchors.fill: parent
  30. source: "assets/fabric.jpg"
  31. fillMode: Image.Tile
  32. }
  33. vertexShader: "
  34. attribute highp vec4 qt_Vertex;
  35. attribute highp vec2 qt_MultiTexCoord0;
  36. uniform highp mat4 qt_Matrix;
  37. varying highp vec2 qt_TexCoord0;
  38. varying lowp float shade;
  39. uniform highp float topWidth;
  40. uniform highp float bottomWidth;
  41. uniform highp float width;
  42. uniform highp float height;
  43. uniform highp float amplitude;
  44. void main() {
  45. qt_TexCoord0 = qt_MultiTexCoord0;
  46. highp vec4 shift = vec4(0.0, 0.0, 0.0, 0.0);
  47. highp float swing = (topWidth - bottomWidth) * (qt_Vertex.y / height);
  48. shift.x = qt_Vertex.x * (width - topWidth + swing) / width;
  49. shade = sin(21.9911486 * qt_Vertex.x / width);
  50. shift.y = amplitude * (width - topWidth + swing) * shade;
  51. gl_Position = qt_Matrix * (qt_Vertex - shift);
  52. shade = 0.2 * (2.0 - shade ) * ((width - topWidth + swing) / width);
  53. }"
  54. fragmentShader: "
  55. uniform sampler2D source;
  56. varying highp vec2 qt_TexCoord0;
  57. varying lowp float shade;
  58. void main() {
  59. highp vec4 color = texture2D(source, qt_TexCoord0);
  60. color.rgb *= 1.0 - shade;
  61. gl_FragColor = color;
  62. }"
  63. }

这个效果在curtaindemo.qml文件中使用。

  1. import QtQuick 2.0
  2. Rectangle {
  3. id: root
  4. width: 480; height: 240
  5. color: '#1e1e1e'
  6. Image {
  7. anchors.centerIn: parent
  8. source: 'assets/wiesn.jpg'
  9. }
  10. CurtainEffect {
  11. id: curtain
  12. anchors.fill: parent
  13. }
  14. MouseArea {
  15. anchors.fill: parent
  16. onClicked: curtain.open = !curtain.open
  17. }
  18. }

剧幕效果通过自定义的open属性打开。我们使用了一个MouseArea来触发打开和关闭剧幕。