Fragment Shaders
片段着色器
The fragment shader is called for every pixel to be rendered. In this chapter, we will develop a small red lens which will increase the red color channel value of the source.
对要渲染的每个像素调用片段着色器。在本章中,我们将开发一个小的红色透镜,它将增加光源的红色通道值。
Setting up the scene
设置场景
First, we set up our scene, with a grid centered in the field and our source image be displayed.
首先,我们设置场景,以场为中心的网格显示源图像。
import QtQuickRectangle {width: 480; height: 240color: '#1e1e1e'Grid {anchors.centerIn: parentspacing: 20rows: 2; columns: 4Image {id: sourceImagewidth: 80; height: widthsource: '../../assets/tulips.jpg'}}
}
A red shader
红色着色器
Next, we will add a shader, which displays a red rectangle by providing for each fragment a red color value.
接下来,我们将添加一个着色器,通过为每个片段提供一个红色值来显示一个红色矩形。
#version 440layout(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;
}
In the fragment shader we simply assign a vec4(1.0, 0.0, 0.0, 1.0)
, representing the color red with full opacity (alpha=1.0), to the fragColor
for each fragment, turning each pixel to a solid red.
在片段着色器中,我们只需为每个片段的fragColor指定一个vec4(1.0、0.0、0.0、1.0),用完全不透明度(alpha=1.0)表示红色,将每个像素变成纯红色。
A red shader with texture
带有纹理的红色着色器
Now we want to apply the red color to each texture pixel. For this, we need the texture back in the vertex shader. As we don’t do anything else in the vertex shader the default vertex shader is enough for us. We just need to provide a compatible fragment shader.
现在我们要将红色应用于每个纹理像素。为此,我们需要在顶点着色器中恢复纹理。由于我们不在顶点着色器中执行任何其他操作,因此默认的顶点着色器对我们来说就足够了。我们只需要提供一个兼容的片段着色器。
#version 440layout(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;
}
The full shader contains now back our image source as variant property and we have left out the vertex shader, which if not specified is the default vertex shader.
完整着色器现在包含了图像源的variant属性,我们省略了顶点着色器,如果没有指定,它就是默认的顶点着色器。
In the fragment shader, we pick the texture fragment texture(source, qt_TexCoord0)
and apply the red color to it.
在片段着色器中,我们拾取纹理片段texture(source, qt_TexCoord0)
并对其应用红色。
The red channel property
红色通道属性
It’s not really nice to hard code the red channel value, so we would like to control the value from the QML side. For this we add a redChannel property to our shader effect and also declare a float redChannel
inside the uniform buffer of the fragment shader. That is all that we need to do to make a value from the QML side available to the shader code.
对红色通道值进行硬编码不是很好,所以我们希望从QML端控制该值。为此,我们在着色器效果中添加了一个redChannel属性,并在片段着色器的统一缓冲区内声明了一个float redChannel。这就是我们需要做的一切,以使着色器代码可以使用QML端的值。
TIP
注
Notice that the redChannel
must come after the implicit qt_Matrix
and qt_Opacity
in the uniform buffer, ubuf
. The order of the parameters after the qt_
parameters is up to you, but qt_Matrix
and qt_Opacity
must come first and in that order.
请注意,redChannel
必须位于uniform缓冲区ubuf中的隐式qt_Matrix
和qt_Opacity
之后。qt_
parameters之后的参数顺序由您决定,但qt_Matrix
和qt_Opacity
必须排在第一位,并且按此顺序排列。
#version 440layout(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;
}
To make the lens really a lens, we change the vec4 color to be vec4(redChannel, 1.0, 1.0, 1.0) so that the other colors are multiplied by 1.0 and only the red portion is multiplied by our redChannel variable.
为了使镜头真正成为镜头,我们将vec4颜色更改为vec4(redChannel, 1.0, 1.0, 1.0),以便其他颜色乘以1.0,只有红色部分乘以我们的redChannel变量。
The red channel animated
红色通道动画
As the redChannel property is just a normal property it can also be animated as all properties in QML. So we can use QML properties to animate values on the GPU to influence our shaders. How cool is that!
由于redChannel属性只是一个普通属性,因此它也可以作为QML中的所有属性设置动画。因此,我们可以使用QML属性在GPU上设置值的动画,以影响着色器。多酷啊!
ShaderEffect {id: effect4width: 80; height: widthproperty variant source: sourceImageproperty real redChannel: 0.3visible: root.step>3NumberAnimation on redChannel {from: 0.0; to: 1.0; loops: Animation.Infinite; duration: 4000}fragmentShader: "red3.frag.qsb"
}
Here the final result.
这里是最终结果。
The shader effect on the 2nd row is animated from 0.0 to 1.0 with a duration of 4 seconds. So the image goes from no red information (0.0 red) over to a normal image (1.0 red).
第二行的着色器效果在0.0到1.0之间设置动画,持续时间为4秒。因此,图像从没有红色信息(0.0红色)变为正常图像(1.0红色)。
Baking
生成
Again, we need to bake the shaders. The following commands from the command line does that:
同样,我们需要烘焙着色器。命令行中的以下命令可执行此操作:
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
示例源码下载