Compare commits

..

109 commits
v3.3 ... main

Author SHA1 Message Date
54cbddbbba
Bump version to v10.2 2025-04-09 19:18:09 +02:00
65fddc0bd8
Update README regarding STEPS uniform 2025-04-09 19:09:32 +02:00
ebc8ce2cb9
Add STEPS uniform to get the number of steps programmatically 2025-04-09 19:04:11 +02:00
c9c5428dbc
shaderlib: Add rotateUV function to common.gdshader 2025-04-09 19:00:02 +02:00
4d0f7ca538
Bump version to v10.1 2025-04-01 21:42:38 +02:00
3dcc4ca02c
shaderlib: Add multipass pixelsorting 2025-04-01 21:37:32 +02:00
bf1c1327ac
Add better documentation of value ranges in oklab and oklch 2025-03-31 21:24:56 +02:00
560e455cd2
Disable VSync while ImageCompositor.update processes the image 2025-03-31 21:24:07 +02:00
77273f9c1b
Add sobel filter (edge detection) 2025-03-31 15:41:31 +02:00
9d23efea63
Fix: Show and align image viewport before first step 2025-03-31 15:39:36 +02:00
4278cde44b
Disable low_processor_mode in the settings to improve multistep performance 2025-03-31 15:37:55 +02:00
f940061135
Declare shaderlib as stable, bump version to v10.0 2025-03-15 20:45:30 +01:00
4546b9eac0
fix: re-enable buttons after image export 2025-03-15 20:40:11 +01:00
e7bb3c8f16
Update example project and export_presets.cfg for Godot 4.4 2025-03-15 18:16:02 +01:00
46829e72e9
Bump version to v9.1 2025-03-13 21:55:57 +01:00
df85ceb469
Update screenshot 2025-03-13 21:55:27 +01:00
b6f6c3d27e
Change background and fix image viewport outline 2025-03-13 21:41:01 +01:00
9b1efc1513
Update to Godot 4.4 2025-03-13 20:57:31 +01:00
9192e020e3
Update screenshot 2025-02-06 23:39:58 +01:00
f32d10dbaa
Update README 2025-02-06 23:29:10 +01:00
c5c9b9f516
Merge pull request #53 from ChaoticByte/release/v9.0
Release/v9.0
2025-02-06 23:02:03 +01:00
b916f43f8b
Fix shader include relative include by using ResourceLoader load() instead of loading the text from the file - fixes #56 2025-02-06 19:54:20 +01:00
ed10da0604
Fix res:// import in shaderlib/oklab.gdshaderinc 2025-02-05 23:52:32 +01:00
89b82695ac
Added empty scene to the project temlate/examples folder so that Godot shuts up about no scene being opened currently 2025-02-05 18:06:08 +01:00
f7aac231ae
Delete .gitkeep in dist folder, create it on demand 2025-02-05 17:50:41 +01:00
cc59ba9b9e
Fix missing STEP variable by making it mandatory to be defined as a uniform in the shader file 2025-02-05 17:48:49 +01:00
7ad9ef002a
Update README 2025-02-04 19:05:20 +01:00
6a7dc164fe
Revert "CLI dump-shaderlib: Update help message"
This reverts commit d29f14c7c9.
2025-02-04 19:04:10 +01:00
ddba85d17c
Revert "Add a method to dump the shaderlib to the local filesystem - implements #47"
This reverts commit a4a1de4bfe.
2025-02-04 19:00:31 +01:00
da1bd6a58a
Don't include the shaderlib in the application anymore, instead distribute a zip file with each release 2025-02-04 18:59:57 +01:00
1ee3088cce
Update screenshot 2025-02-03 20:40:46 +01:00
6bb6b8b768
Add dist.sh script to automatically build and create project template, Update README 2025-02-03 20:30:27 +01:00
5a8a6e4c61
Fixed commandline: apply 2025-02-03 19:25:48 +01:00
9f5011aaac
Removed all editor functionality and improved new viewer-only UI #54 2025-02-03 17:40:56 +01:00
e40ab18683
Update README regarding commandline interface 2025-01-31 20:27:14 +01:00
d29f14c7c9
CLI dump-shaderlib: Update help message 2025-01-31 20:24:40 +01:00
a4a1de4bfe
Add a method to dump the shaderlib to the local filesystem - implements #47 2025-01-31 20:20:35 +01:00
168cb036be
Clean up Main.gd, improve CLI and rename 'cli' command to 'apply' - implements #48 2025-01-31 19:51:30 +01:00
fee1c64775
Merge pull request #52 from ChaoticByte/release/v8.2
Release/v8.2
2025-01-28 21:46:52 +01:00
60332eadaf
Bump version to 8.2 2025-01-28 21:44:33 +01:00
d376801058
Give credits for example image mountain.jpg 2025-01-27 23:13:21 +01:00
632b4593aa
Add Kuwahara Filter, implements #49 2025-01-27 23:09:07 +01:00
d512005b86
Merge pull request #51 from ChaoticByte/release/v8.1
Release/v8.1
2025-01-27 22:59:37 +01:00
a444cea11a
Bump version to v8.1 2025-01-27 22:58:18 +01:00
f8cfbf91c9
Fixed out-of-date shaderlib autocomplete 2025-01-27 22:57:01 +01:00
b3044f64a6
Merge pull request #46 from ChaoticByte/release/v8.0
Release/v8.0
2025-01-27 18:19:16 +01:00
c0d9e00fe9
Bump version to 8.0 2025-01-27 18:17:01 +01:00
7810a1fa83
cli: added support for processing whole directories (batch mode) - this implements #40; changed build template to support more image formats 2025-01-26 21:38:16 +01:00
40374bd849
Restructured shaderlib by moving functions and renaming files 2025-01-24 22:16:45 +01:00
c70eaed0c4
Readme: Improved the screenshot subtitle and Usage section, added a link to the Godot Shader documentation - fixes #44 2025-01-24 21:56:37 +01:00
acdb52c91e
Readme: add a toc, add a Known Issues section and add a note about screen scaling not being supported (issue #45) 2025-01-24 21:44:41 +01:00
dc325d3e77
Add note about shaderlib API stability 2025-01-22 07:19:56 +01:00
701efcaed6
Merge pull request #43 from ChaoticByte/release/v7.0
Release/v7.0
2025-01-19 13:00:29 +01:00
6419ab1c3c
Bump version to 7.0 and update screenshot 2025-01-19 12:57:31 +01:00
0fad7cb575
Implement a commandline interface that can be used in scripts - implements #42 2025-01-19 12:51:10 +01:00
1a21589fc1
Implement oklab, oklch color space conversion functions, add example, restructure comments in the shaderlib, implements #37 2025-01-17 16:51:17 +01:00
35959290d3
Update LICENSE (fix name) 2025-01-17 15:37:57 +01:00
88e85c19f5
Switch to BSD-3-Clause lincense, closes #41 2025-01-17 15:35:36 +01:00
d08329c750
shaderlib: Implement gaussian_blur(); implements #39 2025-01-11 15:59:58 +01:00
85d33d5d77
Refactored Main/%Compositor into a standalone ImageCompositor class; implements #38 2025-01-10 22:18:43 +01:00
3b47d57353
shaderlib: Remove hsv_multiply and hsv_offset, rename hsv.gdshaderinc to colorspaces.gdshaderinc, update autocomplete and example accordingly; implements #36 2025-01-10 21:58:37 +01:00
50ddf6e6d3
Merge pull request #35 from ChaoticByte/release/v6.2
Release/v6.2
2025-01-10 20:58:28 +01:00
a7da554320
Bump version to 6.2 2025-01-10 20:57:04 +01:00
9b6d5d0a78
shaderlib: Add smart_denoise function, add example, move example images into separate folder; implements #29 2025-01-08 20:53:45 +01:00
7b72274140
Don't try to load the shader code if the file couldn't be opened 2025-01-08 19:43:27 +01:00
4d12ad4432
shaderlib: Implement a simple pixelate function (effects.gdshaderinc), examples/hsv.gdshader -> color_and_pixelate.gdshader; implements #30 2025-01-08 19:41:03 +01:00
6c48c9fe72
shaderlib: Shorten lowpass filter example 2025-01-07 22:50:15 +01:00
974c40fcb4
Remember last opened file and open it on start, implements #33 2025-01-07 22:46:13 +01:00
ec7544cb3b
Add autocomplete support for preprocessor directives, implements #34 2025-01-07 22:19:08 +01:00
71394edbf1
Disable Export Button during Composite applying shader, implements #31 2025-01-07 21:58:20 +01:00
fd16d2a8df
Assign ShaderMaterial outside of loop 2025-01-07 21:53:15 +01:00
4787810651
Merge pull request #32 from ChaoticByte/release/v6.1
Release/v6.1
2025-01-06 23:00:35 +01:00
1fba2ee826
Bump version to v6.1 2025-01-06 22:55:17 +01:00
2646f3da9b
Minor typo Compositor.gd 2025-01-06 22:26:53 +01:00
0ddfa74bb4
Call camera.fit_image() only once in $Compositor.update() 2025-01-06 22:24:43 +01:00
885cfbf3bc
Pass a String to the shader directive parser functions instead of Shader 2025-01-06 22:22:42 +01:00
6c1ec9ed2c
Rename example rgb_uv_distort to multistep_distort 2025-01-06 22:15:00 +01:00
1d9f838668
Add //!steps directive, some code cleanup, updated README & examples 2025-01-06 22:11:53 +01:00
1b87cafb13
Merge pull request #27 from ChaoticByte/release/v6
Release/v6
2025-01-01 21:57:15 +01:00
01e17bd6e3
Bump version to v6.0, update & improve README, add new screenshot, and more; closes #19 2025-01-01 21:43:32 +01:00
afea5c9727
Update the window title when saving the shader file - fix #26 2024-12-30 18:34:51 +01:00
1635cd5b82
shaderlib: Add alpha_blend function, extend place_texture example, update autocomplete - implements #23 2024-12-30 17:02:19 +01:00
3abaf38400
Add shaderlib function to autocomplete with, check which files are included - implements #25 2024-12-29 21:32:54 +01:00
7f1415dada
Remove some obsolete autocomplete keywords - closes #18 2024-12-29 21:07:32 +01:00
5b939a3d20
Improve size of status indicator 2024-12-29 20:13:59 +01:00
11ba75517c
Implement a naive way of checking if the shader compilation failed - implements 1/2 of #16 2024-12-29 19:45:57 +01:00
bbbd35f26d
Update window title with current shader filename - implements #22 2024-12-28 00:12:40 +01:00
1aa217776c
shaderlib: Implement place_texture() and provide an example, replace mix.gdshader example; implements #17 2024-12-28 00:00:46 +01:00
8b5a1cc37d
Update image outline when new image is loaded 2024-12-27 23:54:38 +01:00
bae23b0ee6
Rename ImageViewport to Compositor 2024-12-26 22:28:58 +01:00
8394ad9d3c
Added an error indicator and error message box and refactored some code in the process - implements #21, prepares #16 2024-12-26 22:18:18 +01:00
ca40971e53
Add a 'New' and 'Save As' button and improve save behaviour, additional minor improvements - fix #20 2024-12-25 00:08:25 +01:00
6b748229ed
Add hsv.gdshaderinc to shaderlibrary, provide an example and update README.md - implement #12 2024-12-22 23:05:03 +01:00
862ef075ff
Add a custom godot build template with reduced features for smaller exports - implements #15 2024-12-22 21:55:33 +01:00
288ddcbc5b
Add a file exclusion list to export preset - fix #14 2024-12-22 19:07:07 +01:00
80d937cb64
Fix typo in README 2024-12-21 18:27:21 +01:00
ad6d2add1b
Cleaned up and refactored some code 2024-12-21 18:25:11 +01:00
57328bc9a3
Place the viewer and editor in separate windows 2024-12-21 17:08:26 +01:00
f069f2911f
Add an outline to the result preview 2024-12-21 17:08:15 +01:00
815ec239f6
Merge pull request #10 from ChaoticByte/release/v5
Release/v5
2024-12-19 19:47:52 +01:00
a9a90235d9
Make UI a little more compact and define a minimum window size 2024-12-19 19:14:39 +01:00
8653b3740e
Update the example in the README 2024-12-19 19:03:00 +01:00
d61dac7acf
Added a new screenshot 2024-12-19 19:01:49 +01:00
4b28a6427e
Removed presets, load all images via load directives (implements #9), removed obsolete code, added some improvements 2024-12-19 18:52:47 +01:00
53d42de4a5
Rename to Fragmented 2024-12-19 16:48:32 +01:00
58323f3d88
Bump version to v4.0 2024-06-21 10:41:58 +02:00
f0cd2d811f
Enable transparent background for SubViewport - fixes #8 2024-06-21 10:41:05 +02:00
d866fd1fe8
Get content for Save from code_editor.text instead of Globals.shader.code - fixes #6 2024-06-21 10:38:31 +02:00
367dbe163c
Store shader directory as working directory and use this as the root for relative uniform image imports via !load directive, drop Windows compatibility - solves #7 2024-06-21 10:33:34 +02:00
107 changed files with 2183 additions and 792 deletions

3
.gitignore vendored
View file

@ -13,8 +13,9 @@ data_*/
mono_crash.*.json mono_crash.*.json
# Builds # Builds
*.x86_64
godot.*.template_release.*
dist/* dist/*
!dist/.gitkeep
screenshot.png.import screenshot.png.import

41
LICENSE
View file

@ -1,21 +1,28 @@
MIT License BSD 3-Clause License
Copyright (c) 2024 Julian MĂĽller (ChaoticByte) Copyright (c) 2025, Julian MĂĽller (ChaoticByte)
Permission is hereby granted, free of charge, to any person obtaining a copy Redistribution and use in source and binary forms, with or without
of this software and associated documentation files (the "Software"), to deal modification, are permitted provided that the following conditions are met:
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 1. Redistributions of source code must retain the above copyright notice, this
copies or substantial portions of the Software. list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2. Redistributions in binary form must reproduce the above copyright notice,
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, this list of conditions and the following disclaimer in the documentation
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE and/or other materials provided with the distribution.
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 3. Neither the name of the copyright holder nor the names of its
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE contributors may be used to endorse or promote products derived from
SOFTWARE. this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

145
README.md
View file

@ -1,39 +1,156 @@
<h1 align=center>GlitchApp</h1> <h1 align=center>Fragmented</h1>
![screenshot](./screenshot.png) ![screenshot](./screenshot.png)
<p align=center>Create image filters by writing shaders.</p> <p align=center>An image editing/compositing software for graphics programmers.</p>
## Table of Contents
- [Supported Platforms](#supported-platforms)
- [Usage](#usage)
- [Shaderlib](#shaderlib)
- [Commandline interface](#commandline-interface)
- [Known Issues](#known-issues)
## Supported Platforms ## Supported Platforms
- Linux [tested] - Linux
- Windows
You can find the latest releases [here](https://github.com/ChaoticByte/GlitchApp/releases/latest). You can find the latest releases [here](https://github.com/ChaoticByte/Fragmented/releases/latest).
## Usage ## Usage
The application includes presets. You can use them as a starting-point to write your own filters. With Fragemented, you are processing images with GDShaders. This brings almost endless opportunities to create unique art.
Just load an image, apply a preset, edit the code and hit `F5` to see the changes. If you want to learn GDShader, take a look at the [Godot docs](https://docs.godotengine.org/en/stable/tutorials/shaders/).
### Load additional images using the `//!load` directive **The builtin editor got removed** from Fragmented with version **v9.0**. I advise you to write your shaders directly in the Godot Editor.
**To get started, use the project template (see the Releases section of this repo) and open it in Godot.**
The template includes many examples. You can use them as a starting-point to write your own stuff.
Besides the regular GDShader stuff, Fragmented has so-called directives. Those allow to further control the behaviour of the application. **The most important directive is `//!load` to load an image.**
### Load TEXTURE using the `//!load` directive
```glsl ```glsl
//!load <name> <filepath> //!load <filepath>
```
The main image file will be read and available as the sampler2D `TEXTURE`.
#### Load additional images
```glsl
//!load+ <name> <filepath>
uniform sampler2D <name>; uniform sampler2D <name>;
``` ```
With this you can load additional images into your shader. Have a look at the `place_texture.gdshader` example.
Have a look at the `Mix` preset:
### Have multiple steps with `//!steps n`
You can apply your shaderfile multiple times. At every additional step, `TEXTURE` is the result of the previous step. This can be used to chain effects that cannot be easily chained otherwise.
To query the current step index, a `STEP` uniform is automatically injected. If `steps` is set to `0`, your shader won't be applied at all.
Example:
```glsl
//!load ...
//!steps 5
uniform int STEP;
uniform int STEPS;
void fragment() {
if (STEP == 0) {
...
} else if (STEP == 1) {
...
} else if (STEP == STEPS-1) {
...
}
}
```
## Shaderlib
This repo comes with a (still small) shader library including pre-written functions and more.
Have a look at the `shaderlib` folder.
Here is an example:
```glsl ```glsl
shader_type canvas_item; shader_type canvas_item;
//!load img2 ./icon.png #include "./shaderlib/oklab.gdshaderinc"
uniform sampler2D img2: repeat_enable, filter_nearest;
//!load ./images/swamp.jpg
void fragment() { void fragment() {
COLOR = mix(COLOR, texture(img2, UV), .5); vec4 oklab = rgb2oklab(COLOR);
vec4 oklch = oklab2oklch(oklab);
oklch.z -= 2.0;
COLOR = oklab2rgb(oklch2oklab(oklch));
} }
``` ```
## Commandline interface
You can run Fragmented from the commandline or scripts.
> Note: Headless mode is not supported. Using the commandline interface still opens a window.
### Usage
```
~ Fragmented CLI ~
-================-
Usage:
./Fragmented <command> <args...>
Commands:
help
| Shows this help text.
apply --shader PATH [--load-image PATH]
| Applies a shader file.
--shader PATH The path to the shader
--output PATH Where to write the resulting image to.
In batch mode, this must be a folder.
--load-image PATH The path to the image. This will overwrite the
load directive of the shader file.
Passing a folder activates batch mode.
(optional)
```
### Batch Mode
Since version v8.0, you can pass a directory to `--load-image` and `--output`. This will process all images in the input directory and write the output to the output directory.
> Note: You *can* use this feature for video frames, but it will take a loooong time.
#### Examples
```
./Fragmented apply --shader ./examples/oklab.gdshader --output ./output.png
```
```
./Fragmented apply --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
```
## Known Issues
- screen scaling is unsupported; Using screen scaling could lead to an either blurry UI, or no scaling at all -> see #45
- commandline interface: `--headless` is not supported

View file

@ -0,0 +1,20 @@
MAINTAINER ChaoticByte
# Using Ubuntu 20.04
FROM docker.io/ubuntu:focal AS os-base
# https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_for_linuxbsd.html
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -yq python3-pip git build-essential pkg-config libx11-dev libxcursor-dev libxinerama-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev libwayland-dev
RUN pip3 install --system scons
FROM os-base AS clone-src
RUN git clone https://github.com/godotengine/godot.git -b 4.4-stable /godot-src
FROM clone-src
WORKDIR /godot-src
ENTRYPOINT scons platform=linuxbsd target=template_release lto=full optimize=size disable_3d=yes module_text_server_adv_enabled=no module_text_server_fb_enabled=yes module_basis_universal_enabled=no module_csg_enabled=no module_enet_enabled=no module_gridmap_enabled=no module_jsonrpc_enabled=no module_mbedtls_enabled=no module_meshoptimizer_enabled=no module_minimp3_enabled=no module_mobile_vr_enabled=no module_msdfgen_enabled=no module_multiplayer_enabled=no module_navigation_enabled=no module_ogg_enabled=no module_openxr_enabled=no module_raycast_enabled=no module_squish_enabled=no module_theora_enabled=no module_upnp_enabled=no module_vhacd_enabled=no module_vorbis_enabled=no module_webrtc_enabled=no module_websocket_enabled=no module_webxr_enabled=no arch=x86_64 && strip bin/godot.linuxbsd.template_release.x86_64

29
build-template/build.sh Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -e
function log {
echo -e "\033[1;36m***** $@ *****\033[0m"
}
log " "
log "Fragmented - Godot Build Template Builder"
log " "
cd $(dirname $0)
log Switched to $(pwd)
tmpsuffix=$(date +%s%N)
image_name=fragmented-godot-template-builder
container_name=${image_name}-${tmpsuffix}
output_file=godot.linuxbsd.template_release.x86_64
log Building image ${image_name} ...
buildah build -t ${image_name}
log Building godot build template with container ${container_name} ...
podman run --name ${container_name} localhost/${image_name}:latest
log Copying ${output_file} from container to $(realpath ./${output_file})
podman cp ${container_name}:/godot-src/bin/${output_file} ./${output_file}
log Removing container ${container_name}
podman container rm ${container_name}
log Done :D

30
dist.sh Executable file
View file

@ -0,0 +1,30 @@
set -e
function log {
echo -e "\033[1;36m***** $@ *****\033[0m"
}
mkdir -p dist
log Building application
VERSION="$(godot --headless --no-header -s tools/get_version.gd)"
godot --headless --export-release "Linux/X11" "dist/Fragmented-${VERSION}.x86_64"
log Packing shaderlib
ZIP_PATH_SHADERLIB=$(realpath "dist/Fragmented-${VERSION}_shaderlib.zip")
zip -r "${ZIP_PATH_SHADERLIB}" shaderlib/
log Packing project template
ZIP_PATH_PROJECT_TEMPLATE=$(realpath "dist/Fragmented-${VERSION}_project_template.zip")
rm -f "${ZIP_PATH_PROJECT_TEMPLATE}"
(
cd examples/
mv project.godot_ project.godot && trap "mv project.godot project.godot_" EXIT
zip -r "${ZIP_PATH_PROJECT_TEMPLATE}" *
)

0
dist/.gitkeep vendored
View file

3
examples/0_empty.tscn Normal file
View file

@ -0,0 +1,3 @@
[gd_scene format=3 uid="uid://db2rhq8rwv5wo"]
[node name="Node" type="Node"]

8
examples/blur.gdshader Normal file
View file

@ -0,0 +1,8 @@
shader_type canvas_item;
//!load ./images/swamp.jpg
#include "./shaderlib/blur.gdshaderinc"
void fragment() {
COLOR = gaussian_blur(TEXTURE, UV, 48, 24.0);
}

View file

@ -0,0 +1 @@
uid://cny8dtukv54wt

View file

@ -1,5 +1,7 @@
shader_type canvas_item; shader_type canvas_item;
//!load ./images/swamp.jpg
const vec2 offset_r = vec2(-0.002, -0.002); const vec2 offset_r = vec2(-0.002, -0.002);
const vec2 offset_g = vec2(0., 0.); const vec2 offset_g = vec2(0., 0.);
const vec2 offset_b = vec2(0.002, 0.002); const vec2 offset_b = vec2(0.002, 0.002);

View file

@ -0,0 +1 @@
uid://0efk4fornlg6

View file

@ -0,0 +1,14 @@
shader_type canvas_item;
#include "./shaderlib/hsv.gdshaderinc"
#include "./shaderlib/pixelate.gdshaderinc"
//!load ./images/swamp.jpg
void fragment() {
COLOR = pixelate(TEXTURE, UV, 200.0);
vec4 hsv = rgb2hsv(COLOR);
hsv.xyz += vec3(0.65, .42-(hsv.y*.3), -.125);
hsv.xyz *= vec3(1.0, 1.0, 1.25);
COLOR = hsv2rgb(hsv);
}

View file

@ -0,0 +1 @@
uid://gd23hu7ro148

View file

@ -0,0 +1,9 @@
shader_type canvas_item;
//!load ./images/noisy.png
#include "./shaderlib/denoise.gdshaderinc"
void fragment() {
COLOR = smart_denoise(TEXTURE, UV, 12.0, 1.0, .12);
}

View file

@ -0,0 +1 @@
uid://cbwyneu03fki6

View file

@ -1,10 +1,10 @@
shader_type canvas_item; shader_type canvas_item;
//!load ./images/swamp.jpg
void fragment() { void fragment() {
vec4 tex = texture(TEXTURE , UV);
float b = (COLOR.r + COLOR.g + COLOR.b) / 3.0; float b = (COLOR.r + COLOR.g + COLOR.b) / 3.0;
COLOR.r = b; COLOR.r = b;
COLOR.g = b; COLOR.g = b;
COLOR.b = b; COLOR.b = b;
COLOR.a = tex.a;
} }

View file

@ -0,0 +1 @@
uid://dvarqolt6es27

View file

@ -0,0 +1,6 @@
# Example Images
- swamp.jpg by [clfr21 on Pixabay](https://pixabay.com/de/users/clfr21-6530007/)
- grass.png by [GDJ on Pixabay](https://pixabay.com/users/gdj-1086657/)
- mountain.jpg by [Phghvvcftyyufj on Pixabay](https://pixabay.com/users/phghvvcftyyufj-12646982)

BIN
examples/images/grass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c1mh1d2f3u4ju"
path="res://.godot/imported/grass.png-61a458998da568ce60ccb8a0c7caaf6d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://examples/images/grass.png"
dest_files=["res://.godot/imported/grass.png-61a458998da568ce60ccb8a0c7caaf6d.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ben72llmopgaj"
path="res://.godot/imported/mountain.jpg-c1b7de1e6557b826bc6f9324027e11af.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://examples/images/mountain.jpg"
dest_files=["res://.godot/imported/mountain.jpg-c1b7de1e6557b826bc6f9324027e11af.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

BIN
examples/images/noisy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cfe2d0qes5x87"
path="res://.godot/imported/noisy.png-1b2e79340785c6c0f50d5bad5ce97356.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://examples/images/noisy.png"
dest_files=["res://.godot/imported/noisy.png-1b2e79340785c6c0f50d5bad5ce97356.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

BIN
examples/images/swamp.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ckjb0agn5btv7"
path="res://.godot/imported/swamp.jpg-1dfdcd52a5ef03d42a82a7f06acefa98.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://examples/images/swamp.jpg"
dest_files=["res://.godot/imported/swamp.jpg-1dfdcd52a5ef03d42a82a7f06acefa98.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

View file

@ -0,0 +1,16 @@
shader_type canvas_item;
//!load ./images/mountain.jpg
#include "./shaderlib/kuwahara.gdshaderinc"
#include "./shaderlib/hsv.gdshaderinc"
void fragment() {
// Kuwahara
COLOR.rgb = kuwahara(TEXTURE, UV, 20, 80.0, 18.0, 0.6, .15, 8);
// A litte bit of color adjustments
vec4 hsv = rgb2hsv(COLOR);
hsv.x += .03;
hsv.y *= 1.4;
COLOR = hsv2rgb(hsv);
}

View file

@ -0,0 +1 @@
uid://cdhyuk7u8kxyk

13
examples/lowpass.gdshader Normal file
View file

@ -0,0 +1,13 @@
shader_type canvas_item;
//!load ./images/swamp.jpg
// Settings
const float threshold = 0.6;
//
void fragment() {
vec4 tex = texture(TEXTURE , UV);
COLOR.rgb = min(tex.rgb, vec3(threshold));
COLOR.a = tex.a;
}

View file

@ -0,0 +1 @@
uid://dn02xsjm1kok8

View file

@ -0,0 +1,22 @@
shader_type canvas_item;
//!steps 9
//!load ./images/swamp.jpg
uniform int STEP;
const float strength = 0.01;
void fragment() {
float v;
if (STEP % 3 == 0) {
v = COLOR.r; // 3 times
} else if (STEP % 3 == 0) {
v = COLOR.g; // 3 times
} else {
v = COLOR.b; // 3 times
}
vec2 uv = UV;
uv.y -= v * strength;
COLOR = texture(TEXTURE, uv);
}

View file

@ -0,0 +1 @@
uid://c17u5jx7a7o81

View file

@ -0,0 +1,20 @@
shader_type canvas_item;
#include "./shaderlib/pixelsort.gdshaderinc"
//!steps 1500
uniform int STEP;
//!load ./images/mountain.jpg
void fragment() {
// pixel sorting works in multiple steps
COLOR = pixelsort_step(
TEXTURE, UV,
DIRECTION_BOTTOM_TO_TOP,
COLOR_MODE_OKLCH,
{true, false, false},
{-INF, .007, -INF},
{INF, INF, INF},
STEP);
}

View file

@ -0,0 +1 @@
uid://csk0fg4by651b

12
examples/oklab.gdshader Normal file
View file

@ -0,0 +1,12 @@
shader_type canvas_item;
#include "./shaderlib/oklab.gdshaderinc"
//!load ./images/swamp.jpg
void fragment() {
vec4 oklab = rgb2oklab(COLOR);
vec4 oklch = oklab2oklch(oklab);
oklch.z -= 2.0;
COLOR = oklab2rgb(oklch2oklab(oklch));
}

View file

@ -0,0 +1 @@
uid://cu37y8lc0x83

View file

@ -0,0 +1,15 @@
shader_type canvas_item;
#include "./shaderlib/place_texture.gdshaderinc"
#include "./shaderlib/common.gdshaderinc"
//!load ./images/swamp.jpg
//!load+ img2 ./images/grass.png
uniform sampler2D img2: repeat_disable, filter_nearest;
void fragment() {
vec4 grass = place_texture(img2, UV, TEXTURE_PIXEL_SIZE, vec2(0, .47), vec2(1));
grass.rgb += (vec3(0.02, 0.07, 0.1) - ((UV.y - .8) * 0.15)); // color correction
COLOR = alpha_blend(COLOR, grass);
}

View file

@ -0,0 +1 @@
uid://dybe4t5rbbkc6

15
examples/project.godot_ Normal file
View file

@ -0,0 +1,15 @@
; 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
[application]
config/name="Fragmented Project"
run/main_scene="res://0_empty.tscn"
config/features=PackedStringArray("4.4", "Forward Plus")

1
examples/shaderlib Symbolic link
View file

@ -0,0 +1 @@
../shaderlib

10
examples/sobel.gdshader Normal file
View file

@ -0,0 +1,10 @@
shader_type canvas_item;
//!load ./images/noisy.png
#include "./shaderlib/sobel.gdshaderinc"
void fragment() {
// Sobel Filter
COLOR = sobel(TEXTURE, UV);
}

View file

@ -0,0 +1 @@
uid://h376mk1fq4ky

View file

@ -1,29 +1,31 @@
[preset.0] [preset.0]
name="Linux/X11" name="Linux/X11"
platform="Linux/X11" platform="Linux"
runnable=true runnable=true
advanced_options=false
dedicated_server=false dedicated_server=false
custom_features="" custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="" exclude_filter="screenshot.png, examples/*, shaderlib/*, tools/*, build-template/*"
export_path="dist/GlitchApp.x86_64" export_path="dist/Fragmented.x86_64"
patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
encryption_exclude_filters="" encryption_exclude_filters=""
seed=0
encrypt_pck=false encrypt_pck=false
encrypt_directory=false encrypt_directory=false
script_export_mode=2
[preset.0.options] [preset.0.options]
custom_template/debug="" custom_template/debug=""
custom_template/release="" custom_template/release="./build-template/godot.linuxbsd.template_release.x86_64"
debug/export_console_wrapper=1 debug/export_console_wrapper=1
binary_format/embed_pck=true binary_format/embed_pck=true
texture_format/bptc=true texture_format/s3tc_bptc=true
texture_format/s3tc=true texture_format/etc2_astc=false
texture_format/etc=false
texture_format/etc2=false
binary_format/architecture="x86_64" binary_format/architecture="x86_64"
ssh_remote_deploy/enabled=false ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip" ssh_remote_deploy/host="user@host_ip"
@ -37,66 +39,7 @@ unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\"" rm -rf \"{temp_dir}\""
[preset.1]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="dist/GlitchApp.exe"
encryption_include_filters=""
encryption_exclude_filters=""
encrypt_pck=false
encrypt_directory=false
[preset.1.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=true
texture_format/bptc=true texture_format/bptc=true
texture_format/s3tc=true texture_format/s3tc=true
texture_format/etc=false texture_format/etc=false
texture_format/etc2=false texture_format/etc2=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name="GlitchApp"
application/file_description=""
application/copyright="ChaoticByte"
application/trademarks=""
application/export_angle=0
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"

View file

@ -10,24 +10,23 @@ config_version=5
[application] [application]
config/name="Glitch" config/name="Fragmented"
config/version="v3.3" config/version="v10.2"
run/main_scene="res://scenes/main.tscn" run/main_scene="res://src/scenes/main.tscn"
config/features=PackedStringArray("4.2", "Mobile") config/features=PackedStringArray("4.4", "Mobile")
run/low_processor_mode=true config/icon="res://src/assets/icon.png"
config/icon="res://icon.png"
[autoload] [autoload]
ShaderPresets="*res://src/presets/Presets.gd" Filesystem="*res://src/Filesystem.gd"
Globals="*res://src/Globals.gd" ShaderDirectiveParser="*res://src/ShaderDirectiveParser.gd"
[display] [display]
window/size/viewport_width=1280 window/size/viewport_width=640
window/size/viewport_height=720 window/size/viewport_height=672
window/size/mode=2
window/energy_saving/keep_screen_on=false window/energy_saving/keep_screen_on=false
window/subwindows/embed_subwindows=false
[editor_plugins] [editor_plugins]
@ -38,15 +37,15 @@ enabled=PackedStringArray()
zoom_out={ zoom_out={
"deadzone": 0.5, "deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":16,"position":Vector2(244, 15),"global_position":Vector2(248, 56),"factor":1.0,"button_index":5,"canceled":false,"pressed":true,"double_click":false,"script":null) "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":16,"position":Vector2(244, 15),"global_position":Vector2(248, 56),"factor":1.0,"button_index":5,"canceled":false,"pressed":true,"double_click":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":45,"physical_keycode":0,"key_label":0,"unicode":45,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":45,"physical_keycode":0,"key_label":0,"unicode":45,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194435,"key_label":0,"unicode":45,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194435,"key_label":0,"unicode":45,"location":0,"echo":false,"script":null)
] ]
} }
zoom_in={ zoom_in={
"deadzone": 0.5, "deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":8,"position":Vector2(270, 19),"global_position":Vector2(274, 60),"factor":1.0,"button_index":4,"canceled":false,"pressed":true,"double_click":false,"script":null) "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":8,"position":Vector2(270, 19),"global_position":Vector2(274, 60),"factor":1.0,"button_index":4,"canceled":false,"pressed":true,"double_click":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":43,"physical_keycode":0,"key_label":0,"unicode":43,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":43,"physical_keycode":0,"key_label":0,"unicode":43,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194437,"key_label":0,"unicode":43,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194437,"key_label":0,"unicode":43,"location":0,"echo":false,"script":null)
] ]
} }
drag={ drag={
@ -56,12 +55,12 @@ drag={
} }
apply_shader={ apply_shader={
"deadzone": 0.5, "deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194336,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194336,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
] ]
} }
save_shader={ save_shader={
"deadzone": 0.5, "deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
] ]
} }
@ -70,4 +69,5 @@ save_shader={
renderer/rendering_method="mobile" renderer/rendering_method="mobile"
textures/vram_compression/import_etc2_astc=true textures/vram_compression/import_etc2_astc=true
textures/lossless_compression/force_png=true textures/lossless_compression/force_png=true
environment/defaults/default_clear_color=Color(0, 0, 0, 1) shader_compiler/shader_cache/enabled=false
environment/defaults/default_clear_color=Color(0.501961, 0.501961, 0.501961, 1)

View file

@ -1,254 +0,0 @@
[gd_scene load_steps=10 format=3 uid="uid://bjah7k4bxo044"]
[ext_resource type="Script" path="res://src/Main.gd" id="1_2625y"]
[ext_resource type="Script" path="res://src/ImageViewport.gd" id="2_hvo65"]
[ext_resource type="Script" path="res://src/ImageViewportDisplays.gd" id="3_n4itb"]
[ext_resource type="Shader" path="res://src/ui_background.gdshader" id="4_ty3qx"]
[ext_resource type="Script" path="res://src/UIAppVersion.gd" id="5_o1ggv"]
[ext_resource type="Script" path="res://src/Editor.gd" id="7_g8bap"]
[ext_resource type="Script" path="res://src/Camera.gd" id="8_mls06"]
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
viewport_path = NodePath("ImageViewport")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_onhxk"]
shader = ExtResource("4_ty3qx")
[node name="Main" type="Node2D"]
script = ExtResource("1_2625y")
[node name="ImageViewport" type="SubViewport" parent="."]
disable_3d = true
canvas_item_default_texture_filter = 0
render_target_update_mode = 4
script = ExtResource("2_hvo65")
[node name="ImageSprite" type="Sprite2D" parent="ImageViewport"]
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
texture = SubResource("ViewportTexture_lct1c")
script = ExtResource("3_n4itb")
[node name="UI_Layer" type="CanvasLayer" parent="."]
[node name="FokusStealer" type="Control" parent="UI_Layer"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 2
[node name="UserInterfaceContainer" type="Control" parent="UI_Layer"]
layout_mode = 3
anchor_right = 0.225
anchor_bottom = 1.0
offset_right = 288.0
grow_horizontal = 2
grow_vertical = 2
[node name="Background" type="ColorRect" parent="UI_Layer/UserInterfaceContainer"]
material = SubResource("ShaderMaterial_onhxk")
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 2
color = Color(1, 1, 1, 0)
[node name="AppName" type="Label" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 0
offset_left = 24.0
offset_top = 24.0
offset_right = 208.0
offset_bottom = 56.0
theme_override_font_sizes/font_size = 20
text = "GlitchApp
"
vertical_alignment = 2
[node name="AppVersion" type="Label" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 0
offset_left = 128.0
offset_top = 24.0
offset_right = 208.0
offset_bottom = 56.0
theme_override_font_sizes/font_size = 14
text = "v0
"
vertical_alignment = 2
script = ExtResource("5_o1ggv")
[node name="OpenImageDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer"]
title = "Load Image"
size = Vector2i(521, 159)
ok_button_text = "Open"
mode_overrides_title = false
file_mode = 0
access = 2
use_native_dialog = true
[node name="SaveImageDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer"]
title = "Export Image"
size = Vector2i(661, 159)
ok_button_text = "Save"
mode_overrides_title = false
access = 2
filters = PackedStringArray("*.png")
use_native_dialog = true
[node name="OpenImageButton" type="Button" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -360.0
offset_top = 24.0
offset_right = -248.0
offset_bottom = 56.0
grow_horizontal = 0
text = "Load Image"
[node name="SaveImageButton" type="Button" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -240.0
offset_top = 23.0
offset_right = -120.0
offset_bottom = 55.0
grow_horizontal = 0
text = "Export Image"
[node name="FitImageButton" type="Button" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -112.0
offset_top = 24.0
offset_right = -24.0
offset_bottom = 56.0
grow_horizontal = 0
text = "Fit Image"
[node name="Editor" type="Control" parent="UI_Layer/UserInterfaceContainer"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 24.0
offset_top = 80.0
offset_right = -24.0
offset_bottom = -24.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("7_g8bap")
[node name="OpenShaderDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer/Editor"]
title = "Load Shader"
size = Vector2i(521, 159)
ok_button_text = "Open"
mode_overrides_title = false
file_mode = 0
access = 2
filters = PackedStringArray("*.gdshader")
use_native_dialog = true
[node name="SaveShaderDialog" type="FileDialog" parent="UI_Layer/UserInterfaceContainer/Editor"]
title = "Save Shader"
size = Vector2i(661, 159)
ok_button_text = "Save"
mode_overrides_title = false
access = 2
filters = PackedStringArray("*.gdshader")
use_native_dialog = true
[node name="Label" type="Label" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 0
offset_right = 104.0
offset_bottom = 32.0
text = "Load Preset: "
vertical_alignment = 1
[node name="PresetOptions" type="OptionButton" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 0
offset_left = 104.0
offset_right = 240.0
offset_bottom = 32.0
[node name="CodeEdit" type="CodeEdit" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = 48.0
grow_horizontal = 2
grow_vertical = 2
theme_override_font_sizes/font_size = 14
placeholder_text = "// Test"
wrap_mode = 1
minimap_draw = true
minimap_width = 40
caret_blink = true
draw_control_chars = true
draw_tabs = true
draw_spaces = true
line_length_guidelines = Array[int]([80])
gutters_draw_line_numbers = true
code_completion_enabled = true
indent_automatic = true
auto_brace_completion_enabled = true
auto_brace_completion_highlight_matching = true
[node name="OpenShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -216.0
offset_right = -160.0
offset_bottom = 32.0
grow_horizontal = 0
text = "Open"
[node name="SaveShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -152.0
offset_right = -96.0
offset_bottom = 32.0
grow_horizontal = 0
text = "Save"
[node name="ApplyShaderButton" type="Button" parent="UI_Layer/UserInterfaceContainer/Editor"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -88.0
offset_bottom = 32.0
grow_horizontal = 0
text = "Apply (F5)"
[node name="Camera" type="Camera2D" parent="."]
script = ExtResource("8_mls06")
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/OpenImageDialog" to="." method="_on_open_image_dialog_file_selected"]
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/SaveImageDialog" to="." method="_on_save_image_dialog_file_selected"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/OpenImageButton" to="." method="_on_open_image_button_pressed"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/SaveImageButton" to="." method="_on_save_image_button_pressed"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/FitImageButton" to="Camera" method="_on_fit_image_button_pressed"]
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/Editor/OpenShaderDialog" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_open_shader_dialog_file_selected"]
[connection signal="file_selected" from="UI_Layer/UserInterfaceContainer/Editor/SaveShaderDialog" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_save_shader_dialog_file_selected"]
[connection signal="item_selected" from="UI_Layer/UserInterfaceContainer/Editor/PresetOptions" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_preset_options_item_selected"]
[connection signal="code_completion_requested" from="UI_Layer/UserInterfaceContainer/Editor/CodeEdit" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_code_edit_code_completion_requested"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/OpenShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_open_shader_button_pressed"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/SaveShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_save_shader_button_pressed"]
[connection signal="pressed" from="UI_Layer/UserInterfaceContainer/Editor/ApplyShaderButton" to="UI_Layer/UserInterfaceContainer/Editor" method="_on_apply_shader_button_pressed"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 502 KiB

Before After
Before After

View file

@ -0,0 +1,32 @@
/*
gaussian_blur adapted from https://godotshaders.com/shader/customizable-gausian-blur/
original code by https://godotshaders.com/author/djbob-gaming-yt/
maximum radius is 64
*/
vec4 gaussian_blur(sampler2D texture, vec2 uv, int radius, float sigma) {
vec2 resolution = 1.0 / vec2(textureSize(texture, 0));
// calculate kernel
float kernel[64];
float sum = 0.0;
for (int i = 0; i <= radius; i++) {
kernel[i] = exp(-0.5 * float(i * i) / (sigma * sigma));
sum += i == 0 ? kernel[i] : 2.0 * kernel[i];
}
for (int i = 0; i <= radius; i++) {
kernel[i] /= sum;
}
//
vec4 final_color = vec4(0.0);
float total_weight = 0.0;
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
float weight = kernel[abs(x)] * kernel[abs(y)];
vec2 offset = vec2(float(x), float(y)) * resolution;
final_color += texture(texture, uv + offset) * weight;
total_weight += weight;
}
}
final_color /= total_weight;
return final_color;
}

