既方便又强大的QML Scene Graph

作者:殷允桥 | Mar 23, 2011 2:08:09 PM

原文链接: [Kim Kalland](http://labs.qt.nokia.com/author/kkalland/)-[The convenient power of QML Scene Graph](http://labs.qt.nokia.com/2011/03/22/the-convenient-power-of-qml-scene-graph/)

基于QGraphicsView之上的QML在功能上仅限于那些绝对必要且速度可以足够快绘制美观用户界面的部分。所以,那些会潜在地影响绘制速度的功能,比如:组合模式、QPainterPaths、QGraphicsEffects和复杂的画笔等都是不被支持的。因为我们一直致力于使大家可以更加容易地创建快速响应而不是缓慢呆滞的用户界面。

但与此同时,我们也正在使用OpenGL技术打造[基于](http://labs.qt.nokia.com.cn/2011/02/28/qt-scene-graph-round-2/)[Scene Graph](http://labs.qt.nokia.com/2010/05/18/a-qt-scenegraph/)技术的[QML](http://labs.qt.nokia.com.cn/2011/02/09/qml-scene-graph-has-moved/)[后端](http://labs.qt.nokia.com.cn/2011/02/28/velvet-and-the-qml-scene-graph/),一些在QGraphicsView上面太慢的功能现在由于受益于硬件加速而在Scene Graph之上成为可能。比如,QGraphicsEffects所实现的过滤器功能 (模糊、色彩化、下拉阴影框以及群组透明等)现在已经可以使用片段着色器来实现了。而像翻页特效(Page curl)和精灵特效(Genie Effect)之类的也可以通过顶点着色器来实现。通过在C++中枯燥地实现一些着色器特效,并从[Qt/3D](http://doc.qt.nokia.com/qt3d-snapshot/qml-shaderprogram.html)借来一些灵感,我们增加了一个新的ShaderEffectItem的QML元素,这东西会让实现着色特效变得非常容易。

所谓ShaderEffectItem只不过就是一个简单的带着定制化顶点和片段着色器的矩形。传给顶点着色器的顶点来自于通过定义在元素中的“width”和“height”属性计算出的矩形的角 (还有一个属性可以让你来把矩形切分成多个行和列,不过默认情况下,只有4个顶点)。ShaderEffectItem中一个特别有意思的地方是,你可以在QML元素上定义自己的属性,然后这些属性就可以在着色器的代码中做为 uniform变量使用。比如,你可以定义一个“tint”类型的“color”属性,然后在QML中进行动态变化,在着色器代码中,这个属性可以做为“uniform vec4 tint”类型来读取。这一特性支持的类型包括:real、point、size、color、vector3d以及一个新类型:ShaderEffectSource,这个类型对应着色器代码中的“sampler2D”类型。ShaderEffectSource可以把一个QML元素渲染成一个OpenGL材质,然后传给ShaderEffectItem。ShaderEffectSource有一些独有属性比如包装模式(wrap mode)和MIP贴图([mipmapping](http://en.wikipedia.org/wiki/Mipmap)),以及需要渲染的QML元素的引用。同时多个ShaderEffectItem元素还可以通过让一个做为另一个的渲染源的方式串接起来。ShaderEffectItem是个很强大的功能,不过正如所有其它强大的事物一样,你要小心使用它。一个写得很糟糕的着色器或者一次性激活太多的特效,将会很容易地大幅降低性能,至少是在移动或者嵌入式设备上是如此。

下面的代码显示了以上这个晃动特效是如何实现的:

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));
}"
}
}

相关源代码请访问代码仓库[http://qt.gitorious.org/+qt-developers/qt/staging/](http://qt.gitorious.org/+qt-developers/qt/staging)上的qml-team/qtquick2分支。