[vlc-commits] [Git][videolan/vlc][master] 6 commits: qt: introduce `VoronoiSnow.frag`

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Sat Jun 7 16:14:59 UTC 2025



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
2d41cd5b by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
qt: introduce `VoronoiSnow.frag`

I initially wanted to use an already made snow shader,
as there are better ones. However, I could not find a
good one with compatible license, so I had to come up
with one myself. Thankfully Inigo Quilez's voronoi
and snowflake SDF helped me (MIT license).

This is not the best snow shader effect, and I can not
create a good one because I'm not familiar with GLSL
or graphics programming in general. But it seemed decent
to me for the use case here.

Note that this is additive, so if the background is
pure white the snow would not be visible.

I also investigated whether to use Qt's particle system
for this, instead of a fragment shader. I decided to
do this with a fragment shader because we currently do
not use the particle system, the performance benefit is
not clear, and the fragment shader is simpler (thanks to
Inigo Quilez).

- - - - -
6d150b68 by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
qt: provide implementations for voronoi diagram and snowflake SDF in `VoronoiSnow.frag`

Thanks go to Inigo Quilez who licensed these with the MIT license.

- - - - -
29a12997 by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
qt: introduce `VoronoiSnow.qml`

- - - - -
7350b13f by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
Partial revert "qt: switch to QML/player/playlist"

This partially reverts commit e1c82853965d44b4390af78d9184bd0b60ccc3fb.

Only brings back easter egg key handling.

- - - - -
5c68ca26 by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
qt: use `ARRAY_SIZE` when comparing `i_kc_offset`

- - - - -
a8f2e4b2 by Fatih Uzunoglu at 2025-06-07T16:01:58+00:00
qml: use `VoronoiSnow` in `MainDisplay`

- - - - -


9 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/interface_window_handler.cpp
- modules/gui/qt/maininterface/interface_window_handler.hpp
- modules/gui/qt/maininterface/qml/MainDisplay.qml
- modules/gui/qt/meson.build
- + modules/gui/qt/shaders/VoronoiSnow.frag
- modules/gui/qt/shaders/meson.build
- modules/gui/qt/shaders/shaders.qrc
- + modules/gui/qt/widgets/qml/VoronoiSnow.qml


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -1307,7 +1307,8 @@ libqml_module_widgets_a_QML = \
 	widgets/qml/ScrollBarExt.qml \
 	widgets/qml/FastBlend.qml \
 	widgets/qml/RadioButtonExt.qml \
-	widgets/qml/RoundedRectangleShadow.qml
+	widgets/qml/RoundedRectangleShadow.qml \
+	widgets/qml/VoronoiSnow.qml
 if HAVE_QT65
 libqml_module_widgets_a_QML += \
 	widgets/qml/BlurEffect.qml
@@ -1363,7 +1364,8 @@ libqt_plugin_la_SHADER := shaders/FadingEdge.frag \
                           shaders/FastBlend_multiply.frag \
                           shaders/FastBlend_screen.frag \
                           shaders/RoundedRectangleShadow.frag \
-                          shaders/RoundedRectangleShadow_hollow.frag
+                          shaders/RoundedRectangleShadow_hollow.frag \
+                          shaders/VoronoiSnow.frag
 if ENABLE_QT
 
 libqt_plugin_la_LIBADD += libqml_module_dialogs.a \


=====================================
modules/gui/qt/maininterface/interface_window_handler.cpp
=====================================
@@ -42,6 +42,13 @@ void setWindowState(QWindow *window, Qt::WindowState state)
 
 }
 
+const Qt::Key InterfaceWindowHandler::kc[10] =
+{
+        Qt::Key_Up, Qt::Key_Up,
+        Qt::Key_Down, Qt::Key_Down,
+        Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right,
+        Qt::Key_B, Qt::Key_A
+};
 
 InterfaceWindowHandler::InterfaceWindowHandler(qt_intf_t *_p_intf, MainCtx* mainCtx, QWindow* window, QObject *parent)
     : QObject(parent)
@@ -143,6 +150,9 @@ InterfaceWindowHandler::InterfaceWindowHandler(qt_intf_t *_p_intf, MainCtx* main
     connect(m_mainCtx, &MainCtx::requestInterfaceMinimized,
             this, &InterfaceWindowHandler::setInterfaceMinimized);
 
+    connect(this, &InterfaceWindowHandler::kc_pressed,
+            m_mainCtx, &MainCtx::kc_pressed);
+
     m_window->installEventFilter(this);
 }
 
