Repository: cashew-olddew/Universal-Transition-Shader Branch: main Commit: c4e3e659e133 Files: 14 Total size: 44.3 KB Directory structure: gitextract_3sws4zr2/ ├── .editorconfig ├── .gitattributes ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets/ │ ├── day.jpg.import │ ├── icon.svg.import │ ├── mask.png.import │ └── night.jpg.import ├── main.tscn ├── project.godot ├── transition.gdshader └── transition.gdshader.uid ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 ================================================ FILE: .gitattributes ================================================ # Normalize line endings for all files that Git considers text files. * text=auto eol=lf # Only include the addons folder when downloading from the Asset Library. /** export-ignore /transition.gdshader !export-ignore ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: cashewolddew tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username buy_me_a_coffee: # Replace with a single Buy Me a Coffee username thanks_dev: # Replace with a single thanks.dev username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .gitignore ================================================ # Godot 4+ specific ignores .godot/ /android/ ================================================ FILE: LICENSE ================================================ Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. ================================================ FILE: README.md ================================================

Universal Transition Shader

Most common transitions inside one Godot shader. 🎬 ## Introduction _Hint:_ You can watch a brief guide video of the shader on my [youtube channel](https://www.youtube.com/watch?v=PtBZs7OvR2Y), or see it on [Godot Shaders](https://godotshaders.com/shader/universal-transition-shader/?post_id=11966) This shader provides a flexible and highly customizable system for creating animated transitions between visual elements in a Godot project. It supports a wide variety of transition styles, such as: - **Directional Wipes**: (left, right, top, bottom, diagonal) - **Clock Wipes**: (radial, with multi-sector support) - **Iris/Shape Reveals**: (polygon-based transitions with any number of sides) - **Dissolves** - **Fades** - **Slides** - **Combinations & Variations** To get started quickly, jump to the [quickstart](#quick-start) or [common transition recipes](#common-transition-recipes) sections. For in depth documentation, check the [parameters reference](#parameters-reference) section. ## Quick Start 1. Attach the shader to any `CanvasItem` (like a `Sprite2D` or `TextureRect`). - _Alternatively, you can clone this repository and open the included demo project for a ready-to-use example._ 2. In the Godot Inspector, navigate to: **Material → Shader → Animation** 3. Adjust the `progress` value to trigger a transition. 4. Explore other parameters to try different transition styles and effects. ## Common Transition Recipes **Let's cook!** 🍜 _Hint: Each recipe contains only the most basic 'ingredients', without which the recipe wouldn't work. Feel free to add any additional parameters to get variations of these recipes._ **Simple Fade** - `transition_type`: Basic - `grid_size`: (0.0, 0.0) - `basic_feather`: Any value > 0.0 **Directional Wipe** - `transition_type`: Basic - `grid_size`: (1.0, 0.0) or (0.0, 1.0) or (-1.0, 0.0) or (0.0, -1.0) **Corner Wipe** - `transition_type`: Basic - `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0) **Diagonal Wipe** - `transition_type`: Basic - `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0) - `rotation_angle`: 45.0 **Center Wipe** - `transition_type`: Basic - `position`: (0.5, 0.5) **Blinder Wipe** - `transition_type`: Basic - `grid_size`: (0.0, `abs(y) > 2.0`) or (`abs(x) > 2.0`, 0.0) **Grid Reveal** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (10.0, 10.0) - or any abs(x) > 0.0; abs(y) > 0.0; **Staggered Grid Reveal** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (10.0, 10.0) - or any abs(x) > 0.0; abs(y) > 0.0; - `stagger`: (1.0, 0.0) or (0.0, 1.0) **Mixed Stagger Reveal** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (5.0, 5.0) - `stagger`: (1.0, 1.0) **Cross-shaped Transition (All corners wipe)** - `transition_type`: Basic - `position`: (0.5, 0.5) - `stagger`: (1.0, 1.0) **Diagonal Popping Squares** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (5.0, 5.0) - `progress_bias`: (10.0, 10.0) **Step Wipe** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (5.0, 0.0) or (0.0, 5.0) - `progress_bias`: (5.0, 0.0) or (0.0, 5.0) **Mask Reveal** - `transition_type`: Mask - `position`: (0.5, 0.5) - `mask_texture`: Any black & white texture **Alternating Mask Grid** - `transition_type`: Mask - `position`: (0.5, 0.5) - `grid_size`: (5.0, 5.0) - `mask_texture`: Any black & white texture - `flip_frequency`: (1.0, 2.0) **Iris Transition** - `transition_type`: Shape - `position`: (0.5, 0.5) - `edges`: 64 - `shape_feather`: 0.1 **Spike Transition** - `transition_type`: Shape - `position`: (0.5, 0.5) - `edges`: 3 - `grid_size` & `rotation_angle`: `(0.5, y) & 0.0` or `(x, -0.5) & 90.0` **Scratch Lines Reveal** - `transition_type`: Shape - `position`: (0.5, 0.5) - `grid_size`: (50.0, 5.0) - `edges`: 3 - `flip_frequency`: (2.0, 1.0) **Overlapping Diamonds** - `transition_type`: Shape - `position`: (0.5, 0.5) - `grid_size`: (0.5, 50.0) or (50.0, 0.5) - `edges`: 3 - `shape_feather`: 0.0 **Corner-Clock Transition** - `transition_type`: Clock - `invert`: true - `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0) **Center-Clock Transition** - `transition_type`: Clock - `invert`: true - `position`: (0.5, 0.5) - `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0) **Fan Transition** - `transition_type`: Clock - `invert`: true - `grid_size`: (1.0, 1.0) or (-1.0, -1.0) or (-1.0, 1.0) or (1.0, -1.0) - `sectors`: 2 or more **Seamless Striped Flower Transition** - `transition_type`: Clock - `invert`: true - `grid_size`: (5.0, 5.0) - `flip_frequency`: (2, 2) - `sectors`: 16 **Hourglass Wipe Transition - by tomsterBG** - `transition_type`: Clock - `position`: (0.5, 0.5) - `stagger`: (1.0, 1.0) - `flip_frequency`: (2.0, 2.0) - `sectors`: 2 **Cumulative Stagger Transition - by tomsterBG** - `transition_type`: Basic - `position`: (0.5, 0.5) - `grid_size`: (4.0, 6.0) - `stagger_frequency`: (1.0, 1.0) - `cumulative_stagger`: _Animated_ alongside progress from (1.0, 0.0) to (3.0, 0.0) **Double Diamond Transition** - `transition_type`: Clock - `position`: (0.5, 0.5) - `grid_size`: (2.0, 2.0) - `local_x_mirror`: true - `local_y_mirror`: true - `sectors`: 4 **Spike Trap Transition** - `transition_type`: Shape - `position`: (0.0, 1.0) - `grid_size`: (1.0, 3.0) - `rotation_angle`: 30.0 - `global_x_mirror`: true - `local_y_mirror`: true - `edges`: 3 ## Parameters Reference The shader parameters are organized into the following categories: - **[Shader Parameters](#shader-parameters)**: Generic shader settings that apply to the overall behavior of the transition, such as the `Transition Type` - **[Positioning](#positioning)**: Used for positioning and orientation of the transition, applying to all transition types - **[Mirror](#mirror)**: Control the local and global mirroring around the center. - **[Stagger](#stagger)**: Handle offset of grid rows and columns, allowing for more varied grid patterns. - **[Basic Transition Controls](#basic-transition-controls)**: Parameters specific to the "Basic" transition type. - **[Mask Transition Controls](#mask-transition-controls)**: Parameters specific to the "Mask" transition type. - **[Shape Transition Controls](#shape-transition-controls)**: Parameters specific to the "Shape" transition type. - **[Clock Transition Controls](#clock-transition-controls)**: Parameters specific to the "Clock" transition type. - **[Animation](#animation)**: Control the progression of the animation. ### Shader Parameters --- `use_sprite_alpha` (`bool`, default: `true`) When `false`, the `CanvasItem`'s original alpha is ignored. When `true`, the final pixel's transparency will be the minimum of the sprite's original alpha and the transition's calculated alpha. This ensures that already-transparent parts of your sprite remain transparent throughout the transition. --- `use_transition_texture` (`bool`, default: `false`) When `false`, the shader blends from the original `COLOR` of the `CanvasItem` to a fully transparent `COLOR`, effectively revealing what's behind the `CanvasItem`. When `true`, the shader blends from the original `COLOR` of the `CanvasItem` to the `COLOR`of the texture provided under the `transition_texture` parameter. _Hint: If `transition_texture` is not set, the shader blends between `COLOR` and white._ --- `transition_texture` (`sampler2D`) The texture that will be transitioned to when `use_transition_texture` is set to `true`. _Hint: Has no effect if `use_transition_texture` is `false`._ --- `transition_type` (`int`, hint: `enum("Basic", "Mask", "Shape", "Clock")`, default: `0` (Basic)) Selects the fundamental algorithm or visual style for the transition effect. - `0` (Basic): Implements simple, rectangular, gradient-based wipes and fades. - `1` (Mask): Implements mask-based transitions. - `2` (Shape): Generates a regular convex polygon as the transition boundary. - `3` (Clock): Creates a radial, clock-like wipe effect. _Hint: Adjustments to parameters within a specific "Transition Controls" group (e.g. "Basic Transition Controls") will only affect the transition if the corresponding `transition_type` is selected._ --- ### Positioning These parameters influence the spatial origin, direction, and tiling of the transition, and they apply universally across all `transition_types`. --- `invert` (`bool`, default: `false`) Reverses the logical direction of the transition's progression. For example, a transition that normally expands outwards would contract, or a wipe from left to right would become right to left. --- `position` (`vec2`, default: `(0.0, 0,0)`) Specifies the position in each grid cell (see `grid_size` parameter) from where the transition starts. - `(0.0, 0.0)` - top left corner - `(1.0, 0.0)` - top right corner - `(0,0, 1.0)` - bottom left corner - `(1.0, 1.0)` - bottom right corner - `(0.5, 0.5)` - center --- `grid_size` (`vec2`, default: `(1.0, 1.0)`) Divides the `CanvasItem`'s area into a grid of independently animating cells. The `x` component defines the number of horizontal divisions, and the `y` component defines the number of vertical divisions. A value of `(1.0, 1.0)` means no division, treating the entire `CanvasItem` as a single cell. Using **negative values**, **zero values**, or **floating-point** numbers for the `grid_size` introduces interesting and powerful behaviors. For instance, `(-1.0, 0.0)` will create a right-to-left wipe. Values like `(0.0, 0.0)` can even produce a simple fade, given the transition has a blur. Experiment to find more special `grid_size` values or check out the [common transition recipes](#common-transition-recipes) section for more examples. --- `rotation_angle` (`float`, default: `0.0`) Rotates the local coordinate system within each grid cell (or the entire `CanvasItem` if `grid_size` is `(1.0, 1.0)`). The angle is specified in degrees. #### Mirror These parameters introduce mirroring of the transition effect around the center. --- `global_x_mirror` (`bool`, default: `false`) Applies horizontal mirroring effect to the _entire transition_. `global_y_mirror` (`bool`, default: `false`) Applies vertical mirroring effect to the _entire transition_. --- `local_x_mirror` (`bool`, default: `false`) Applies horizontal mirroring effect to the transition _within each grid cell_. `local_y_mirror` (`bool`, default: `false`) Applies vertical mirroring effect to the transition _within each grid cell_. #### Stagger These parameters introduce offsets and flipping to individual grid cells, creating more complex and dynamic grid-based patterns. --- `stagger` (`vec2`, default `(0.0, 0.0)`) Applies a fractional offset to the UV coordinates of alternating grid rows and columns before the transition calculation. This can create a "checkerboard" or "wave" effect when combined with a `grid_size` greater than `(1.0, 1.0)`. _Hint 1: When `grid_size` is `(1.0, 1.0)` or smaller, this just offsets the transition location._ _Hint 2: Due to each element having limited space in its cell, applying a stagger on both axis can lead to interesting patterns._ --- `cumulative_stagger_flip` (`bool`, default `false`) Enabling this allows the `cumulative_stagger` to start from the last row or column. --- `cumulative_stagger` (`vec2`, default `(0.0, 0.0)`) Adds an offset between staggered rows or columns. The stagger amount of each row/column will be calculated based on its index. --- `stagger_frequency` (`ivec2`, default: `(2, 2)`) Determines how often the stagger offset is applied to grid cells. - `x`: Controls the frequency for rows (every `x` rows are staggered). - `y`: Controls the frequency for columns (every `y` columns are staggered). For example, `(1, 0)` staggers every single row, while `(2, 0)` staggers every second row. --- `flip_frequency` (`ivec2`, default: `(1, 1)`) Controls the frequency at which the local UV coordinates within grid cells are flipped (mirrored). - `x`: Flips horizontally every `x` rows. - `y`: Flips vertically every `y` columns. This introduces visual variation and can create interesting symmetrical or asymmetrical patterns within a grid. --- ### Basic Transition Controls These parameters are exclusively relevant when `transition_type` is set to `0` (Basic). --- `basic_feather` (`float`) Controls the softness or blurriness of the transition's edges. --- ### Mask Transition Controls These parameters are exclusively relevant when `transition_type` is set to `1` (Mask). --- `mask_texture` (`sampler2D`) The grayscale image used as a mask for the transition. The white part of the mask shows the current texture and the black part shows the texture (or alpha) transitioned to. --- `use_mask_size` (`bool`) When `false` the mask texture keeps the aspect ratio of the CanvasItem. When `true` the mask texture keeps the aspect ratio of the `mask_size` parameter. For example, if `mask_size` equals `(50, 25)`, the mask aspect ratio will stick to `2:1` _Hint: This is experimental and works best for grids containing a single element. Currently, for larger grids, this introduces horizontal/vertical artifacts because of `fwidth`'s nature._ --- `mask_size` (`vec2`) If `use_mask_size` is `true`, the `mask_texture`'s aspect ratio will be determined by `mask_size.x`:`mask_size.y`. --- ### Shape Transition Controls These parameters are exclusively relevant when `transition_type` is set to `2` (Shape). --- `edges` (`int`, hint: `range(3, 64)`, default: `6`) Specifies the number of sides (edges) of the regular polygon shape that forms the transition boundary. A value of `3` creates a triangle, `4` a square (or diamond if rotated), `5` a pentagon, and so on. Higher values approximate a circle. --- `shape_feather` (`float`, hint: `range(0.0, 10.0)`, default: `0.1`) Controls the softness or blurriness of the polygon's edges. A value of `0.0` results in a perfectly sharp polygon outline, while increasing the value expands the feathered transition zone, creating a softer blend. --- ### Clock Transition Controls These parameters are exclusively relevant when `transition_type` is set to `3` (Clock). --- `sectors` (`int`, hint: `range(1, 128)`, default: `1`) Determines how many radial segments (sectors) the clock-wipe effect is divided into. --- `clock_feather` (`float`, hint: `range(0.0, 16.0)`, default `0.0`) Controls the amount of feathering or blur applied to the edges of the individual clock sectors. A `0.0` value produces sharp, distinct sector edges, while higher values create a smoother transition. --- ### Animation These parameters are the backbone of this shader as they control the timing of the transition. --- `progress` (`float`, default: `0.0`) The primary control for advancing the transition. It is the overall stage of the animation. Typically, `0.0` means the transition has not started, and `1.0` means the transition is complete. (or the other way around if `invert` is set to `true`) _Hint: Sometimes this is not the case. For example, one might provide a mask that's too small and needs values greater than `1.0` to fully cover the `CanvasItem` area. In such cases, play around with the property to figure out when the animation starts and when it ends._ --- `progress_bias` (`vec2`, default: `(0.0, 0.0)`) Applies an additional offset to the progress value for each individual grid cell. - The `x` component biases progress across columns. - the `y` component biases progress across rows. This allows for creating "wave-like" or "staggered" animation effects where grid cells transition at slightly different times based on their position. ## Contributing If you find any bugs, improvement ideas, interesting recipes, feel free to [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#about-forks) this repository and suggest a change. Since the shader is quite simple, exploring and contributing to it can be a valuable learning experience for beginners. If you'd like to see an improvement, but don't know how to contribute, you can [create an Issue](https://github.com/cashew-olddew/Universal-Transition-Shader/issues/new). ### Recipe Contributions To add a new recipe to the [Common Transition Recipes](#common-transition-recipes) section, write down the recipe in the README.md file and add under it one ore more gifs showcasing variations of the transitions. To generate a gif: 1. Configure the AnimationPlayer in the main scene so that at time 1.0 & 1.5 the progress value reaches the value at which the transition completes. 2. [Enable Movie Maker mode](https://docs.godotengine.org/en/stable/tutorials/animation/creating_movies.html#enabling-movie-maker-mode). 3. Run the scene. This will generate a "recipe.avi" file under the [assets/recipes](assets/recipes) folder. 4. Convert the generated .avi file to a .gif. - This is how I generated the other gifs. Feel free to generate them in any way. ```bash ffmpeg -i recipe.avi -vf "fps=24,scale=160:160:flags=lanczos,palettegen" -y palette.png ffmpeg -i recipe.avi -i palette.png -filter_complex "fps=24,scale=160:160:flags=lanczos[x];[x][1:v]paletteuse=dither=none" -y recipe.gif ``` - Recipes with a single gif should have a width of 100px, while recipes with multiple variations should have a width of 80px. 5. Add the generated .gif to the [assets/recipes](assets/recipes) folder and link it to the README.md file similarly to how the others are linked. ## License This project and shader falls under the [CC0](LICENSE) license, meaning that you can do anything you want with the code here, even use it commercially. You do not have any obligation to credit me, but doing so would be highly appreciated. ## Support [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H2XSCXW) Donations are appreciated and help me continue creating free content. Please donate only what you can afford. 🥜 ================================================ FILE: assets/day.jpg.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://bbvtgdx0l2gs2" path="res://.godot/imported/day.jpg-38d54002cde6a7513e5dd5fa3f3756ba.ctex" metadata={ "vram_texture": false } [deps] source_file="res://assets/day.jpg" dest_files=["res://.godot/imported/day.jpg-38d54002cde6a7513e5dd5fa3f3756ba.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: assets/icon.svg.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://bg4r54wgjqnhg" path="res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex" metadata={ "vram_texture": false } [deps] source_file="res://assets/icon.svg" dest_files=["res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 editor/scale_with_editor_scale=false editor/convert_colors_with_editor_theme=false ================================================ FILE: assets/mask.png.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://cjgwxc45vn007" path="res://.godot/imported/mask.png-9f44758278b9ba6cd146ee809a9389e4.ctex" metadata={ "vram_texture": false } [deps] source_file="res://assets/mask.png" dest_files=["res://.godot/imported/mask.png-9f44758278b9ba6cd146ee809a9389e4.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: assets/night.jpg.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://dtcrggrkee5tb" path="res://.godot/imported/night.jpg-1829c8ff2570590826aeda305085a4e0.ctex" metadata={ "vram_texture": false } [deps] source_file="res://assets/night.jpg" dest_files=["res://.godot/imported/night.jpg-1829c8ff2570590826aeda305085a4e0.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: main.tscn ================================================ [gd_scene format=3 uid="uid://bkij6lxw3m3qx"] [ext_resource type="Shader" uid="uid://ddxgjcn15adry" path="res://transition.gdshader" id="1_0xm2m"] [ext_resource type="Texture2D" uid="uid://bg4r54wgjqnhg" path="res://assets/icon.svg" id="1_ig7tw"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_h2yge"] shader = ExtResource("1_0xm2m") shader_parameter/use_sprite_alpha = true shader_parameter/use_transition_texture = false shader_parameter/transition_type = 0 shader_parameter/position = Vector2(0, 0) shader_parameter/invert = false shader_parameter/grid_size = Vector2(1, 1) shader_parameter/rotation_angle = 0.0 shader_parameter/global_x_mirror = false shader_parameter/global_y_mirror = false shader_parameter/local_x_mirror = false shader_parameter/local_y_mirror = false shader_parameter/stagger = Vector2(0, 0) shader_parameter/cumulative_stagger_flip = false shader_parameter/cumulative_stagger = Vector2(0, 0) shader_parameter/stagger_frequency = Vector2i(2, 2) shader_parameter/flip_frequency = Vector2i(1, 1) shader_parameter/basic_feather = 0.0 shader_parameter/use_mask_size = false shader_parameter/mask_size = Vector2(100, 100) shader_parameter/edges = 6 shader_parameter/shape_feather = 0.1 shader_parameter/sectors = 1 shader_parameter/clock_feather = 0.0 shader_parameter/progress = 2.0 shader_parameter/progress_bias = Vector2(0, 0) [sub_resource type="Animation" id="Animation_0xm2m"] length = 0.001 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath("Transition:material:shader_parameter/progress") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 0, "values": [2.0] } tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true tracks/1/path = NodePath("AnimationPlayer:movie_quit_on_finish") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 1, "values": [false] } [sub_resource type="Animation" id="Animation_h2yge"] resource_name = "transition" length = 3.0 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath("Transition:material:shader_parameter/progress") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0, 1, 1.5, 2.5), "transitions": PackedFloat32Array(1, 1, 1, 1), "update": 0, "values": [0.0, 2.0, 2.0, 0.0] } tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true tracks/1/path = NodePath("AnimationPlayer:movie_quit_on_finish") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { "times": PackedFloat32Array(3), "transitions": PackedFloat32Array(1), "update": 1, "values": [true] } [sub_resource type="AnimationLibrary" id="AnimationLibrary_1bvp3"] _data = { &"RESET": SubResource("Animation_0xm2m"), &"transition": SubResource("Animation_h2yge") } [node name="Main" type="Control" unique_id=26648888] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 [node name="Background" type="ColorRect" parent="." unique_id=874746378] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 color = Color(0.050980393, 0.06666667, 0.09019608, 1) [node name="Transition" type="TextureRect" parent="." unique_id=1517388025] material = SubResource("ShaderMaterial_h2yge") layout_mode = 1 anchors_preset = 8 anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 offset_left = -64.0 offset_top = -64.0 offset_right = 64.0 offset_bottom = 64.0 grow_horizontal = 2 grow_vertical = 2 texture = ExtResource("1_ig7tw") [node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=39034180] libraries/ = SubResource("AnimationLibrary_1bvp3") autoplay = &"transition" ================================================ FILE: project.godot ================================================ ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. ; ; Format: ; [section] ; section goes between [] ; param=value ; assign values to parameters config_version=5 [animation] compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="Universal Transition Shader" run/main_scene="uid://bkij6lxw3m3qx" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://assets/icon.svg" [display] window/size/viewport_width=160 window/size/viewport_height=160 [editor] movie_writer/movie_file="E:/Projects/Games/Universal-Transition-Shader/assets/recipes/recipe.avi" ================================================ FILE: transition.gdshader ================================================ shader_type canvas_item; uniform bool use_sprite_alpha = true; uniform bool use_transition_texture = false; uniform sampler2D transition_texture; uniform int transition_type: hint_enum("Basic", "Mask", "Shape", "Clock") = 0; group_uniforms positioning; uniform vec2 position = vec2(0,0); uniform bool invert = false; uniform vec2 grid_size = vec2(1.0, 1.0); uniform float rotation_angle = 0.0; group_uniforms positioning.mirror; uniform bool global_x_mirror = false; uniform bool global_y_mirror = false; uniform bool local_x_mirror = false; uniform bool local_y_mirror = false; group_uniforms positioning.stagger; uniform vec2 stagger = vec2(0.0, 0.0); uniform bool cumulative_stagger_flip = false; uniform vec2 cumulative_stagger = vec2(0, 0); uniform ivec2 stagger_frequency = ivec2(2, 2); uniform ivec2 flip_frequency = ivec2(1, 1); group_uniforms basic_transition_controls; uniform float basic_feather = 0.0; group_uniforms mask_transition_controls; uniform sampler2D mask_texture; uniform bool use_mask_size = false; uniform vec2 mask_size = vec2(100.0); group_uniforms shape_transition_controls; uniform int edges : hint_range(3, 64) = 6; // default hexagon uniform float shape_feather : hint_range(0.0, 10.0) = 0.1; group_uniforms clock_transition_controls; uniform int sectors : hint_range(1, 128) = 1; uniform float clock_feather : hint_range(0.0, 16.0) = 0.0; group_uniforms animation; uniform float progress = 0.0; uniform vec2 progress_bias = vec2(0.0); varying vec4 modulate; vec2 use_actual_texture_size(vec2 uv, vec2 texture_size, vec2 raw_uv_deriv) { uv -= 0.5; float screen_ratio = raw_uv_deriv.x / raw_uv_deriv.y; float texture_ratio = texture_size.x / texture_size.y; float mixed_ratio = texture_ratio * screen_ratio; if (screen_ratio > texture_ratio) { uv.x /= mixed_ratio; } else { uv.y *= mixed_ratio; } return uv + 0.5; } vec2 get_stagger_offset(vec2 grid) { vec2 cells = floor(grid); float offset_row = mod(cells.y, float(stagger_frequency.x)) == 0.0 ? 0.5 : 0.0; float offset_col = mod(cells.x, float(stagger_frequency.y)) == 0.0 ? 0.5 : 0.0; float row_index = cumulative_stagger_flip ? (grid_size.y - 1.0 - cells.y) : cells.y; float col_index = cumulative_stagger_flip ? (grid_size.x - 1.0 - cells.x) : cells.x; return vec2( offset_row * (stagger.x + row_index * cumulative_stagger.x), offset_col * (stagger.y + col_index * cumulative_stagger.y) ); } vec2 get_grid_flip(vec2 grid) { vec2 cells = floor(grid); float flip_row = mod(cells.y, float(flip_frequency.x)) == 0.0 ? 1.0 : -1.0; float flip_col = mod(cells.x, float(flip_frequency.y)) == 0.0 ? 1.0 : -1.0; return vec2( flip_row * grid.x, flip_col * grid.y ); } vec2 rotate(vec2 v, float angle) { return mat2( vec2(cos(angle), sin(angle)), vec2(-sin(angle), cos(angle)) ) * v; } vec2 get_edges(float center, float width) { float half_width = width * 0.5; float edge0 = center - half_width; float edge1 = center + half_width + 1e-5; return vec2(edge0, edge1); } float get_local_progress(vec2 grid) { vec2 cell = floor(grid); // Easier to control bias values vec2 pretty_bias = progress_bias / 10.0; float offset = dot(cell, pretty_bias); return progress - offset; } vec2 apply_mirror(vec2 uv, bool mirror_x, bool mirror_y) { vec2 mirrored = 0.5 - abs(uv - 0.5); vec2 toggle = vec2(float(mirror_x), float(mirror_y)); return mix(uv, mirrored, toggle); } void vertex() { modulate = COLOR; } void fragment() { vec2 uv_deriv = fwidth(UV); // derivate used for keeping mask texture size regardless of sprite ratio vec2 mirrored_uv = apply_mirror(UV, global_x_mirror, global_y_mirror); vec2 grid = mirrored_uv * grid_size; vec2 offset_uv = grid + get_stagger_offset(grid); vec2 grid_flipped_uv = get_grid_flip(offset_uv); vec2 tiled_uv = fract(grid_flipped_uv); tiled_uv = apply_mirror(tiled_uv, local_x_mirror, local_y_mirror); vec2 uv = (tiled_uv - position) * 2.0; uv = rotate(uv, radians(rotation_angle)); float local_progress = get_local_progress(grid); COLOR = texture(TEXTURE, UV) * modulate; float alpha = COLOR.a; float transition_progress = 0.0; if (transition_type == 1) { vec2 mask_zoom_uv = uv / local_progress; vec2 mask_zoom_uv_01 = mask_zoom_uv * 0.5 + 0.5; mask_zoom_uv_01 = use_mask_size ? use_actual_texture_size(mask_zoom_uv_01, mask_size, uv_deriv) : mask_zoom_uv_01; transition_progress = texture(mask_texture, mask_zoom_uv_01).r; } else if(transition_type == 2) { float radius = length(uv); float angle = atan(uv.y, uv.x); float sector_angle = 2.0 * PI / float(edges); float half_sector = sector_angle / 2.0; // Define polygon sectors using the power of trigonometry float d = cos(half_sector) / cos(mod(angle + half_sector, sector_angle) - half_sector); vec2 smooth_edges = get_edges(local_progress, shape_feather); transition_progress = smoothstep(smooth_edges.x, smooth_edges.y, radius / d); } else if (transition_type == 3) { float radius = length(uv); float angle = atan(uv.y, uv.x); float sector_angle = 2.0 * PI / float(sectors); float half_sector = sector_angle / 2.0; angle = mod(angle - half_sector, sector_angle); float progress_angle = local_progress * 2.0 * PI / float(sectors); float smooth_angle = smoothstep(0.0, clock_feather, progress_angle - angle); transition_progress = smooth_angle; } else { vec2 smooth_edges = get_edges(local_progress, basic_feather); float separation_x = smoothstep(smooth_edges.x, smooth_edges.y, abs(uv.x)); float separation_y = smoothstep(smooth_edges.x, smooth_edges.y, abs(uv.y)); transition_progress = max(separation_x, separation_y); } alpha = invert ? 1.0 - transition_progress : transition_progress; alpha = use_sprite_alpha ? min(COLOR.a, alpha) : alpha; vec4 transition_color = texture(transition_texture, UV); if (use_transition_texture) { vec4 chosen_color = mix(transition_color, COLOR, alpha); COLOR = chosen_color; } else { COLOR.a = alpha; } } ================================================ FILE: transition.gdshader.uid ================================================ uid://ddxgjcn15adry