View file

@ -0,0 +1 @@
uid://bjtljvcjcu6dr

View file

@ -0,0 +1,27 @@
// inefficient cuberoot function
float cbrt(float x) {
return pow(x, 1.0/3.0);
}
/*
Alpha Blending a over b after Bruce A. Wallace
source: https://en.wikipedia.org/wiki/Alpha_compositing
*/
vec4 alpha_blend(vec4 b, vec4 a) {
float alpha = a.a + (b.a * (1.0 - a.a));
vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha);
return vec4(col.r, col.g, col.b, alpha);
}
/*
Rotate UV
*/
vec2 rotateUV(vec2 uv, float rotation, vec2 center) {
float cosRot = cos(rotation);
float sinRot = sin(rotation);
return vec2(
cosRot * (uv.x - center.x) + sinRot * (uv.y - center.y) + center.x,
cosRot * (uv.y - center.y) - sinRot * (uv.x - center.x) + center.y);
}

View file

@ -0,0 +1 @@
uid://764b6ekchgb8

View file

@ -0,0 +1,68 @@
/*
glslSmartDenoise by Michele Morrone, adapted
original code: https://github.com/BrutPitt/glslSmartDeNoise
license of the original code:
BSD 2-Clause License
Copyright (c) 2019-2020 Michele Morrone
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439 // 1.0/SQRT_OF_2PI
#define INV_PI 0.31830988618379067153776752674503
vec4 smart_denoise(sampler2D tex, vec2 uv, float sigma, float kSigma, float threshold) {
float radius = round(kSigma*sigma);
float radQ = radius * radius;
float invSigmaQx2 = .5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0)
float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1/(2 * PI * sigma^2)
float invThresholdSqx2 = .5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0)
float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma^2)
vec4 centrPx = texture(tex,uv);
float zBuff = 0.0;
vec4 aBuff = vec4(0.0);
vec2 size = vec2(textureSize(tex, 0));
for (float dx = -radius; dx <= radius; dx++) {
float pt = sqrt(radQ - dx * dx); // pt = yRadius: have circular trend
for (float dy = -pt; dy <= pt; dy++) {
vec2 d = vec2(dx, dy);
float blurFactor = exp( -dot(d, d) * invSigmaQx2 ) * invSigmaQx2PI;
vec4 walkPx = texture(tex,uv+d/size);
vec4 dC = walkPx-centrPx;
float deltaFactor = exp(-dot(dC, dC) * invThresholdSqx2) * invThresholdSqrt2PI * blurFactor;
zBuff += deltaFactor;
aBuff += deltaFactor*walkPx;
}
}
return aBuff/zBuff;
}

View file

@ -0,0 +1 @@
uid://b7ksfifyyfcip

27
shaderlib/hsv.gdshaderinc Normal file
View file

@ -0,0 +1,27 @@
/*
rgb2hsv and hsv2rgb functions adapted
from https://godotshaders.com/shader/hsv-adjustment/
original code by https://godotshaders.com/author/al1-ce/
Color space conversion functions always work with vec4.
The fourth value is always alpha.
*/
// Convert RGB to HSV (hue, saturation, brightness)
vec4 rgb2hsv(vec4 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec4(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x, c.a);
}
// Convert HSV back to RGB (red, green, blue)
vec4 hsv2rgb(vec4 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
vec3 rgb = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
return vec4(rgb.r, rgb.g, rgb.b, c.a);
}

