Skip to content

Commit

Permalink
ch10: shaders half done
Browse files Browse the repository at this point in the history
  • Loading branch information
e8johan committed Sep 20, 2021
1 parent 62c5afe commit c6797af
Show file tree
Hide file tree
Showing 44 changed files with 1,381 additions and 984 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,6 @@ examples.tar.gz
# Python
.venv
__pycache__

# Qt Shaders
*.qsb
4 changes: 1 addition & 3 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,13 @@ function ch10Sidebar() {
'/ch09-particles/directed-particles',
'/ch09-particles/affecting-particles',
'/ch09-particles/particle-groups',
'/ch09-particles/summary',
'/ch10-effects/shader-effects',
'/ch10-effects/opengl-shaders',
'/ch10-effects/shader-elements',
'/ch10-effects/fragment-shaders',
'/ch10-effects/wave-effect',
'/ch10-effects/vertex-shader',
'/ch10-effects/curtain-effect',
'/ch10-effects/effect-library',
'/ch09-particles/summary',
]
}
}
Expand Down
150 changes: 21 additions & 129 deletions docs/ch10-effects/curtain-effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,142 +2,34 @@

In the last example for custom shader effects, I would like to bring you the curtain effect. This effect was published first in May 2011 as part of [Qt labs for shader effects](http://labs.qt.nokia.com/2011/05/03/qml-shadereffectitem-on-qgraphicsview/).



![image](./assets/curtain.png)

At that time I really loved these effects and the curtain effect was my favorite out of them. I just love how the curtain opens and hide the background object.

I took the code and adapted it towards Qt 5, which was straightforward. Also, I did some simplifications to be able to use it better for a showcase. So if you are interested in the full example, please visit the lab’s blog.

Just a little bot for the background, the curtain is actually an image called *fabric.jpg* and it is the source for a shader effect. The effect uses the vertex shader to swing the curtain and uses the fragment shader to provide some shades. Here is a simple diagram to make you hopefully better understand the code.
For this chapter, the effect has been adapted for Qt 6. It has also been slghtly simplifed to make it a better showcase.

The curtain image is called `fabric.png`. The effect then uses a vertex shader to swing the curtain forth and back and a fragment shader to apply shadows to show how the fabric folds.

The diagram below shows how the shader works. The waves are computed through a sin curve with 7 periods (7\*PI=21.99…). The other part is the swinging. The `topWidht` of the curtain is animated when the curtain is opened or closed. The `bottomWidth` follows the `topWidth` using a `SpringAnimation`. This creates the effect of the bottom part of the curtain swinging freely. The calulated `swing` component is the strengh of the swing based on the y-component of the vertexes.

![image](./assets/curtain_diagram.png)

The waved shades of the curtain are computed through a sin curve with 7 up/downs (7\*PI=21.99…) on the width of the curtain. The other important part is the swing. The *topWidth* of the curtain is animated when the curtain is opened or closed. The *bottomWidth* follows the *topWidth* with a *SpringAnimation*. By this, we create the effect of the swinging bottom part of the curtain. The calculated *swing* provides the strength of this swing interpolated over the y-component of the vertexes.

The curtain effect is located in the `CurtainEffect.qml` component where the fabric image act as the texture source. There is nothing new on the use of shaders here, only a different way to manipulate the *gl_Position* in the vertex shader and the *gl_FragColor* in the fragment shader.

```qml
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;
}
"
}
```

The effect is used in the `curtaindemo.qml` file.

```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
}
}
```

The curtain is opened through a custom *open* property on the curtain effect. We use a *MouseArea* to trigger the opening and closing of the curtain.
The curtain effect is implemed in the `CurtainEffect.qml` file where the fabric image act as the texture source. In the QML code, the `mesh` property is adjusted to make sure that the number of vertixes is increased to give a smoother result.

<<< @/docs/ch10-effects/src/effects/curtain/CurtainEffect.qml#M1

The vertex shader, shown below, reshapes the curtain based on the `topWidth` and `bottomWidth` properies, extrapolating the shift based on the y-coordinate. It also calculates the `shade` value, which is used in the fragment shader. The `shade` property is passed through an additional output in `location` 1.

<<< @/docs/ch10-effects/src/effects/curtain/curtain.vert#M1

In the fragment shader below, the `shade` is picked up as an input in `location` 1 and is then used to calculate the `fragColor`, which is used to draw the pixel in question.

<<< @/docs/ch10-effects/src/effects/curtain/curtain.frag#M1

The combination of QML animations and passing variables from the vertex shader to the fragment shader demonstrates how QML and shaders can be used together to build complex, animated, effects.

The effect itself is used from the `curtaindemo.qml` file shown below.

<<< @/docs/ch10-effects/src/effects/curtain/curtaindemo.qml#M1

The curtain is opened through a custom `open` property on the curtain effect. We use a `MouseArea` to trigger the opening and closing of the curtain when the user clicks or taps the area.
97 changes: 0 additions & 97 deletions docs/ch10-effects/effect-library.md

This file was deleted.

Loading

0 comments on commit c6797af

Please sign in to comment.