通过QML的ShaderEffect和ShaderEffectSource元素,我们可以运用OpenGL的能力实现各种各样的渲染效果。着色器的渲染算法是通过OpengGL的渲染语言实现的。这意味着,我们需要实现QML代码和着色器代码的混合编程,在程序执行的时候对应的着色器程序会被发送到GPU上编译运行。QML的着色器程序允许用户与OpenGL进行实时交互,动态修改。
OpenGL的着色器主要分为两种:顶点着色器(vertex shader)和片源着色器(FragmentShader)。顶点着色器在接收到顶点数据之后,将顶点数据信息分配给gl_Position变量。在下一阶段中,顶点模型会被分割、转化,并实现栅格化,形成一个个小三角,为下一步像素渲染做好准备。在片源着色器程序中,色值被设置到gl_FragColor变量中,并最终渲染到对应的分割块中。整个流程中,顶点着色器负责尺寸分割,片源着色器负责颜色渲染。具体流程如下图所示:
下面以一个例子说明一下QML中着色器元素的使用方法:
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 20
//需要被主色器操作的图片
Image {
id: sourceImage
width: 80; height: width
source: 'qrc:/transform.png'
}
ShaderEffect {
id: effect
width: 80; height: width
property variant source: sourceImage
}
ShaderEffect {
id: effect2
width: 80; height: width
//指定输入的图片源
property variant source: sourceImage
//顶点着色器程序
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}"
//片源着色器程序
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
}
}
}
}
这个程序中着色器并没有对图片执行任何操作,显示效果如下图所示:
下面详细介绍一下着色程序中的基本类型和一些关键字:
顶点着色器
//顶点着色器程序
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}"
shader程序中的关键字的含义
uniform 用来定义常量,代表着在处理过程中不会变化的值
attribute 定义用来连接外部数据的变量
varying 用来定义在两个着色器之间传递的数据
highp 定义高精度值
lowp 定义低精度的值
mat4 定义4x4的矩阵
vec2 定义维度为2的容器
sampler2D 定义2D的纹理
float 定义浮点数
qt_Matric代表着模型-视图-投影矩阵
qt_Vertex代表着当前的顶点位置
qt_MultiTexCoord0: 代表着纹理的坐标
qt_TexCoord0: 和其它着色器共享的纹理坐标
//main函数是着色器程序的入口函数
void main() {
//在传到片源着色器程序的时候纹理图片坐标不变化
qt_TexCoord0 = qt_MultiTexCoord0;
//顶点着色器的顶点坐标位置也不发生变化
//投影矩阵没有添加数据不会对顶点信息进行修改
gl_Position = qt_Matrix * qt_Vertex;
}"
片源着色器
片源着色器从顶点着色器接收到顶点的坐标和纹理的坐标,然后进行颜色渲染,程序中我们可以通过修改qt_Opacity来控制图片的透明度。每一个片源着色器最终输出的都是gl_FragColor变量,该变量代表着程序输出到屏幕上的颜色。
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
//纹理按照原始的位置进行显示
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
Rectangle {
width: 600; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 20
//原始图片
Image {
id: sourceImage
width: 80; height: width
source: 'qrc:/transform.png'
}
/***显示成灰色***/
//vect4(Red,Green,Blue,Alpa)
ShaderEffect {
id: effect
width: 80; height: width
property variant source: sourceImage
//片源着色器程序
//显示成单一固定颜色
fragmentShader: "
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0) * qt_Opacity;
}"
}
/***添加灰色蒙版***/
ShaderEffect {
id: effect2
width: 80; height: width
//指定输入的图片源
property variant source: sourceImage
//片源着色器程序
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(0.5, 0.5, 0.5, 1.0) * qt_Opacity;
}"
}
/***通过外部通道手动指定渲染通道的颜色***/
//除了红色通道之外的其它的值都是1不会影响最终效果,只有红色通道影响最终效果
ShaderEffect {
id: effect3
width: 80; height: width
property variant source: sourceImage
//指定红色通道的值为0.3
property real redChannel: 0.3
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
uniform lowp float redChannel;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(redChannel, 1.0, 1.0, 1.0) * qt_Opacity;
}"
}
/***动态修改红色通道的值***/
ShaderEffect {
id: effect4
width: 80; height: width
property variant source: sourceImage
property real redChannel: 0.3
NumberAnimation on redChannel {
from: 0.0; to: 1.0; loops: Animation.Infinite; duration: 4000
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
uniform lowp float redChannel;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(redChannel, 1.0, 1.0, 1.0) * qt_Opacity;
}"
}
}
}
}
片源着色器波动效果
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
Rectangle {
width: 600; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 20
//原始图片
Image {
id: sourceImage
width: 160; height: width
source: 'qrc:/transform.png'
}
//波动效果
ShaderEffect {
width: 160; height: width
property variant source: sourceImage
//频率为8
property real frequency: 8
//幅度为0.1
property real amplitude: 0.1
//事件从0到2PI
property real time: 0.0
NumberAnimation on time {
from: 0; to: Math.PI*2; duration: 1000; loops: Animation.Infinite
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
uniform highp float frequency;
uniform highp float amplitude;
uniform highp float time;
void main() {
//指定正弦波
highp vec2 pulse = sin(time - frequency * qt_TexCoord0);
highp vec2 coord = qt_TexCoord0 + amplitude * vec2(pulse.x, -pulse.x);
gl_FragColor = texture2D(source, coord) * qt_Opacity;
}"
}
}
}
}
显示效果如下图所示:
顶点着色器用来控制纹理的各个顶点的位置。通常情况下,顶点着色器可以指定四个位置(top-left、top-right、bottom-left、bottom-right).每个顶点的位置的类型都是vec4。下面介绍一下,通过顶点着色器控制的各种特效。
动态对图片进行缩放
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 640
height: 480
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row{
spacing:20
Image {
id: sourceImage
width: 160; height: width
source: "qrc:/transform.png"
}
ShaderEffect {
id: sacleEffect
width: 160; height: width
property variant source: sourceImage
property bool minimized: false
//点击的时候修改标志位minimized来对状态进行修改
MouseArea {
anchors.fill: parent
onClicked: sacleEffect.minimized = !sacleEffect.minimized
}
property real minimize: 0.0
//minimized为1的时候的动画
//注意minimized是标志位bool类型, mimimize是变化程度是real类型,两个是不同的变量
SequentialAnimation on minimize {
id: animMinimize
running: sacleEffect.minimized
PauseAnimation { duration: 300 }
//minimize从0变到1
NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
//minimized为0的时候的动画
SequentialAnimation on minimize {
id: animNormalize
running: !sacleEffect.minimized
//minimize从1变到0
NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
//顶点着色器程序
//mix是差值函数原理如下
//mix(qt_Vertex.y,height,minimize) = qt_Vertex.y*(1-minimize) + height*minimize
//mimizmize为1的时候最大显示,为0的时候不显示
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
uniform highp float minimize;
uniform highp float width;
uniform highp float height;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
pos.x = mix(qt_Vertex.x, width, minimize);
gl_Position = qt_Matrix * pos;
}"
}
}
}
}
效果如下所示:
通过定点着色器实现的其它特效,主要涉及到一些矩阵变化和数学领域的知识,就不详细介绍了。
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
visible: true
width: 680
height: 480
Rectangle {
width: 680; height: 240
color: '#1e1e1e'
Row{
spacing:20
Image {
id: sourceImage
width: 160; height: width
source: "qrc:/transform.png"
}
ShaderEffect {
id: sacleEffect
width: 160; height: width
property variant source: sourceImage
property bool minimized: false
//点击的时候修改标志位minimized来对状态进行修改
MouseArea {
anchors.fill: parent
onClicked: sacleEffect.minimized = !sacleEffect.minimized
}
property real minimize: 0.0
//minimized为1的时候的动画
//注意minimized是标志位bool类型, mimimize是变化程度是real类型,两个是不同的变量
SequentialAnimation on minimize {
id: animMinimize
running: sacleEffect.minimized
PauseAnimation { duration: 300 }
//minimize从0变到1
NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
//minimized为0的时候的动画
SequentialAnimation on minimize {
id: animNormalize
running: !sacleEffect.minimized
//minimize从1变到0
NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
vertexShader: "
uniform highp mat4 qt_Matrix;
uniform highp float minimize;
uniform highp float height;
uniform highp float width;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
highp float t = pos.y / height;
pos.x = mix(qt_Vertex.x, width, t * minimize);
gl_Position = qt_Matrix * pos;
}"
}
ShaderEffect {
id: genieEffect
width: 160; height: width
property variant source: sourceImage
mesh: GridMesh { resolution: Qt.size(10, 10) }
property real minimize: 0.0
property real bend: 0.0
property bool minimized: false
property real side: 1.0
MouseArea {
anchors.fill: parent
onClicked: parent.minimized = !parent.minimized
}
ParallelAnimation {
id: animMinimize1
running: genieEffect.minimized
SequentialAnimation {
PauseAnimation { duration: 300 }
NumberAnimation {
target: genieEffect; property: 'minimize';
to: 1; duration: 700;
easing.type: Easing.InOutSine
}
PauseAnimation { duration: 1000 }
}
SequentialAnimation {
NumberAnimation {
target: genieEffect; property: 'bend'
to: 1; duration: 700;
easing.type: Easing.InOutSine }
PauseAnimation { duration: 1300 }
}
}
ParallelAnimation {
id: animNormalize1
running: !genieEffect.minimized
SequentialAnimation {
NumberAnimation {
target: genieEffect; property: 'minimize';
to: 0; duration: 700;
easing.type: Easing.InOutSine
}
PauseAnimation { duration: 1300 }
}
SequentialAnimation {
PauseAnimation { duration: 300 }
NumberAnimation {
target: genieEffect; property: 'bend'
to: 0; duration: 700;
easing.type: Easing.InOutSine }
PauseAnimation { duration: 1000 }
}
}
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
uniform highp float height;
uniform highp float width;
uniform highp float minimize;
uniform highp float bend;
uniform highp float side;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
highp float t = pos.y / height;
t = (3.0 - 2.0 * t) * t * t;
pos.x = mix(qt_Vertex.x, side * width, t * bend);
gl_Position = qt_Matrix * pos;
}"
}
}
}
}
QML中对常用的图像效果进行了封装,我们可以直接拿来就用,同时这个封装库也是一个很好的学习资料,我们可以根据其中的例子进行参考,实现自己想要的图像效果。
下面以一个模糊效果为例,说明一下这个库的使用方法:
import QtQuick 2.0
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
import QtGraphicalEffects 1.0
ApplicationWindow {
visible: true
width: 680
height: 480
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 16
Image {
id: sourceImage
source: "qrc:/transform.png"
width: 200; height: width
sourceSize: Qt.size(parent.width, parent.height)
smooth: true
}
FastBlur {
width: 200; height: width
source: sourceImage
radius: blurred?32:0
property bool blurred: false
Behavior on radius {
NumberAnimation { duration: 1000 }
}
MouseArea {
id: area
anchors.fill: parent
onClicked: parent.blurred = !parent.blurred
}
}
}
}
}
效果如下:
图像库效果列表如下,供参考
类别 |
效果 |
介绍 |
Blend |
Blend |
merges two source items by using a blend mode |
Color |
BrightnessContrast |
adjusts brightness and contrast |
Colorize |
sets color in the HSL color space |
|
ColorOverlay |
applies a color layer |
|
Desaturate |
reduces color saturation |
|
GammaAdjust |
adjusts luminance |
|
HueSaturation |
adjusts colors in the HSL color space |
|
LevelAdjust |
adjusts colors in the RGB color space |
|
Gradient |
ConicalGradient |
draws a conical gradient |
LinearGradient |
draws a linear gradient |
|
RadialGradient |
draws a radial gradient |
|
Distortion |
Displace |
moves the pixels of the source item according to the specified displacement source |
Drop Shadow |
DropShadow |
draws a drop shadow |
InnerShadow |
draws an inner shadow |
|
Blur |
FastBlur |
applies a fast blur effect |
GaussianBlur |
applies a higher quality blur effect |
|
MaskedBlur |
applies a varying intensity blur effect |
|
RecursiveBlur |
blurs repeatedly, providing a strong blur effect |
|
Motion Blur |
DirectionalBlur |
applies a directional motion blur effect |
RadialBlur |
applies a radial motion blur effect |
|
ZoomBlur |
applies a zoom motion blur effect |
|
Glow |
Glow |
draws an outer glow effect |
RectangularGlow |
draws a rectangular outer glow effect |
|
Mask |
OpacityMask |
masks the source item with another item |
ThresholdMask |
masks the source item with another item and applies a threshold value |