View file

@ -0,0 +1 @@
uid://bbr3tq6mp5qa2

View file

@ -0,0 +1,116 @@
/*
Kuwahara Filter, adapted
original code: https://godotshaders.com/shader/generalized-kuwahara/
original authors:
- https://godotshaders.com/author/firerabbit/
- https://github.com/GarrettGunnell (Acerola)
license of the original code:
MIT License
Copyright (c) 2022 Garrett Gunnell
Copyright (c) 2024 Firerabbit
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
vec3 kuwahara(
sampler2D texture,
vec2 uv,
int kernel_size, // should be > 2 - high values will affect performance
float hardness, // should be in the range of 1.0 - 100.0
float sharpness, // should be in the range of 1.0 - 18.0
float zero_crossing, // should be in the range of 0.5 - 2.0
float zeta, // should be in the range of 0.01 - 3.0
int n // number of iterations, should be 8, must be <= 8
) {
vec2 texelSize = vec2(1.0 / vec2(textureSize(texture, 0)));
vec4 m[8];
vec3 s[8];
int kernel_radius = kernel_size / 2;
float sin_zero_crossing = sin(zero_crossing);
float eta = (zeta + cos(zero_crossing)) / (sin_zero_crossing * sin_zero_crossing);
for (int k = 0; k < n; ++k) {
m[k] = vec4(0.0f);
s[k] = vec3(0.0f);
}
for (int y = -kernel_radius; y <= kernel_radius; ++y) {
for (int x = -kernel_radius; x <= kernel_radius; ++x) {
vec2 v = vec2(float(x), float(y)) / float(kernel_radius);
vec3 c = texture(texture, uv + vec2(float(x), float(y)) * texelSize.xy).rgb;
c = clamp(c, 0.0f, 1.0f);
float sum = 0.0f;
float w[8];
float z, vxx, vyy;
/* Calculate Polynomial Weights */
vxx = zeta - eta * v.x * v.x;
vyy = zeta - eta * v.y * v.y;
z = max(0, v.y + vxx);
w[0] = z * z;
sum += w[0];
z = max(0, -v.x + vyy);
w[2] = z * z;
sum += w[2];
z = max(0, -v.y + vxx);
w[4] = z * z;
sum += w[4];
z = max(0, v.x + vyy);
w[6] = z * z;
sum += w[6];
v = sqrt(2.0f) / 2.0f * vec2(v.x - v.y, v.x + v.y);
vxx = zeta - eta * v.x * v.x;
vyy = zeta - eta * v.y * v.y;
z = max(0, v.y + vxx);
w[1] = z * z;
sum += w[1];
z = max(0, -v.x + vyy);
w[3] = z * z;
sum += w[3];
z = max(0, -v.y + vxx);
w[5] = z * z;
sum += w[5];
z = max(0, v.x + vyy);
w[7] = z * z;
sum += w[7];
float g = exp(-3.125f * dot(v,v)) / sum;
for (int k = 0; k < 8; ++k) {
float wk = w[k] * g;
m[k] += vec4(c * wk, wk);
s[k] += c * c * wk;
}
}
}
vec4 output = vec4(0.0f);
for (int k = 0; k < n; ++k) {
m[k].rgb /= m[k].w;
s[k] = abs(s[k] / m[k].w - m[k].rgb * m[k].rgb);
float sigma2 = s[k].r + s[k].g + s[k].b;
float w = 1.0f / (1.0f + pow(hardness * 1000.0f * sigma2, 0.5f * sharpness));
output += vec4(m[k].rgb * w, w);
}
return clamp(output / output.w, 0.0f, 1.0f).rgb;
}

