The convenient power of QML Scene Graph

QML on QGraphicsView is limited to only features that are fast enough and absolutely necessary to make nice UIs. Potentially slow features like composition modes, QPainterPaths, QGraphicsEffects and complex brushes are not supported. We have been trying to make it easy for you to create fast and responsive UIs, and hard to create sluggish ones.

However, we are working on a new scene graph based backend for QML using OpenGL, and certain things that were too slow on QGraphicsView are now possible on scene graph thanks to hardware acceleration. For instance, the filters implemented by QGraphicsEffects (blur, colourize, drop shadow and group opacity) could now be implemented with fragment shaders. Page curl and genie effects could be implemented with vertex shaders. After tediously implementing some shader effects in C++, we were inspired by Qt/3D and added a new QML item, the ShaderEffectItem, which made implementing shader effects a whole lot easier.

The ShaderEffectItem is simply a rectangle with a custom vertex and fragment shader. The vertices passed to the vertex shader are the corners of the rectangle defined by the "width" and "height" properties of the item. (There is a property you can set to divide the rectangle into multiple rows and columns, but by default, you get four vertices.) The nifty thing about the ShaderEffectItem, though, is that you can define your own properties on the QML item, and they will be available to you as uniform variables in the shaders. For instance, you can define a property called "tint" of type "color" and animate it in QML, and you can read it in the shaders as "uniform vec4 tint". Several types are supported: real, point, size, color, vector3d, and a new type, ShaderEffectSource, which maps to a "sampler2D" in the shader code. The ShaderEffectSource allows you to render a QML item into a texture and pass it to the ShaderEffectItem. The ShaderEffectSource has properties like wrap mode and mipmapping, and a reference to the QML item to render into the texture. ShaderEffectItems can be strung together by letting one ShaderEffectItem be the source of another. The ShaderEffectItem is powerful, and as with anything powerful, you need to use it with caution. A poorly written shader or too many effects active at once can easily ruin the performance, well, on devices at least.

The code below shows how the wobble effect can be implemented.

import QtQuick 2.0

Image { width: 180 height: 180 source: "winter.jpg" Text { id: theItem anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: 120 font.family: "Times" color: "blue" text: "Qt" } ShaderEffectItem { anchors.fill: parent property variant source: ShaderEffectSource { sourceItem: theItem smooth: true hideSource: true } property real amplitude: 0.02 property real frequency: 20 property real time: 0 NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 } fragmentShader: " uniform highp float amplitude; uniform highp float frequency; uniform highp float time; uniform sampler2D source; uniform lowp float qt_Opacity; varying highp vec2 qt_TexCoord0; void main() { highp vec2 p = sin(time + frequency * qt_TexCoord0); gl_FragColor = qt_Opacity * texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)); }" } }

The source code can be found in the qml-team/qtquick2 branch of http://qt.gitorious.org/+qt-developers/qt/staging/.


Blog Topics:

Comments