一、前言
2022年圣诞节到来啦,很高兴这次我们又能一起度过~
这次给大家带来一个简单漂亮圣诞树灯。
当然了,本篇文章主要是讲解一下如何在 Qml 中使用 GLSL 来实现自己的特效。
至于代码嘛,我比较喜欢在 Shaderjoy 上寻找,那里有很多超级炫酷的着色器实现的特效,并且可以很轻松的集成到 Qml 中。
二、纯 GLSL 实现的圣诞树旋转灯
首先,想要在 Qml 中使用 GLSL,我们需要借助 ShaderEffect。
ShaderEffect 类型将自定义顶点和片段(像素)着色器应用于矩形。它允许在 QML 场景中添加阴影、模糊、着色和页面卷曲等效果。
注意:根据使用的 Qt Quick Scenegraph 后端,可能不支持 ShaderEffect 类型。例如,使用软件后端,则根本不会渲染效果。
ShaderEffect 有两个重要属性:
vertexShader: 此属性保存顶点着色器源码字符串 ( 实际上也可以是文件 )。
fragmentShader: 此属性保存片元着色器源码字符串 ( 实际上也可以是文件 )。
我这里就比较简单了,直接将着色器代码文件传入即可:
-
import
QtQuick
2.15
-
import
QtQuick.
Window
2.15
-
-
Window {
-
id: root
-
width:
1280
-
height:
900
-
visible:
true
-
title:
qsTr(
"Christmas tree lights")
-
-
ShaderEffect {
-
anchors.
fill: parent
-
vertexShader:
"file:./glsl/christmas_tree_lights.vert"
-
fragmentShader:
"file:./glsl/christmas_tree_lights.frag"
-
property vector3d
iResolution:
Qt.
vector3d(root.
width, root.
height,
0)
-
property real
iTime:
0
-
-
Text {
-
text:
"Time: " + parent.
iTime.
toFixed(
2)
-
color:
"white"
-
}
-
-
Timer {
-
running:
true
-
repeat:
true
-
interval:
10
-
onTriggered: parent.
iTime +=
0.01;
-
}
-
}
-
}
当然,还要向着色器中传入一些 uniform 变量,直接在 Qml 中声明即可,Qt 会自动帮我们传入。
着色器的代码来自 Shadertoy:https://www.shadertoy.com
顶点着色器很简单,直接传递变量,计算顶点即可:
-
#version 450
-
-
uniform mat4 qt_Matrix;
-
in vec4 qt_Vertex;
-
out vec4 fragCoord;
-
-
void main() {
-
fragCoord = qt_Vertex;
-
gl_Position = qt_Matrix * qt_Vertex;
-
}
然后是片元着色器( 比较复杂 ) :
-
#version 450
-
-
in vec4 fragCoord;
-
-
uniform vec3 iResolution;
// viewport resolution (in pixels)
-
uniform
float iTime;
// shader playback time (in seconds)
-
-
const
float pi =
3.1415927;
-
const
float dotsnbt =
90.0;
// Number of dots for the tree
-
const
float dotsnbs =
20.0;
// Number of dots for the star (per circle)
-
-
vec3 hsv2rgb (vec3 hsv) {
// from HSV to RGB color vector
-
hsv.yz =
clamp (hsv.yz,
0.0,
1.0);
-
return hsv.z * (
1.0 +
0.63 * hsv.y * (
cos (
2.0 *
3.14159 * (hsv.x +
vec3 (
0.0,
2.0 /
3.0,
1.0 /
3.0))) -
1.0));
-
}
-
-
void mainImage(out vec4 fragColor, in vec2 fragCoord)
-
{
-
float time = iTime;
-
float mx =
max(iResolution.x, iResolution.y);
-
vec2 scrs = iResolution.xy / mx;
-
vec2 uv =
vec2(fragCoord.x, iResolution.y - fragCoord.y) / mx;
-
//vec2 m = vec2(mouse.x / scrs.x, mouse.y * (scrs.y / scrs.x));
-
-
vec2 pos =
vec2(
0.0);
// Position of the dots
-
vec3 col =
vec3(
0.0);
// Color of the dots
-
float intensitys =
1.0 /
4000.0;
// Light intensity for the star
-
float intensityt =
1.0 /
2000.0;
// Light intensity for the tree
-
float scale =
0.2;
// Size of the star
-
-
/*** Star ***/
-
for(
float i =
0.0 ; i < dotsnbs; i++){
-
pos =
vec2(
cos(time *
0.2) /
20.0 *
cos(
2.0 * pi * i / dotsnbs),
-
0.15 *
sin(
2.0 * pi * i / dotsnbs)) * scale;
-
pos +=
vec2(scrs.x /
2.0, scrs.y *
0.11);
-
-
col +=
hsv2rgb(
vec3(i / dotsnbs,
distance(uv, pos) * (
1.0 / intensitys), intensitys /
distance(uv, pos)));
-
-
pos =
vec2(
0.12 *
cos(
2.0 * pi * i / dotsnbs + time *
0.2),
-
0.08 *
sin(
2.0 * pi * i / dotsnbs)) * scale;
-
pos +=
vec2(scrs.x /
2.0, scrs.y *
0.11);
-
-
col +=
hsv2rgb(
vec3(
1.0 - i / dotsnbs,
distance(uv, pos) * (
1.0 / intensitys), intensitys /
distance(uv, pos)));
-
-
pos =
vec2(
0.12 *
cos(
2.0 * pi * i / dotsnbs + time *
0.2),
-
-0.08 *
sin(
2.0 * pi * i / dotsnbs)) * scale;
-
pos +=
vec2(scrs.x /
2.0, scrs.y *
0.11);
-
-
col +=
hsv2rgb(
vec3(i / dotsnbs,
distance(uv, pos) * (
1.0 / intensitys), intensitys /
distance(uv, pos)));
-
}
-
-
/*** Tree ***/
-
float angle = dotsnbt *
1.8;
// Angle of the cone
-
for(
float i =
0.0 ; i < dotsnbt ; i++){
-
pos =
vec2(scrs.x /
2.0 +
sin(i /
2.0 - time *
0.2) / (
3.0 / (i +
1.0) * angle),
-
scrs.y * ((i) / dotsnbt +
0.21) *
0.80);
-
-
col +=
hsv2rgb(
vec3(
1.5 * i / dotsnbt +
fract(time /
4.0),
distance(uv, pos) * (
1.0 / intensityt), intensityt /
distance(uv, pos)));
-
}
-
-
fragColor =
vec4( col,
1.0 );
-
}
-
-
void main(void)
-
{
-
mainImage(gl_FragColor,
vec2(fragCoord.x, iResolution.y - fragCoord.y));
-
}
三、效果展示
_(:3 」∠)_ 动图质量不太行,大家凑合看。
四、结语
另外,要提一点:
在Qt 5中,效果以GLSL(OpenGL着色语言)源代码的形式提供,通常作为字符串嵌入QML中。从Qt 5.8开始,可以引用本地文件或Qt资源系统中的文件。
在Qt 6中,Qt Quick支持图形API,如Vulkan、Metal和Direct3D 11。因此,使用GLSL源字符串不再可行。相反,新的着色器管道基于将Vulkan兼容的GLSL代码编译成SPIR-V,然后收集反射信息并翻译成其他着色语言,如HLSL、金属着色语言和各种GLSL版本。生成的资产被打包到一个单独的包中,通常存储在扩展名为.qsb的文件中。此过程最迟在应用程序构建时离线完成。在运行时,场景图和底层图形抽象使用这些.qsb文件。因此,ShaderEffect需要Qt 6中的文件(本地或qrc)引用来代替内联着色器代码。
例如,vertexShader和fragmentShader属性是Qt 6中的URL,其工作方式与Image.source非常相似。然而,ShaderEffect仅支持文件和qrc方案。也可以省略文件方案,以便以方便的方式指定相对路径。这样的路径是相对于组件(.qml文件)位置解析的。
因此,在 Qt 6 中,我们可以使用更加广泛的 qsb,这样只需要写一种着色器即可支持所有后端。
不过关于 qsb 具体如何使用,我后面会写一下相关的博客~
五、源码下载
Github的:
https://github.com/mengps/ShadertoyExampleshttps://github.com/mengps/ShadertoyExamples CSDN的:
转载:https://blog.csdn.net/u011283226/article/details/128429822