@@ -221,6 +231,18 @@ bool InterfaceWindowHandler::eventFilter(QObject*, QEvent* event)
     {
         QKeyEvent * keyEvent = static_cast<QKeyEvent *> (event);
 
+        /* easter eggs sequence handling */
+        if ( keyEvent->key() == kc[ i_kc_offset ] )
+            i_kc_offset++;
+        else
+            i_kc_offset = 0;
+
+        if ( i_kc_offset == ARRAY_SIZE( kc ) )
+        {
+            i_kc_offset = 0;
+            emit kc_pressed();
+        }
+
         if (applyKeyEvent(keyEvent) == false)
             return false;
 


=====================================
modules/gui/qt/maininterface/interface_window_handler.hpp
=====================================
@@ -60,6 +60,7 @@ signals:
     void interfaceAlwaysOnTopChanged(bool);
     void interfaceFullScreenChanged(bool);
     void incrementIntfUserScaleFactor(bool increment);
+    void kc_pressed();
 
 private:
     bool applyKeyEvent(QKeyEvent * event) const;
@@ -84,6 +85,9 @@ protected:
     bool m_hasResizeCursor = false;
 
     QRect m_interfaceGeometry;
+
+    static const Qt::Key kc[10]; /* easter eggs */
+    int i_kc_offset = 0;
 };
 
 #endif // INTERFACEWINDOWHANDLER_H


=====================================
modules/gui/qt/maininterface/qml/MainDisplay.qml
=====================================
@@ -162,6 +162,25 @@ FocusScope {
         colorSet: ColorContext.View
     }
 
+    Loader {
+        id: voronoiSnowLoader
+
+        z: 1.5
+        source: "qrc:///qt/qml/VLC/Widgets/VoronoiSnow.qml"
+        anchors.fill: parent
+        active: false
+
+        function toggleActive() {
+            voronoiSnowLoader.active = !voronoiSnowLoader.active
+        }
+
+        Component.onCompleted: {
+            if (MainCtx.useXmasCone()) {
+                MainCtx.kc_pressed.connect(voronoiSnowLoader.toggleActive)
+            }
+        }
+    }
+
     ColumnLayout {
         id: mainColumn
         anchors.fill: parent


=====================================
modules/gui/qt/meson.build
=====================================
@@ -904,6 +904,7 @@ qml_modules += {
         'widgets/qml/FastBlend.qml',
         'widgets/qml/RadioButtonExt.qml',
         'widgets/qml/RoundedRectangleShadow.qml',
+        'widgets/qml/VoronoiSnow.qml',
     ),
 }
 