View file

@ -0,0 +1 @@
uid://chqh2cni1qiuu

View file

@ -0,0 +1,70 @@
/*
OkLab and OkLCh
For more details on oklab, see
- https://bottosson.github.io/posts/oklab/
- https://en.wikipedia.org/wiki/Oklab_color_space
Color space conversion functions always work with vec4.
The fourth value is always alpha.
*/
#include "./common.gdshaderinc"
vec4 rgb2oklab(vec4 c) {
// oklab.x and .y (a and b) should range from -0.5 to 0.5
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
float l_ = cbrt(l);
float m_ = cbrt(m);
float s_ = cbrt(s);
return vec4(
0.2104542553f*l_ + 0.7936177850f*m_ - 0.0040720468f*s_,
1.9779984951f*l_ - 2.4285922050f*m_ + 0.4505937099f*s_,
0.0259040371f*l_ + 0.7827717662f*m_ - 0.8086757660f*s_,
c.a
);
}
vec4 oklab2rgb(vec4 c) {
// oklab.x and .y (a and b) should range from -0.5 to 0.5
float l_ = c.x + 0.3963377774f * c.y + 0.2158037573f * c.z;
float m_ = c.x - 0.1055613458f * c.y - 0.0638541728f * c.z;
float s_ = c.x - 0.0894841775f * c.y - 1.2914855480f * c.z;
float l = l_*l_*l_;
float m = m_*m_*m_;
float s = s_*s_*s_;
return vec4(
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
c.a
);
}
vec4 oklab2oklch(vec4 c) {
// oklch.z (hue) ranges from -3.6 to 3.6
return vec4(
c.x,
sqrt((c.y * c.y) + (c.z * c.z)),
atan(c.z, c.y),
c.a
);
}
vec4 oklch2oklab(vec4 c) {
// oklch.z (hue) ranges from -3.6 to 3.6
return vec4(
c.x,
c.y * cos(c.z),
c.y * sin(c.z),
c.a
);
}

