# 片段着色器
为每个要渲染的像素都调用片段着色器。 在本章中,将开发一个小的红色透镜,它将增加光源的红色通道的值。
# 设置场景
首先,设置场景,在该场景中带有一个居中的网格,并显示源图像。
import QtQuick
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Grid {
anchors.centerIn: parent
spacing: 20
rows: 2; columns: 4
Image {
id: sourceImage
width: 80; height: width
source: '../../assets/tulips.jpg'
}
}
}
# 红色着色器
接下来,将添加一个着色器,它通过为每个片段提供一个红色值来显示一个红色矩形。
#version 440
layout(location=0) in vec2 qt_TexCoord0;
layout(location=0) out vec4 fragColor;
layout(std140, binding=0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
} ubuf;
layout(binding=1) uniform sampler2D source;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0) * ubuf.qt_Opacity;
}
在片段着色器中,简单地为每个片段分配一个vec4(1.0, 0.0, 0.0, 1.0)
,表示具有完全不透明(alpha=1.0)的红色;对于每个片段的fragColor
,将每个像素变成纯红色 .
# 带有纹理的红色着色器
现在想要将红色应用于每个纹理像素。 为此,需要将纹理返回到顶点着色器中。 因为没有在顶点着色器中做任何其他事情,所以默认的顶点着色器就足够了。 只需要提供一个兼容的片段着色器。
#version 440
layout(location=0) in vec2 qt_TexCoord0;
layout(location=0) out vec4 fragColor;
layout(std140, binding=0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
} ubuf;
layout(binding=1) uniform sampler2D source;
void main() {
fragColor = texture(source, qt_TexCoord0) * vec4(1.0, 0.0, 0.0, 1.0) * ubuf.qt_Opacity;
}
完整的着色器目前包含图像源,将其作为变体属性;这里省略了顶点着色器,如果没有指定它是默认的顶点着色器。
在片段着色器中,选择纹理片段texture(source, qt_TexCoord0)
,并将红色应用到此纹理片段。
# 红色通道属性
对红色通道值进行硬编码并不是很好,因此希望从QML端控制该值。 为此,为着色器效果添加了一个redChannel属性,并在片段着色器的统一缓冲区中声明了一个float redChannel
变量。 这就是需要做的所有事情,使得QML端的值可用于着色器代码。
提示
请注意,redChannel
必须位于统一缓冲区ubuf
中隐含的qt_Matrix
和qt_Opacity
之后。 qt_
参数之后的参数顺序由自己决定,但qt_Matrix
和qt_Opacity
必须按此顺序排在第一位。
#version 440
layout(location=0) in vec2 qt_TexCoord0;
layout(location=0) out vec4 fragColor;
layout(std140, binding=0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float redChannel;
} ubuf;
layout(binding=1) uniform sampler2D source;
void main() {
fragColor = texture(source, qt_TexCoord0) * vec4(ubuf.redChannel, 1.0, 1.0, 1.0) * ubuf.qt_Opacity;
}
为了使镜头真正成为镜头,将vec4颜色更改为vec4(redChannel, 1.0, 1.0, 1.0)将其他颜色乘以1.0,只有红色部分乘以redChannel变量。
# 红色通道动画
由于redChannel属性是一个普通属性,它也可以像QML中的所有的属性一样进行动画处理。 所以可以使用QML属性在GPU上设置动画值来影响着色器。 很酷!
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: "red3.frag.qsb"
}
这是最终结果。
第2行的着色器效果值从0.0变为1.0,持续时间为4秒。 所以展示的效果是图像从没有红色信息(0.0 红色)变为正常图像(1.0 红色)。
# 烘焙
同样,需要烘焙着色器。 在命令行中使用以下命令执行操作:
qsb --glsl 100es,120,150 --hlsl 50 --msl 12 -o red1.frag.qsb red1.frag
qsb --glsl 100es,120,150 --hlsl 50 --msl 12 -o red2.frag.qsb red2.frag
qsb --glsl 100es,120,150 --hlsl 50 --msl 12 -o red3.frag.qsb red3.frag