=====================================
modules/gui/qt/shaders/VoronoiSnow.frag
=====================================
@@ -0,0 +1,166 @@
+#version 440
+
+/*****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * The MIT License
+ * Copyright (C) 2015 Inigo Quilez
+ * Copyright (C) 2017 Inigo Quilez
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the “Software”), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *****************************************************************************/
+
+#define ANIMATE
+
+#define LAYER_MAX 1.5
+#define LAYER_INCREMENT 0.2 // increment
+#define ANTIALIASING
+
+layout(location = 0) out vec4 fragColor; // premultiplied
+
+layout(std140, binding = 0) uniform buf {
+    mat4 qt_Matrix;
+    float qt_Opacity;
+
+    vec2 windowSize;
+    float time; // seed
+    vec4 color; // snowflake color
+};
+
+// Inigo Quilez's voronoi (https://iquilezles.org/articles/voronoilines):
+/// <voronoi>
+vec2 hash2( vec2 p )
+{
+    // procedural white noise
+    return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
+}
+
+vec3 voronoi( in vec2 x )
+{
+    vec2 ip = floor(x);
+    vec2 fp = fract(x);
+
+    //----------------------------------
+    // first pass: regular voronoi
+    //----------------------------------
+    vec2 mg, mr;
+
+    float md = 8.0;
+    for( int j=-1; j<=1; j++ )
+    for( int i=-1; i<=1; i++ )
+    {
+        vec2 g = vec2(float(i),float(j));
+        vec2 o = hash2( ip + g );
+        #ifdef ANIMATE
+        o = 0.5 + 0.5*sin( time + 6.2831*o );
+        #endif
+        vec2 r = g + o - fp;
+        float d = dot(r,r);
+
+        if( d<md )
+        {
+            md = d;
+            mr = r;
+            mg = g;
+        }
+    }
+
+    //----------------------------------
+    // second pass: distance to borders
+    //----------------------------------
+    md = 8.0;
+    for( int j=-2; j<=2; j++ )
+    for( int i=-2; i<=2; i++ )
+    {
+        vec2 g = mg + vec2(float(i),float(j));
+        vec2 o = hash2( ip + g );
+        #ifdef ANIMATE
+        o = 0.5 + 0.5*sin( time + 6.2831*o );
+        #endif
+        vec2 r = g + o - fp;
+
+        if( dot(mr-r,mr-r)>0.00001 )
+        md = min( md, dot( 0.5*(mr+r), normalize(r-mr) ) );
+    }
+
+    return vec3( md, mr );
+}
+/// </voronoi>
+
+// Inigo Quilez's snowflake SDF (https://www.shadertoy.com/view/wsGSD3):
+/// <snowflake>
+float sdLine( in vec2 p, in vec2 a, in vec2 b )
+{
+    vec2 pa = p-a, ba = b-a;
+    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
+    return length( pa - ba*h );
+}
+
+vec2 opModPolarMirrored( in vec2 p, float theta, float offset)
+{
+    float a = atan(p.y, p.x) - offset;
+    a = abs(mod(a + .5 * theta, theta) - .5 * theta);
+    return length(p) * vec2(cos(a), sin(a));
+}
+
+float sdSnowflake( in vec2 p )
+{
+    p = opModPolarMirrored(p, radians(360.) / 6., radians(90.));
+
+    float d = sdLine( p, vec2(0, 0), vec2(.75, 0) );
+    d = min(d, sdLine( p, vec2(.5, 0), vec2(.5, 0) + vec2(.1, .1) ));
+    d = min(d, sdLine( p, vec2(.25, 0), vec2(.25, 0) + 1.5 * vec2(.1, .1) ));
+    return d - .04;
+}
+/// </snowflake>
+
+void main()
+{
+    vec2 p = gl_FragCoord.xy / windowSize.xx;
+
+    vec3 col = vec3(0.0, 0.0, 0.0);
+
+    // Multiple layers
+    for (float i = 1.0; i <= LAYER_MAX; i += LAYER_INCREMENT)
+    {
+        vec3 c = voronoi((6.0 * p + sign(qt_Matrix[3][1]) * vec2(sin(time) / 2.0, time)) * i);
+
+        // Snowflake size depends on the layer:
+        float dist = sdSnowflake(c.yz * 8.0 * i);
+
+        // additive:
+#ifdef ANTIALIASING
+        float fw = fwidth(dist) * 0.75; // for AA
+        col += (1.0 - smoothstep(-fw , fw, dist)) * color.rgb * color.a;
+#else
+        col += (1.0 - step(0.0, dist)) * color.rgb * color.a;
+#endif
+    }
+
+    col *= qt_Opacity;
+
+    fragColor = vec4(col, 0.0); // premultiplied additive
+}


=====================================
modules/gui/qt/shaders/meson.build
=====================================
@@ -22,6 +22,7 @@ shader_sources = [
     'FastBlend_screen.frag',
     'RoundedRectangleShadow.frag',
     'RoundedRectangleShadow_hollow.frag',
+    'VoronoiSnow.frag',
 ]
 
 shader_files = files(shader_sources)


=====================================
modules/gui/qt/shaders/shaders.qrc
=====================================
@@ -16,5 +16,6 @@
         <file alias="FastBlend_screen.frag.qsb">FastBlend_screen.frag.qsb</file>
         <file alias="RoundedRectangleShadow.frag.qsb">RoundedRectangleShadow.frag.qsb</file>
         <file alias="RoundedRectangleShadow_hollow.frag.qsb">RoundedRectangleShadow_hollow.frag.qsb</file>
+        <file alias="VoronoiSnow.frag.qsb">VoronoiSnow.frag.qsb</file>
     </qresource>
 </RCC>


=====================================
modules/gui/qt/widgets/qml/VoronoiSnow.qml
=====================================
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+import QtQuick
+import QtQuick.Window
+
+ShaderEffect {
+    id: effect
+
+    readonly property size windowSize: {
+        if (Window.window)
+            // Currently only one dimension is taken into account (to not stretch)
+            // in the shader, but still provide both for now. We use window size
+            // instead of item (shader effect) size because the shader uses
+            // gl_FragCoord:
+            return Qt.size(Window.window.width, Window.window.height)
+        return Qt.size(0, 0)
+    }
+
+    property color color: Qt.rgba(0.8, 0.8, 0.8, 0.4) // snowflake color
+
+    property real time: 0.0 // seed
+
+    property real speed: 1.2 // speed factor
+
+    UniformAnimator on time {
+        loops: Animation.Infinite
+        from: 0
+        to: 50
+        duration: 100000 / effect.speed
+    }
+
+    fragmentShader: "qrc:///shaders/VoronoiSnow.frag.qsb"
+}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/940ed9f9e3ea56ebba09c9bfa6cb8a127cc83b65...a8f2e4b2cd12f9f0f01792656a0b36dac5e781fd

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/940ed9f9e3ea56ebba09c9bfa6cb8a127cc83b65...a8f2e4b2cd12f9f0f01792656a0b36dac5e781fd
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list