View file

@ -0,0 +1 @@
uid://ckw4nfslk4m6l

View file

@ -0,0 +1,14 @@
// pixelate by lowering uv resolution
vec4 pixelate(sampler2D tex, vec2 uv, float resolution_x) {
vec2 texture_size = vec2(textureSize(tex, 0));
vec2 ratio;
if (texture_size.x > texture_size.y) {
ratio = vec2(texture_size.x / texture_size.y, 1.0);
}
else {
ratio = vec2(1.0, texture_size.y / texture_size.x);
}
vec2 r = ratio * resolution_x;
return texture(tex, trunc(uv * r) / r);
}

View file

@ -0,0 +1 @@
uid://dpu5nneo5bgnq

View file

@ -0,0 +1,126 @@
/*
Pixelsorting using odd-even sort
I roughly followed https://ciphrd.com/2020/04/08/pixel-sorting-on-shader-using-well-crafted-sorting-filters-glsl/
- vector fields aren't implemented, diagonal sorting is not supported!
*/
#include "./hsv.gdshaderinc"
#include "./oklab.gdshaderinc"
#define INF (1.0/0.0)
#define DIRECTION_LEFT_TO_RIGHT vec2(1, 0)
#define DIRECTION_RIGHT_TO_LEFT vec2(-1, 0)
#define DIRECTION_TOP_TO_BOTTOM vec2(0, 1)
#define DIRECTION_BOTTOM_TO_TOP vec2(0, -1)
#define COLOR_MODE_RGB 0
#define COLOR_MODE_OKLAB 1
#define COLOR_MODE_OKLCH 2
#define COLOR_MODE_HSV 3
vec4 pixelsort_step(
sampler2D tex, vec2 uv,
vec2 direction, // e.g. (1, 0) for left-to-right or (0, -1) for bottom-to-top
// see DIRECTION_LEFT_TO_RIGHT, etc.
// note: vertical sorting doesn't work, so using e.g. (1, 1) won't work
int color_mode, // 0 = RGB, 1 = OKLAB, 2 = OKLCH, 3 = HSV
// see COLOR_MODE_RGB, etc.
bool color_channel_mask[3], // which color channel(s) to take into account
float lower_threshold[3], // lower threshold for pixels to be considered sorted
// when in doubt, use {-INF, -INF, -INF}
float upper_threshold[3], // upper threshold; {INF, INF, INF}
int step_ // from STEP
) {
// sanitize inputs
direction = clamp(direction, vec2(-1, -1), vec2(1, 1));
color_mode = clamp(color_mode, 0, 3);
// get neighbour
vec2 texture_size = vec2(textureSize(tex, 0));
vec2 a = (mod(floor(uv * texture_size), 2.0) * 2.0 - 1.0) * (mod(float(step_), 2.0) * 2.0 - 1.0);
vec2 neighbour_uv = uv + (direction * a / texture_size);
//
vec4 x = texture(tex, uv);
vec4 y = texture(tex, neighbour_uv);
if ( // stop at borders
neighbour_uv.x > 1.0 ||
neighbour_uv.x < 0.0 ||
neighbour_uv.y > 1.0 ||
neighbour_uv.y < 0.0
) {
return x;
} else {
// convert color if necessary
// get value to compare
float vx = 0.0;
float vy = 0.0;
vec3 color_x;
vec3 color_y;
if (color_mode == COLOR_MODE_RGB) {
color_x = x.rgb;
color_y = y.rgb;
} else if (color_mode == COLOR_MODE_OKLAB) {
color_x = rgb2oklab(x).rgb;
color_y = rgb2oklab(y).rgb;
} else if (color_mode == COLOR_MODE_OKLCH) {
color_x = oklab2oklch(rgb2oklab(x)).rgb;
color_y = oklab2oklch(rgb2oklab(y)).rgb;
} else if (color_mode == COLOR_MODE_HSV) {
color_x = rgb2hsv(x).rgb;
color_y = rgb2hsv(y).rgb;
}
float divisor = 0.0;
if (color_channel_mask[0]) {
vx += color_x.r;
vy += color_y.r;
divisor += 1.0;
}
if (color_channel_mask[1]) {
vx += color_x.g;
vy += color_y.g;
divisor += 1.0;
}
if (color_channel_mask[2]) {
vx += color_x.b;
vy += color_y.b;
divisor += 1.0;
}
divisor = max(divisor, 1.0);
vx /= divisor;
vy /= divisor;
//
if (
(a.x < .0 && abs(direction).y == .0) ||
(a.y < .0 && abs(direction).x == .0)
) {
if (
vy > vx &&
// threshold
color_x.r < upper_threshold[0] &&
color_x.g < upper_threshold[1] &&
color_x.b < upper_threshold[2] &&
color_x.r > lower_threshold[0] &&
color_x.g > lower_threshold[1] &&
color_x.b > lower_threshold[2]
) { return y; }
else { return x; }
} else if (
(a.x > .0 && abs(direction).y == .0) ||
(a.y > .0 && abs(direction).x == .0)
) {
if (
vx >= vy &&
// threshold
color_y.r < upper_threshold[0] &&
color_y.g < upper_threshold[1] &&
color_y.b < upper_threshold[2] &&
color_y.r > lower_threshold[0] &&
color_y.g > lower_threshold[1] &&
color_y.b > lower_threshold[2]
) { return y; }
else { return x; }
}
}
}

View file

@ -0,0 +1 @@
uid://doefnwk3vyr0o

View file

@ -0,0 +1,21 @@
/*
Load in a texture from a sampler2D with an offset and scale
See examples/place_texture.gdshader
*/
vec4 place_texture(sampler2D sampler, vec2 uv, vec2 texture_pixel_size, vec2 offset, vec2 scale) {
vec2 texture_size = vec2(textureSize(sampler, 0));
// position of current pixel; sample color c
vec2 pos = (uv - offset) / (texture_size*texture_pixel_size) / scale;
vec4 c = texture(sampler, pos);
// top-left bounds
vec2 a = offset;
// bottom-right bounds
vec2 b = offset + (texture_size*texture_pixel_size) * scale;
// check bounds
if (
a.x < uv.x && a.y < uv.y
&& b.x > uv.x && b.y > uv.y
) { return c; } // within bounds -> return color
return vec4(0); // not within bounds -> return transparency
}

View file

@ -0,0 +1 @@
uid://51u2hjq62e5i

View file

