着色器允许我们利用SceneGraph的接口直接调用在强大的GPU上运行的OpenGL来创建渲染效果。着色器使用ShaderEffect与ShaderEffectSource元素来实现。着色器本身的算法使用OpenGL Shading Language(OpenGL着色语言) 来实现。
着色器元素(Shader Elements)
为了对着色器编程,Qt Quick提供了两个元素。ShaderEffectSource与ShaderEffect。ShaderEffect将会使用自定义的着色器,ShaderEffectSource可以将一个QML元素渲染为一个纹理然后再渲染这个纹理。由于ShaderEffect能够应用自定义的着色器到它的矩形几何形状,并且能够使用在着色器中操作资源。一个资源可以是一个图片,它被作为一个纹理或者着色器资源。
import QtQuick 2.12
import QtQuick.Window 2.12
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row{
anchors.centerIn: parent
spacing: 20
Image {
id: sourceImage
width: 80; height: width
source: "assets/tulips.jpg"
}
//将会使用自定义的着色器
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;
}"
}
}
}
变量简介:
- uniform-在处理过程中不能够改变的值。
- attribute-连接外部数据
- varying-着色器之间的共享数据
- highp-高精度值
- lowp-低精度值
- mat4-4x4浮点数(float) 矩阵
- vec2-包含两个浮点数的向量
- sampler2D-2D纹理
- float-浮点数
- qt_Matrix:model-view-projection(模型-视图-投影) 矩阵
- qt_Vertex:当前顶点坐标
- qt_MultiTexCoord0:纹理坐标
- qt_TexCoord0:共享纹理坐标
片段着色器(Fragement Shader)
import QtQuick 2.0
import QtQuick 2.5
Rectangle {
id: root
width: 480; height: 240
color: '#1e1e1e'
property int step: 4
Grid {
anchors.centerIn: parent
spacing: 20
rows: 2; columns: 4
Image {
id: sourceImage
width: 80; height: width
source: 'assets/tulips.jpg'
}
//我们简单的给gl_FragColor赋值为vec4(1.0, 0.0, 0.0, 1.0),它代
// 表红色, 并且不透明(alpha=1.0)
ShaderEffect {
id: effect1
width: 80; height: width
visible: root.step>0
// M1>>
fragmentShader: "
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * qt_Opacity;
}"
// <<M1
}
// M2>>
//在片段着色器中,我们提取纹理片段texture2D(source,qt_TexCoord0)
ShaderEffect {
id: effect2
width: 80; height: width
property variant source: sourceImage
visible: root.step>1
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(1.0, 0.0, 0.0, 1.0) * qt_Opacity;
}"
}
// <<M2
// M3>>
//我们改变vec4颜色为vec4(redChannel, 1.0, 1.0, 1.0),这
// 样其它颜色与1.0相乘,只有红色部分使用我们的redChannel变量
ShaderEffect {
id: effect3
width: 80; height: width
property variant source: sourceImage
property real redChannel: 0.3
visible: root.step>2
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;
}"
}
// <<M3
// M4>>
ShaderEffect {
id: effect4
width: 80; height: width
property variant source: sourceImage
property real redChannel: 0.3
visible: root.step>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;
}"
}
// <<M4
}
}
最后一张图片 在这4秒内,第二排的着色器红色通道的值从0.0到1.0。图片从没有红色信息(0.0red) 到一个正常的图片(1.0 red) 。
波浪效果(Wave Effect)
import QtQuick 2.0
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row{
anchors.centerIn: parent
spacing: 20
Image {
id: sourceImage
width: 160; height: width
source: "assets/coastline.jpg"
}
ShaderEffect {
width: 160; height: width
property variant source: sourceImage
property real frequency: 8
property real amplitude: 0.1
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;
}"
}
}
}
顶点着色器(Vertex Shader)
首先是GenieEffect.qml 文件
import QtQuick 2.5
ShaderEffect {
id: genieEffect
width: 160; height: width
anchors.centerIn: parent
property variant source
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
ParallelAnimation {
id: animMinimize
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: animNormalize
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;
}"
}
在 GenieDemo.qml 中
import QtQuick 2.5
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
GenieEffect {
source: Image { source: 'assets/lighthouse.jpg' }
MouseArea {
anchors.fill: parent
onClicked: parent.minimized = !parent.minimized
}
}
}
剧幕效果(Curtain Effect)
剧幕的波形阴影通过一个在剧幕宽度上的sin曲线使用7的振幅来计算(7*PI=221.99..) 另一个重要的部分是摆动,当剧幕打开或者关闭时,使用动画来播放剧幕的topWidth。bottomWidth使用SpringAnimation来跟随topWidth变化。这样我们就能创建出底部摆动的剧幕效果。计算得到的swing提供了摇摆的强度,用来对顶点的y值进行插值。
剧幕效果放在CurtainEffect.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;
}"
}
这个效果在curtaindemo.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
}
}
Qt图像效果库(Qt GraphicsEffect Library)
通过点击添加滤波模糊。
import QtQuick 2.5
import QtGraphicalEffects 1.0
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 16
Image {
id: sourceImage
source: "assets/tulips.jpg"
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
}
}
}
}
滤波器这章节我没有详细的去分析,只是简单的记录数据,以便以后查找方便。