Wave Effect

In this more complex example, we will create a wave effect with the fragment shader. The waveform is based on the sinus curve and it influences the texture coordinates used for the color.

The qml file defines the properties and animation.

  1. import QtQuick 2.5
  2. Rectangle {
  3. width: 480; height: 240
  4. color: '#1e1e1e'
  5. Row {
  6. anchors.centerIn: parent
  7. spacing: 20
  8. Image {
  9. id: sourceImage
  10. width: 160; height: width
  11. source: "../assets/coastline.jpg"
  12. }
  13. ShaderEffect {
  14. width: 160; height: width
  15. property variant source: sourceImage
  16. property real frequency: 8
  17. property real amplitude: 0.1
  18. property real time: 0.0
  19. NumberAnimation on time {
  20. from: 0; to: Math.PI*2; duration: 1000; loops: Animation.Infinite
  21. }
  22. fragmentShader: "wave.frag.qsb"
  23. }
  24. }
  25. }

The fragment shader takes the properties and calculates the color of each pixel based on the properties.

  1. #version 440
  2. layout(location=0) in vec2 qt_TexCoord0;
  3. layout(location=0) out vec4 fragColor;
  4. layout(std140, binding=0) uniform buf {
  5. mat4 qt_Matrix;
  6. float qt_Opacity;
  7. float frequency;
  8. float amplitude;
  9. float time;
  10. } ubuf;
  11. layout(binding=1) uniform sampler2D source;
  12. void main() {
  13. vec2 pulse = sin(ubuf.time - ubuf.frequency * qt_TexCoord0);
  14. vec2 coord = qt_TexCoord0 + ubuf.amplitude * vec2(pulse.x, -pulse.x);
  15. fragColor = texture(source, coord) * ubuf.qt_Opacity;
  16. }

The wave calculation is based on a pulse and the texture coordinate manipulation. The pulse equation gives us a sine wave depending on the current time and the used texture coordinate:

  1. vec2 pulse = sin(ubuf.time - ubuf.frequency * qt_TexCoord0);

Without the time factor, we would just have a distortion but not a traveling distortion like waves are.

For the color we use the color at a different texture coordinate:

  1. vec2 coord = qt_TexCoord0 + ubuf.amplitude * vec2(pulse.x, -pulse.x);

The texture coordinate is influenced by our pulse x-value. The result of this is a moving wave.

image

In this example we use a fragment shader, meaning that we move the pixels inside the texture of the rectangular item. If we wanted the entire item to move as a wave we would have to use a vertex shader.