@ -0,0 +1,50 @@
/*
Edge Detection (Sobel Filter and Gaussian Blur) by FencerDevLog, adapted
original code: https://godotshaders.com/shader/edge-detection-sobel-filter-and-gaussian-blur/
license of the original code: CC0
*/
vec3 _convolution(sampler2D tex, vec2 uv, vec2 pixel_size) {
vec3 conv = vec3(0.0);
// Gaussian blur kernel
float gauss[25] = {
0.00390625, 0.015625, 0.0234375, 0.015625, 0.00390625,
0.015625, 0.0625, 0.09375, 0.0625, 0.015625,
0.0234375, 0.09375, 0.140625, 0.09375, 0.0234375,
0.015625, 0.0625, 0.09375, 0.0625, 0.015625,
0.00390625, 0.015625, 0.0234375, 0.015625, 0.00390625
};
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
conv += texture(tex, uv + vec2(float(col - 2), float(row - 2)) * pixel_size).rgb * gauss[row * 5 + col];
}
}
return conv;
}
vec4 sobel(sampler2D tex, vec2 uv) {
vec2 pixel_size = 1.0/vec2(textureSize(tex, 0));
vec3 pixels[9]; // Sobel kernel
// [0, 1, 2]
// [3, 4, 5]
// [6, 7, 8]
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
vec2 uv_ = uv + vec2(float(col - 1), float(row - 1)) * pixel_size;
pixels[row * 3 + col] = _convolution(tex, uv_, pixel_size);
}
}
// Sobel operator
vec3 gx = (
pixels[0] * -1.0 + pixels[3] * -2.0 + pixels[6] * -1.0
+ pixels[2] * 1.0 + pixels[5] * 2.0 + pixels[8] * 1.0
);
vec3 gy = (
pixels[0] * -1.0 + pixels[1] * -2.0 + pixels[2] * -1.0
+ pixels[6] * 1.0 + pixels[7] * 2.0 + pixels[8] * 1.0
);
vec3 sobel = sqrt(gx * gx + gy * gy);
return vec4(sobel, 1.0);
}

View file

@ -0,0 +1 @@
uid://bqo1fpunnl05f

View file

@ -1,42 +1,50 @@
extends Camera2D extends Camera2D
@onready var image_viewport_display = %ImageViewportDisplay
var drag = false var drag = false
@onready var user_interface_container = get_parent().get_node("UI_Layer/UserInterfaceContainer")
@onready var image_viewport = get_parent().get_node("ImageViewport")
func _input(event): func _input(event):
if event.is_action_pressed("zoom_out") && !Globals.camera_freeze: if event.is_action_pressed("zoom_out"):
zoom_out() zoom_out()
elif event.is_action_pressed("zoom_in") && !Globals.camera_freeze: elif event.is_action_pressed("zoom_in"):
zoom_in() zoom_in()
if event.is_action_pressed("drag") && !Globals.camera_freeze: if event.is_action_pressed("drag"):
drag = true self.drag = true
elif event.is_action_released("drag"): elif event.is_action_released("drag"):
drag = false self.drag = false
if drag && event is InputEventMouseMotion: if self.drag && event is InputEventMouseMotion:
global_position -= event.relative / zoom self.global_position -= event.relative / self.zoom
var old_zoom = self.zoom
func _process(_delta: float) -> void:
if self.zoom != old_zoom:
image_viewport_display.update_zoom_texture_filter(self.zoom)
image_viewport_display.material.set_shader_parameter("zoom_level", self.zoom)
old_zoom = self.zoom
func fit_image(): func fit_image():
if image_viewport.image_original_tex != null: if Filesystem.original_image != null:
var ui_container_size = user_interface_container.size var image_size = Filesystem.original_image.get_size()
var image_size = image_viewport.image_original_tex.get_size()
var viewport_size = get_viewport_rect().size var viewport_size = get_viewport_rect().size
var zoomf = (viewport_size.x - ui_container_size.x) / image_size.x / 1.1 var zoomf = 1.0
if zoomf * image_size.y > viewport_size.y: if viewport_size.x / image_size.x * image_size.y > viewport_size.y:
zoomf = viewport_size.y / image_size.y / 1.1 zoomf = viewport_size.y / image_size.y / 1.25
zoom = Vector2(zoomf, zoomf) else:
global_position = Vector2(-((ui_container_size.x) / 2 / zoom.x), 0) zoomf = viewport_size.x / image_size.x / 1.2
self.zoom = Vector2(zoomf, zoomf)
self.global_position = Vector2(0, 0)
func zoom_in(): func zoom_in():
var old_mouse_pos = get_global_mouse_position() var old_mouse_pos = get_global_mouse_position()
zoom *= 1.2 self.zoom *= 1.2
global_position += old_mouse_pos - get_global_mouse_position() self.global_position += old_mouse_pos - get_global_mouse_position()
func zoom_out(): func zoom_out():
var old_mouse_pos = get_global_mouse_position() var old_mouse_pos = get_global_mouse_position()
zoom *= 1/1.2 self.zoom *= 1/1.2
global_position += old_mouse_pos - get_global_mouse_position() self.global_position += old_mouse_pos - get_global_mouse_position()
func _on_fit_image_button_pressed(): func _on_fit_image_button_pressed():
fit_image() fit_image()

1
src/Camera.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b6r8rigubdctk

View file

@ -1,243 +0,0 @@
extends Control
@onready var preset_options = $PresetOptions
@onready var code_editor = $CodeEdit
@onready var open_shader_dialog = $OpenShaderDialog
@onready var save_shader_dialog = $SaveShaderDialog
var selected_preset_name = ShaderPresets.default_preset
var last_save_filepath = ""
# # # # # # # # # # #
# GDShader keywords #
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_language.cpp
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_types.cpp
#
const gdshader_boolean_values = [
"true", "false"
]
const gdshader_datatypes = [
"void",
"bool", "bvec2", "bvec3", "bvec4",
"int", "ivec2", "ivec3", "ivec4",
"uint", "uvec2", "uvec3", "uvec4",
"float", "vec2", "vec3", "vec4",
"mat2", "mat3", "mat4",
"sampler2D", "isampler2D", "usampler2D",
"sampler2DArray", "isampler2DArray", "usampler2DArray",
]
const gdshader_precision_modifiers = [
"lowp", "mediump", "heighp"
]
const gdshader_global_space_keywords = [
"uniform", "group_uniforms", "varying", "const",
"struct", "shader_type", "render_mode"
]
const gdshader_uniform_qualifiers = [
"instance", "global"
]
const gdshader_block_keywords = [
"if", "else",
"for", "while", "do",
"switch", "case",
"default", "break", "continue",
"return", "discard"
]
const gdshader_function_specifier_keywords = [
"in", "out", "inout"
]
const gdshader_hints = [
"source_color", "hint_range", "instance_index"
]
const gdshader_sampler_hints = [
"hint_normal",
"hint_default_white", "hint_default_black", "hint_default_transparent",
"hint_anisotropy",
"hint_roughness_r", "hint_roughness_g", "hint_roughness_b", "hint_roughness_a",
"hint_roughness_normal", "hint_roughness_gray",
"hint_screen_texture", "hint_normal_roughness_texture",
"hint_depth_texture",
"filter_nearest", "filter_linear",
"filter_nearest_mipmap", "filter_linear_mipmap",
"filter_nearest_mipmap_anisotropic", "filter_linear_mipmap_anisotropic",
"repeat_enable", "repeat_disable"
]
const gdshader_builtin_functions = [
"radians", "degrees",
"sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
"asinh", "acosh", "atanh",
"pow", "exp", "log", "exp2", "log2", "sqrt", "inversesqrt",
"abs", "sign", "floor", "trunc", "round", "roundEven", "ceil", "fract",
"mod", "modf", "min", "max", "clamp",
"mix", "step", "smoothstep",
"isnan", "isinf",
"floatBitsToInt", "floatBitsToUint", "intBitsToFloat", "uintBitsToFloat",
"length", "distance",
"dot", "cross",
"normalize", "reflect", "refract", "faceforward",
"matrixCompMult", "outerProduct", "transpose",
"determinant", "inverse",
"lessThan", "greaterThan", "lessThanEqual", "greaterThanEqual",
"equal", "notEqual",
"any", "all", "not",
"textureSize", "texture", "textureProj", "textureLod", "texelFetch",
"textureProjLod", "textureGrad", "textureProjGrad", "textureGather",
"textureQueryLod", "textureQueryLevels",
"dFdx", "dFdxCoarse", "dFdxFine", "dFdy", "dFdyCoarse", "dFdyFine",
"fwidth", "fwidthCoarse", "fwidthFine"
]
const gdshader_sub_functions = [
"length", "fma",
"packHalf2x16", "packUnorm2x16", "packSnorm2x16", "packUnorm4x8", "packSnorm4x8",
"unpackHalf2x16", "unpackUnorm2x16", "unpackSnorm2x16", "unpackUnorm4x8", "unpackSnorm4x8",
"bitfieldExtract", "bitfieldInsert", "bitfieldReverse", "bitCount",
"findLSB", "findMSB",
"umulExtended", "imulExtended",
"uaddCarry", "usubBorrow",
"ldexp", "frexp"
]
const gdshader_builtins = [
"TIME", "PI", "TAU", "E",
"VERTEX",
"UV",
"COLOR",
"POINT_SIZE",
"MODEL_MATRIX", "CANVAS_MATRIX", "SCREEN_MATRIX",
"INSTANCE_CUSTOM", "INSTANCE_ID",
"VERTEX_ID",
"AT_LIGHT_PASS",
"TEXTURE_PIXEL_SIZE",
"CUSTOM0", "CUSTOM1",
"SHADOW_VERTEX", "LIGHT_VERTEX",
"FRAGCOORD",
"NORMAL", "NORMAL_MAP", "NORMAL_MAP_DEPTH",
"TEXTURE", "NORMAL_TEXTURE",
"SCREEN_UV", "SCREEN_PIXEL_SIZE",
"POINT_COORD",
]
#
# configure Highlighter
#
class ShaderSyntaxHighlighter extends CodeHighlighter:
func _init():
add_color_region("//", "", Color.WEB_GRAY, true)
add_color_region("/*", "*/", Color.WEB_GRAY, false)
function_color = Color.INDIAN_RED
for k in gdshader_boolean_values:
keyword_colors[k] = Color.INDIAN_RED
for k in ( gdshader_datatypes
+ gdshader_hints
+ gdshader_sampler_hints
+ gdshader_global_space_keywords
+ gdshader_function_specifier_keywords
+ gdshader_precision_modifiers
+ gdshader_uniform_qualifiers):
keyword_colors[k] = Color.ORCHID;
for k in gdshader_block_keywords:
keyword_colors[k] = Color.CORAL
for k in gdshader_builtins:
keyword_colors[k] = Color.DARK_TURQUOISE
member_variable_color = Color.LIGHT_BLUE
number_color = Color.AQUA
symbol_color = Color.GRAY
#
# and code completion
#
func _on_code_edit_code_completion_requested():
for k in gdshader_boolean_values:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.INDIAN_RED)
for k in ( gdshader_datatypes
+ gdshader_hints
+ gdshader_sampler_hints
+ gdshader_global_space_keywords
+ gdshader_function_specifier_keywords
+ gdshader_precision_modifiers
+ gdshader_uniform_qualifiers):
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_CLASS, k, k, Color.ORCHID)
for k in gdshader_block_keywords:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.CORAL)
for k in gdshader_builtins:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_CONSTANT, k, k, Color.DARK_TURQUOISE)
for k in gdshader_builtin_functions + gdshader_sub_functions:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED)
code_editor.update_code_completion_options(true)
#
# # # # # # # # # # # #
func _camera_freeze():
Globals.camera_freeze = true
func _camera_unfreeze():
Globals.camera_freeze = false
func _ready():
code_editor.code_completion_enabled = true
code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new()
for c in get_children():
c.connect("mouse_entered", _camera_freeze)
c.connect("mouse_exited", _camera_unfreeze)
update()
func _input(event):
if event.is_action_pressed("apply_shader"):
_on_apply_shader_button_pressed()
elif event.is_action_pressed("save_shader"):
accept_event() # Event is now handled.
_on_save_shader_button_pressed()
func _on_preset_options_item_selected(index):
selected_preset_name = preset_options.get_item_text(index)
Globals.shader = ShaderPresets.presets[selected_preset_name]
Globals.target_viewport.update()
update()
last_save_filepath = ""
func update():
preset_options.clear()
# the following lines are weird af
var presets: Array[String] = []
var current_p_idx = 0
for p in ShaderPresets.presets:
presets.append(p)
if p == selected_preset_name:
current_p_idx = len(presets) - 1
preset_options.add_item(p)
preset_options.select(current_p_idx)
# weirdness ends here
code_editor.text = Globals.shader.code
func _on_open_shader_button_pressed():
open_shader_dialog.show()
func _on_save_shader_button_pressed():
if last_save_filepath == "":
save_shader_dialog.current_file = selected_preset_name + "_custom.gdshader"
else:
save_shader_dialog.current_path = last_save_filepath
save_shader_dialog.show()
func _on_open_shader_dialog_file_selected(path):
var file = FileAccess.open(path, FileAccess.READ)
var shader_code = file.get_as_text()
var shader = Shader.new()
shader.code = shader_code
Globals.shader = shader
Globals.target_viewport.update()
update()
last_save_filepath = path
func _on_save_shader_dialog_file_selected(path):
var file = FileAccess.open(path, FileAccess.WRITE)
var content = Globals.shader.code
file.store_string(content)
last_save_filepath = path
func _on_apply_shader_button_pressed():
var shader = Shader.new()
shader.code = code_editor.text
Globals.shader = shader
Globals.target_viewport.update()

77
src/Filesystem.gd Normal file
View file

@ -0,0 +1,77 @@
extends Node
var cwd = "."
var shader_path = "":
get():
return shader_path
set(v):
var old = shader_path
shader_path = v
if "/" in v: # update current working directory
cwd = v.substr(0, v.rfind("/"))
if old != shader_path:
store_last_opened_file()
var shader: Shader:
get():
print("Load ", shader_path)
return load(shader_path)
var original_image: ImageTexture
var additional_images: Dictionary
var result: Image
var last_image_savepath = ""
var last_original_image_path = ""
func get_absolute_path(p: String) -> String:
# this only works on Linux!
if !p.begins_with("/"):
return self.cwd + "/" + p.lstrip("./")
return p
func load_original_image(path: String) -> String: # returns an error message
print("Load ", path)
var img = Image.new()
var err = img.load(path)
if err == OK:
original_image = ImageTexture.create_from_image(img)
if self.last_image_savepath == "" or path != self.last_original_image_path:
self.last_image_savepath = path
self.last_original_image_path = path
return ""
return error_string(err) + " " + path
func clear_additional_images():
additional_images.clear()
func load_additional_image(key: String, path: String) -> String: # returns Error Message String
print("Load ", path)
var img = Image.new()
var err = img.load(path)
if err == OK:
additional_images[key] = ImageTexture.create_from_image(img)
return ""
else:
return error_string(err) + " " + path
func save_result(path: String):
print("Export ", path)
var err = self.result.save_png(path)
if err != OK:
print("An error occured!")
else:
self.last_image_savepath = path
func store_last_opened_file():
var f = FileAccess.open("user://last_opened", FileAccess.WRITE)
if f != null:
f.store_pascal_string(shader_path)
f.flush()
func remember_last_opened_file():
var f = FileAccess.open("user://last_opened", FileAccess.READ)
if f != null:
shader_path = f.get_pascal_string()

1
src/Filesystem.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://rlb041ygdwol

View file

@ -1,5 +0,0 @@
extends Node
var camera_freeze = false
@onready var shader: Shader = ShaderPresets.presets[ShaderPresets.default_preset]
var target_viewport: SubViewport

123
src/ImageCompositor.gd Normal file
View file

@ -0,0 +1,123 @@
class_name ImageCompositor extends SubViewport
var image_sprite: Sprite2D
func _init() -> void:
# Overwrite some variables
self.render_target_update_mode = SubViewport.UPDATE_ALWAYS
self.disable_3d = true
self.transparent_bg = true
self.canvas_item_default_texture_filter = Viewport.DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST
self.image_sprite = Sprite2D.new()
@onready var camera = %Camera
@onready var image_viewport_display = %ImageViewportDisplay
func _ready() -> void:
# Add image sprite as child to be rendered
self.add_child(image_sprite)
var _fragment_function_regex: RegEx = RegEx.create_from_string(r'\s*void\s+fragment\s*\(\s*\)\s*{\s*')
func validate_shader_compilation(shader: Shader) -> bool:
# Inject code to validate shader compilation
var shader_code = shader.code;
# -> get position of fragment shader
var fragment_function_match = _fragment_function_regex.search(shader.code)
if fragment_function_match == null:
return false
# -> inject uniform
var uniform_name = "shader_compilation_validate_" + str(randi_range(999999999, 100000000))
var uniform_code_line = "\nuniform bool " + uniform_name + ";\n"
shader_code = shader_code.insert(fragment_function_match.get_start(), uniform_code_line)
# -> inject variable access to prevent that the uniform gets optimized away
shader_code = shader_code.insert(fragment_function_match.get_end() + len(uniform_code_line), "\n" + uniform_name + ";\n")
# apply shader code
shader.code = shader_code
# test if uniform list is empty -> if it is empty, the shader compilation failed
return len(shader.get_shader_uniform_list()) > 0
func shader_has_uniform(shader: Shader, name: String, type: int) -> bool:
for u in shader.get_shader_uniform_list():
if u["name"] == name && u["type"] == type:
return true
return false
func set_vsync(enabled: bool):
if enabled:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
else:
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
var shader = Filesystem.shader # read from disk
if shader == null:
return ["No shader opened!"]
# get number of steps & check if code has STEP uniform
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
var has_step_uniform: bool = shader_has_uniform(shader, "STEP", 2)
var has_steps_uniform: bool = shader_has_uniform(shader, "STEPS", 2)
# validate shader
if not validate_shader_compilation(shader):
return ["Shader compilation failed!"]
var errors = []
# load texture(s) from //!load directive -> TEXTURE
var original_image_path = ""
if overwrite_image_path == "":
var m = ShaderDirectiveParser.parse_load_directive(shader.code)
if len(m) < 1:
errors.append("Didn't find a load directive!")
return errors
original_image_path = Filesystem.get_absolute_path(m[1])
else:
original_image_path = overwrite_image_path
var fit_image = false
if original_image_path != Filesystem.last_original_image_path:
fit_image = true
var err = Filesystem.load_original_image(original_image_path)
if err != "":
errors.append(err)
image_viewport_display.hide()
return errors
# ... from //!load+ directives
Filesystem.clear_additional_images()
for n in ShaderDirectiveParser.parse_load_additional_directive(shader.code):
err = Filesystem.load_additional_image(n[1], Filesystem.get_absolute_path(n[2]))
if err != "":
errors.append(err)
if len(errors) > 0:
return errors
# apply textures
image_sprite.texture = Filesystem.original_image
image_sprite.offset = Filesystem.original_image.get_size() / 2
self.size = Filesystem.original_image.get_size()
# already show the image viewport & fit the image
if fit_image: camera.fit_image()
image_viewport_display.show()
# create shader material
var mat = ShaderMaterial.new()
mat.shader = shader
# add images as shader parameters
for key in Filesystem.additional_images:
mat.set_shader_parameter(
key, # uniform param name
Filesystem.additional_images[key])
# assign material
image_sprite.material = mat
# iterate n times
set_vsync(false) # speed up processing
if has_steps_uniform:
# set STEPS param
mat.set_shader_parameter("STEPS", steps)
for i in range(steps):
if has_step_uniform:
# set STEP param
mat.set_shader_parameter("STEP", i)
# Get viewport texture
await RenderingServer.frame_post_draw # wait for next frame to get drawn
Filesystem.result = get_texture().get_image()
image_sprite.texture = ImageTexture.create_from_image(Filesystem.result)
set_vsync(true) # reenable vsync
image_sprite.material = null
# done
return errors

View file

@ -0,0 +1 @@
uid://d106170kuigl3

View file

@ -1,40 +0,0 @@
extends SubViewport
@onready var image_sprite = $ImageSprite
var image_original_tex: ImageTexture
var image_result: Image
var load_uniform_regex: RegEx
func _ready():
load_uniform_regex = RegEx.new()
load_uniform_regex.compile(r'\/\/!load\s(\w*)\s(.*)')
func set_original_image(image: Image):
image_original_tex = ImageTexture.create_from_image(image)
image_sprite.texture = image_original_tex
image_sprite.offset = image_original_tex.get_size() / 2
size = image_original_tex.get_size()
func update():
if image_original_tex != null:
image_sprite.texture = image_original_tex
var mat = ShaderMaterial.new()
mat.shader = Globals.shader
# load images from //!load directives and apply them to
# the material as shader parameters
for m in load_uniform_regex.search_all(Globals.shader.code):
var u_image = Image.load_from_file(m.strings[2])
mat.set_shader_parameter(
m.strings[1], # uniform param name
ImageTexture.create_from_image(u_image))
# assign material
image_sprite.material = mat
# Get viewport texture
await RenderingServer.frame_post_draw # for good measure
image_result = get_texture().get_image()
image_sprite.material = null
image_sprite.texture = ImageTexture.create_from_image(image_result)
func get_result():
return image_result

View file

@ -1,9 +1,10 @@
extends Sprite2D extends Sprite2D
@onready var camera = get_parent().get_node("Camera") func _ready() -> void:
hide()
func _process(_delta): func update_zoom_texture_filter(zoom: Vector2):
if camera.zoom.x >= 1.5: if zoom.x >= 1.5:
texture_filter = TEXTURE_FILTER_NEAREST_WITH_MIPMAPS texture_filter = TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
else: else:
texture_filter = TEXTURE_FILTER_LINEAR texture_filter = TEXTURE_FILTER_LINEAR

View file

@ -0,0 +1 @@
uid://ctc4lhbdsoq7u

View file

@ -1,37 +1,126 @@
extends Node2D extends Node
@onready var camera = $Camera const BATCH_MODE_SUPPORTED_EXTS = [
@onready var image_viewport = $ImageViewport ".bmp", ".dds", ".exr", ".hdr", ".jpeg", ".jpg", ".ktx", ".png", ".svg", ".webp"
@onready var ui_container = $UI_Layer/UserInterfaceContainer ]
@onready var ui_control_fileopen = $UI_Layer/UserInterfaceContainer/OpenImageDialog
@onready var ui_control_filesave = $UI_Layer/UserInterfaceContainer/SaveImageDialog @onready var app_name = ProjectSettings.get_setting("application/config/name")
var last_save_filepath = ""
func show_help():
print(
"Usage:\n\n",
"./Fragmented <command> <args...>\n\n",
"Commands:\n\n",
" help\n\n",
" | Shows this help text.\n\n",
" apply --shader PATH [--load-image PATH]\n\n",
" | Applies a shader file.\n\n",
" --shader PATH The path to the shader\n",
" --output PATH Where to write the resulting image to.\n",
" In batch mode, this must be a folder.\n",
" --load-image PATH The path to the image. This will overwrite the\n",
" load directive of the shader file.\n",
" Passing a folder activates batch mode.\n",
" (optional)\n")
func parse_custom_cmdline(args: PackedStringArray):
var kwargs: Dictionary = {"--shader": null, "--output": null, "--load-image": null}
var args_len = args.size()
var i = 0
while i < args_len:
var a = args[i]
if a in kwargs && args_len > i+1:
i += 1
kwargs[a] = args[i]
i += 1
return kwargs
func cli_handle_errors(errors: Array) -> int:
# returns number of errors
var n_errors = errors.size()
if n_errors > 0:
print("One or more errors occurred.")
for e in errors:
printerr(e)
return n_errors
func cli(args: PackedStringArray):
print(
"~ Fragmented CLI ~\n",
"-================-\n")
if "help" in args:
show_help()
get_tree().quit(1)
return
var kwargs: Dictionary = parse_custom_cmdline(args)
if kwargs["--shader"] == null or kwargs["--output"] == null:
show_help()
get_tree().quit(1)
return
var batch_mode = false
var load_image_dir: DirAccess
if kwargs["--load-image"] != null:
load_image_dir = DirAccess.open(kwargs["--load-image"])
if load_image_dir != null:
# batch mode
if DirAccess.open(kwargs["--output"]) == null:
printerr("If --load-image is a directory, --output has to be one too.\n")
show_help()
get_tree().quit(1)
return
else:
batch_mode = true
#
Filesystem.shader_path = kwargs["--shader"]
#
if batch_mode:
var in_dir_path = load_image_dir.get_current_dir()
var out_dir_path: String = kwargs["--output"].rstrip("/")
for f in load_image_dir.get_files():
var supported = false
for e in BATCH_MODE_SUPPORTED_EXTS:
if f.ends_with(e):
supported = true
break
if supported:
f = in_dir_path + "/" + f
print(f)
var errors = await $Compositor.update(f)
if cli_handle_errors(errors) == 0:
var filename = out_dir_path + "/" + f.substr(f.rfind("/"), -1)
Filesystem.save_result(filename)
else:
get_tree().quit(1)
return
get_tree().quit(0)
else:
var errors = []
if kwargs["--load-image"] == null:
errors = await $Compositor.update()
else:
errors = await $Compositor.update(kwargs["--load-image"])
if cli_handle_errors(errors) == 0:
Filesystem.save_result(kwargs["--output"])
get_tree().quit(0)
else:
get_tree().quit(1)
func prepare_gui():
update_title()
# Load last opened file
Filesystem.remember_last_opened_file()
%MainUI._on_apply_shader_button_pressed()
func _ready(): func _ready():
Globals.target_viewport = image_viewport var args = OS.get_cmdline_args()
if len(args) > 0 and args[0] in ["apply", "help"]:
func _on_open_image_button_pressed(): # use the commandline interface
ui_control_fileopen.show() cli(args)
func _on_open_image_dialog_file_selected(path):
var img = Image.new()
var err = img.load(path)
if err == OK:
image_viewport.set_original_image(img)
image_viewport.update()
camera.fit_image()
last_save_filepath = path
else: else:
print("An error occured!") prepare_gui()
func _on_save_image_button_pressed(): func update_title(current_file: String = ""):
if image_viewport.get_result() != null: if current_file == "":
ui_control_filesave.current_path = last_save_filepath get_window().title = app_name + " - Viewer"
ui_control_filesave.show()
func _on_save_image_dialog_file_selected(path):
var err = image_viewport.get_result().save_png(path)
if err != OK:
print("An error occured!")
else: else:
last_save_filepath = path get_window().title = current_file + " - " + app_name + " - Viewer"

1
src/Main.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://5sbslwysin5a

100
src/MainUI.gd Normal file
View file

@ -0,0 +1,100 @@
extends Control
@onready var open_shader_dialog = %OpenShaderDialog
@onready var save_image_dialog = %SaveImageDialog
@onready var open_shader_button = %OpenShaderButton
@onready var save_image_button = %SaveImageButton
@onready var fit_image_button = %FitImageButton
@onready var apply_shader_button = %ApplyShaderButton
@onready var status_indicator = %StatusIndicator
@onready var error_msg_dialog = %ErrorMessageDialog
@onready var main = get_tree().root.get_node("Main")
@onready var compositor = %Compositor
@onready var camera = %Camera
var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst")
var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu")
enum Status {OKAY, ERROR, UNKNOWN = -1}
#
func _input(event):
if event.is_action_pressed("apply_shader"):
_on_apply_shader_button_pressed()
elif event.is_action_pressed("save_shader"):
accept_event() # Event is now handled.
#
func set_buttons_disabled(disabled: bool):
for b in [open_shader_button, save_image_button, fit_image_button, apply_shader_button, status_indicator]:
b.disabled = disabled
func _on_open_shader_button_pressed():
set_buttons_disabled(true)
open_shader_dialog.show()
func _on_fit_image_button_pressed():
camera.fit_image()
func _on_apply_shader_button_pressed():
set_buttons_disabled(true)
var errors = await compositor.update()
set_buttons_disabled(false)
if len(errors) > 0:
update_status(Status.ERROR, "\n".join(errors))
else:
update_status(Status.OKAY)
status_indicator.disabled = true
func _on_save_image_button_pressed():
if Filesystem.result != null:
set_buttons_disabled(true)
save_image_dialog.current_path = Filesystem.last_image_savepath
save_image_dialog.show()
#
func _on_open_shader_dialog_file_selected(path: String):
Filesystem.shader_path = path
main.update_title(path.split("/")[-1])
self._on_apply_shader_button_pressed()
func _on_open_shader_dialog_canceled() -> void:
set_buttons_disabled(false)
func _on_open_shader_dialog_confirmed() -> void:
set_buttons_disabled(false)
func _on_save_image_dialog_file_selected(path):
Filesystem.save_result(path)
set_buttons_disabled(false)
func _on_save_image_dialog_canceled() -> void:
set_buttons_disabled(false)
func _on_save_image_dialog_confirmed() -> void:
set_buttons_disabled(false)
#
func update_status(status: Status, msg: String = ""):
error_msg_dialog.dialog_text = msg
error_msg_dialog.reset_size()
if status == Status.OKAY:
status_indicator.texture_normal = status_okay_texture
elif status == Status.ERROR:
status_indicator.texture_normal = status_error_texture
else:
status_indicator.texture_normal = null
if msg == "":
status_indicator.disabled = true
else:
status_indicator.disabled = false
func _on_status_indicator_pressed() -> void:
error_msg_dialog.show()

1
src/MainUI.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bxgmf2ny7yuc8

View file

@ -0,0 +1,24 @@
extends Node
var _load_regex: RegEx = RegEx.create_from_string(r'\/\/!load\s(.*)')
var _load_additional_regex: RegEx = RegEx.create_from_string(r'\/\/!load\+\s(\w*)\s(.*)')
var _iterate_regex: RegEx = RegEx.create_from_string(r'\/\/!steps\s([0-9]+)\s*')
func parse_load_directive(shader_code: String) -> PackedStringArray:
var regex_match = self._load_regex.search(shader_code)
if regex_match == null:
return []
return regex_match.strings
func parse_load_additional_directive(shader_code: String) -> Array[PackedStringArray]:
var results : Array[PackedStringArray] = []
for m in self._load_additional_regex.search_all(shader_code):
results.append(m.strings)
return results
func parse_steps_directive(shader_code: String) -> int:
var regex_match = self._iterate_regex.search(shader_code)
if regex_match == null:
return 1
else:
return max(0, int(regex_match.strings[1]))

View file

@ -0,0 +1 @@
uid://dw8bep14j4j3w

View file

@ -1,4 +0,0 @@
extends Label
func _ready():
text = ProjectSettings.get_setting("application/config/version")

8
src/VersionLabel.gd Normal file
View file

@ -0,0 +1,8 @@
extends Label
func _ready():
text = ProjectSettings.get_setting("application/config/name") \
+ " " \
+ ProjectSettings.get_setting("application/config/version") \
+ " | Godot " \
+ Engine.get_version_info()["string"]

1
src/VersionLabel.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bh0gpu3i2p47f

BIN
src/assets/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

34
src/assets/bg.png.import Normal file
View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d2nwchyd6huob"
path="res://.godot/imported/bg.png-7c8713dd1fab321784216191fa747e53.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/bg.png"
dest_files=["res://.godot/imported/bg.png-7c8713dd1fab321784216191fa747e53.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

80
src/assets/error.svg Normal file
View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="30"
height="30"
viewBox="0 0 30 30"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="error.svg"
inkscape:export-filename="error.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="12.28598"
inkscape:cy="16.108776"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="66"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2">
<rect
x="-19.109422"
y="15.119667"
width="23.697131"
height="28.949477"
id="rect4519" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 4,25 H 26 L 15,6 Z"
id="path11345"
sodipodi:nodetypes="cccc" />
<circle
style="opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path15121"
cx="15"
cy="15"
r="13" />
<g
aria-label="!"
transform="matrix(0.80238423,0,0,0.80238423,20.811527,-5.9033718)"
id="text4517"
style="font-size:24px;white-space:pre;shape-inside:url(#rect4519);display:inline;fill:#ffffff;fill-opacity:1">
<path
d="m -5.6133052,29.164717 h -3.264 L -9.5630195,16.081288 h 4.6354286 z m -4.0045715,4.580571 q 0,-1.261715 0.6857143,-1.755429 0.6857143,-0.521142 1.6731429,-0.521142 0.96,0 1.6457143,0.521142 0.6857143,0.493714 0.6857143,1.755429 0,1.206857 -0.6857143,1.755429 -0.6857143,0.521142 -1.6457143,0.521142 -0.9874286,0 -1.6731429,-0.521142 -0.6857143,-0.548572 -0.6857143,-1.755429 z"
style="font-weight:bold;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:1.14286"
id="path4674" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://04iv1gogpuhu"
path="res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/error.svg"
dest_files=["res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.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=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

View file

@ -3,15 +3,15 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://kqwc4avs2xdp" uid="uid://kqwc4avs2xdp"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" path="res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"
metadata={ metadata={
"vram_texture": false "vram_texture": false
} }
[deps] [deps]
source_file="res://icon.png" source_file="res://src/assets/icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] dest_files=["res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"]
[params] [params]

81
src/assets/okay.svg Normal file
View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="30"
height="30"
viewBox="0 0 30 30"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="success.svg"
inkscape:export-filename="error.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="3.314563"
inkscape:cy="14.385204"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="66"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2">
<rect
x="0"
y="0"
width="30"
height="30"
id="rect15390" />
<rect
x="-19.109422"
y="15.119667"
width="23.697131"
height="28.949477"
id="rect4519" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 4,25 H 26 L 15,6 Z"
id="path11345"
sodipodi:nodetypes="cccc" />
<circle
style="opacity:0.75;fill:#15ff1e;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path15121"
cx="15"
cy="15"
r="13" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 8.1284306,14.403599 5.2374004,4.840883 8.39167,-9.6564053"
id="path20589"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://m1omb6g45vst"
path="res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/okay.svg"
dest_files=["res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.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=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -1,14 +0,0 @@
extends Node
const dir = "res://src/presets/shaders/"
@onready var presets = {
"Empty": load(dir + "empty.gdshader"),
"Greyscale": load(dir + "greyscale.gdshader"),
"Lowpass": load(dir + "lowpass.gdshader"),
"Channel Offset": load(dir + "channel_offset.gdshader"),
"RGB -> UV Distort": load(dir + "rgb_uv_distort.gdshader"),
"Mix": load(dir + "mix.gdshader")
}
var default_preset: String = "Empty"

View file

@ -1,5 +0,0 @@
shader_type canvas_item;
void fragment() {
// Called for every pixel the material is visible on.
}

View file

@ -1,13 +0,0 @@
shader_type canvas_item;
// Settings
const float threshold = 0.5;
//
void fragment() {
vec4 tex = texture(TEXTURE , UV);
COLOR.r = min(tex.r, threshold);
COLOR.g = min(tex.g, threshold);
COLOR.b = min(tex.b, threshold);
COLOR.a = tex.a;
}

View file

@ -1,8 +0,0 @@
shader_type canvas_item;
//!load img2 ./icon.png
uniform sampler2D img2: repeat_enable, filter_nearest;
void fragment() {
COLOR = mix(COLOR, texture(img2, UV), .5);
}

View file

@ -1,11 +0,0 @@
shader_type canvas_item;
const float strength = 0.1;
void fragment() {
vec2 uv = UV;
float b = (COLOR.r + COLOR.g + COLOR.b) / 3.0;
uv.x = mix(uv.x, b, strength);
uv.y = mix(uv.y, b, strength);
COLOR = texture(TEXTURE, uv);
}

Some files were not shown because too many files have changed in this diff Show more