Compare commits
109 commits
Author | SHA1 | Date | |
---|---|---|---|
54cbddbbba | |||
65fddc0bd8 | |||
ebc8ce2cb9 | |||
c9c5428dbc | |||
4d0f7ca538 | |||
3dcc4ca02c | |||
bf1c1327ac | |||
560e455cd2 | |||
77273f9c1b | |||
9d23efea63 | |||
4278cde44b | |||
f940061135 | |||
4546b9eac0 | |||
e7bb3c8f16 | |||
46829e72e9 | |||
df85ceb469 | |||
b6f6c3d27e | |||
9b1efc1513 | |||
9192e020e3 | |||
f32d10dbaa | |||
c5c9b9f516 | |||
b916f43f8b | |||
ed10da0604 | |||
89b82695ac | |||
f7aac231ae | |||
cc59ba9b9e | |||
7ad9ef002a | |||
6a7dc164fe | |||
ddba85d17c | |||
da1bd6a58a | |||
1ee3088cce | |||
6bb6b8b768 | |||
5a8a6e4c61 | |||
9f5011aaac | |||
e40ab18683 | |||
d29f14c7c9 | |||
a4a1de4bfe | |||
168cb036be | |||
fee1c64775 | |||
60332eadaf | |||
d376801058 | |||
632b4593aa | |||
d512005b86 | |||
a444cea11a | |||
f8cfbf91c9 | |||
b3044f64a6 | |||
c0d9e00fe9 | |||
7810a1fa83 | |||
40374bd849 | |||
c70eaed0c4 | |||
acdb52c91e | |||
dc325d3e77 | |||
701efcaed6 | |||
6419ab1c3c | |||
0fad7cb575 | |||
1a21589fc1 | |||
35959290d3 | |||
88e85c19f5 | |||
d08329c750 | |||
85d33d5d77 | |||
3b47d57353 | |||
50ddf6e6d3 | |||
a7da554320 | |||
9b6d5d0a78 | |||
7b72274140 | |||
4d12ad4432 | |||
6c48c9fe72 | |||
974c40fcb4 | |||
ec7544cb3b | |||
71394edbf1 | |||
fd16d2a8df | |||
4787810651 | |||
1fba2ee826 | |||
2646f3da9b | |||
0ddfa74bb4 | |||
885cfbf3bc | |||
6c1ec9ed2c | |||
1d9f838668 | |||
1b87cafb13 | |||
01e17bd6e3 | |||
afea5c9727 | |||
1635cd5b82 | |||
3abaf38400 | |||
7f1415dada | |||
5b939a3d20 | |||
11ba75517c | |||
bbbd35f26d | |||
1aa217776c | |||
8b5a1cc37d | |||
bae23b0ee6 | |||
8394ad9d3c | |||
ca40971e53 | |||
6b748229ed | |||
862ef075ff | |||
288ddcbc5b | |||
80d937cb64 | |||
ad6d2add1b | |||
57328bc9a3 | |||
f069f2911f | |||
815ec239f6 | |||
a9a90235d9 | |||
8653b3740e | |||
d61dac7acf | |||
4b28a6427e | |||
53d42de4a5 | |||
58323f3d88 | |||
f0cd2d811f | |||
d866fd1fe8 | |||
367dbe163c |
3
.gitignore
vendored
|
@ -13,8 +13,9 @@ data_*/
|
|||
mono_crash.*.json
|
||||
|
||||
# Builds
|
||||
*.x86_64
|
||||
godot.*.template_release.*
|
||||
dist/*
|
||||
!dist/.gitkeep
|
||||
|
||||
screenshot.png.import
|
||||
|
||||
|
|
41
LICENSE
|
@ -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
|
||||
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:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
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
|
@ -1,39 +1,156 @@
|
|||
|
||||
<h1 align=center>GlitchApp</h1>
|
||||
<h1 align=center>Fragmented</h1>
|
||||
|
||||

|
||||
|
||||
<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
|
||||
|
||||
- Linux [tested]
|
||||
- Windows
|
||||
- Linux
|
||||
|
||||
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
|
||||
|
||||
The application includes presets. You can use them as a starting-point to write your own filters.
|
||||
Just load an image, apply a preset, edit the code and hit `F5` to see the changes.
|
||||
With Fragemented, you are processing images with GDShaders. This brings almost endless opportunities to create unique art.
|
||||
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
|
||||
//!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>;
|
||||
```
|
||||
|
||||
With this you can load additional images into your shader.
|
||||
Have a look at the `Mix` preset:
|
||||
Have a look at the `place_texture.gdshader` example.
|
||||
|
||||
### 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
|
||||
shader_type canvas_item;
|
||||
|
||||
//!load img2 ./icon.png
|
||||
uniform sampler2D img2: repeat_enable, filter_nearest;
|
||||
#include "./shaderlib/oklab.gdshaderinc"
|
||||
|
||||
//!load ./images/swamp.jpg
|
||||
|
||||
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
|
||||
|
|
20
build-template/Containerfile
Normal 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
|
@ -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
|
@ -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
3
examples/0_empty.tscn
Normal file
|
@ -0,0 +1,3 @@
|
|||
[gd_scene format=3 uid="uid://db2rhq8rwv5wo"]
|
||||
|
||||
[node name="Node" type="Node"]
|
8
examples/blur.gdshader
Normal 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);
|
||||
}
|
1
examples/blur.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://cny8dtukv54wt
|
|
@ -1,5 +1,7 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
//!load ./images/swamp.jpg
|
||||
|
||||
const vec2 offset_r = vec2(-0.002, -0.002);
|
||||
const vec2 offset_g = vec2(0., 0.);
|
||||
const vec2 offset_b = vec2(0.002, 0.002);
|
1
examples/channel_offset.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://0efk4fornlg6
|
14
examples/color_and_pixelate.gdshader
Normal 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);
|
||||
}
|
1
examples/color_and_pixelate.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://gd23hu7ro148
|
9
examples/denoise.gdshader
Normal 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);
|
||||
}
|
1
examples/denoise.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://cbwyneu03fki6
|
|
@ -1,10 +1,10 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
//!load ./images/swamp.jpg
|
||||
|
||||
void fragment() {
|
||||
vec4 tex = texture(TEXTURE , UV);
|
||||
float b = (COLOR.r + COLOR.g + COLOR.b) / 3.0;
|
||||
COLOR.r = b;
|
||||
COLOR.g = b;
|
||||
COLOR.b = b;
|
||||
COLOR.a = tex.a;
|
||||
}
|
1
examples/greyscale.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dvarqolt6es27
|
6
examples/images/CREDITS.md
Normal 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
After Width: | Height: | Size: 150 KiB |
34
examples/images/grass.png.import
Normal 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
|
BIN
examples/images/mountain.jpg
Normal file
After Width: | Height: | Size: 814 KiB |
34
examples/images/mountain.jpg.import
Normal 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
After Width: | Height: | Size: 2.8 MiB |
34
examples/images/noisy.png.import
Normal 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
After Width: | Height: | Size: 477 KiB |
34
examples/images/swamp.jpg.import
Normal 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
|
16
examples/kuwahara.gdshader
Normal 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);
|
||||
}
|
1
examples/kuwahara.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://cdhyuk7u8kxyk
|
13
examples/lowpass.gdshader
Normal 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;
|
||||
}
|
1
examples/lowpass.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dn02xsjm1kok8
|
22
examples/multistep_distort.gdshader
Normal 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);
|
||||
}
|
1
examples/multistep_distort.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://c17u5jx7a7o81
|
20
examples/multistep_pixelsort.gdshader
Normal 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);
|
||||
}
|
1
examples/multistep_pixelsort.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://csk0fg4by651b
|
12
examples/oklab.gdshader
Normal 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));
|
||||
}
|
1
examples/oklab.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://cu37y8lc0x83
|
15
examples/place_texture.gdshader
Normal 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);
|
||||
}
|
1
examples/place_texture.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dybe4t5rbbkc6
|
15
examples/project.godot_
Normal 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
|
@ -0,0 +1 @@
|
|||
../shaderlib
|
10
examples/sobel.gdshader
Normal 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);
|
||||
}
|
1
examples/sobel.gdshader.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://h376mk1fq4ky
|
|
@ -1,29 +1,31 @@
|
|||
[preset.0]
|
||||
|
||||
name="Linux/X11"
|
||||
platform="Linux/X11"
|
||||
platform="Linux"
|
||||
runnable=true
|
||||
advanced_options=false
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path="dist/GlitchApp.x86_64"
|
||||
exclude_filter="screenshot.png, examples/*, shaderlib/*, tools/*, build-template/*"
|
||||
export_path="dist/Fragmented.x86_64"
|
||||
patches=PackedStringArray()
|
||||
encryption_include_filters=""
|
||||
encryption_exclude_filters=""
|
||||
seed=0
|
||||
encrypt_pck=false
|
||||
encrypt_directory=false
|
||||
script_export_mode=2
|
||||
|
||||
[preset.0.options]
|
||||
|
||||
custom_template/debug=""
|
||||
custom_template/release=""
|
||||
custom_template/release="./build-template/godot.linuxbsd.template_release.x86_64"
|
||||
debug/export_console_wrapper=1
|
||||
binary_format/embed_pck=true
|
||||
texture_format/bptc=true
|
||||
texture_format/s3tc=true
|
||||
texture_format/etc=false
|
||||
texture_format/etc2=false
|
||||
texture_format/s3tc_bptc=true
|
||||
texture_format/etc2_astc=false
|
||||
binary_format/architecture="x86_64"
|
||||
ssh_remote_deploy/enabled=false
|
||||
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
|
||||
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
|
||||
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/s3tc=true
|
||||
texture_format/etc=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}'"
|
||||
|
|
|
@ -10,24 +10,23 @@ config_version=5
|
|||
|
||||
[application]
|
||||
|
||||
config/name="Glitch"
|
||||
config/version="v3.3"
|
||||
run/main_scene="res://scenes/main.tscn"
|
||||
config/features=PackedStringArray("4.2", "Mobile")
|
||||
run/low_processor_mode=true
|
||||
config/icon="res://icon.png"
|
||||
config/name="Fragmented"
|
||||
config/version="v10.2"
|
||||
run/main_scene="res://src/scenes/main.tscn"
|
||||
config/features=PackedStringArray("4.4", "Mobile")
|
||||
config/icon="res://src/assets/icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
ShaderPresets="*res://src/presets/Presets.gd"
|
||||
Globals="*res://src/Globals.gd"
|
||||
Filesystem="*res://src/Filesystem.gd"
|
||||
ShaderDirectiveParser="*res://src/ShaderDirectiveParser.gd"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=1280
|
||||
window/size/viewport_height=720
|
||||
window/size/mode=2
|
||||
window/size/viewport_width=640
|
||||
window/size/viewport_height=672
|
||||
window/energy_saving/keep_screen_on=false
|
||||
window/subwindows/embed_subwindows=false
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
|
@ -38,15 +37,15 @@ enabled=PackedStringArray()
|
|||
zoom_out={
|
||||
"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)
|
||||
, 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":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":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,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
zoom_in={
|
||||
"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)
|
||||
, 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":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":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,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
drag={
|
||||
|
@ -56,12 +55,12 @@ drag={
|
|||
}
|
||||
apply_shader={
|
||||
"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={
|
||||
"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"
|
||||
textures/vram_compression/import_etc2_astc=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)
|
||||
|
|
254
scenes/main.tscn
|
@ -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"]
|
BIN
screenshot.png
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 502 KiB |
32
shaderlib/blur.gdshaderinc
Normal 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;
|
||||
}
|
1
shaderlib/blur.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://bjtljvcjcu6dr
|
27
shaderlib/common.gdshaderinc
Normal 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);
|
||||
}
|
1
shaderlib/common.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://764b6ekchgb8
|
68
shaderlib/denoise.gdshaderinc
Normal 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;
|
||||
}
|
1
shaderlib/denoise.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b7ksfifyyfcip
|
27
shaderlib/hsv.gdshaderinc
Normal 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);
|
||||
}
|
1
shaderlib/hsv.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://bbr3tq6mp5qa2
|
116
shaderlib/kuwahara.gdshaderinc
Normal 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;
|
||||
}
|
1
shaderlib/kuwahara.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://chqh2cni1qiuu
|
70
shaderlib/oklab.gdshaderinc
Normal 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
|
||||
);
|
||||
}
|
1
shaderlib/oklab.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://ckw4nfslk4m6l
|
14
shaderlib/pixelate.gdshaderinc
Normal 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);
|
||||
}
|
1
shaderlib/pixelate.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dpu5nneo5bgnq
|
126
shaderlib/pixelsort.gdshaderinc
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
1
shaderlib/pixelsort.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://doefnwk3vyr0o
|
21
shaderlib/place_texture.gdshaderinc
Normal 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
|
||||
}
|
1
shaderlib/place_texture.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://51u2hjq62e5i
|
50
shaderlib/sobel.gdshaderinc
Normal 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);
|
||||
}
|
1
shaderlib/sobel.gdshaderinc.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://bqo1fpunnl05f
|
|
@ -1,42 +1,50 @@
|
|||
extends Camera2D
|
||||
|
||||
@onready var image_viewport_display = %ImageViewportDisplay
|
||||
|
||||
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):
|
||||
if event.is_action_pressed("zoom_out") && !Globals.camera_freeze:
|
||||
if event.is_action_pressed("zoom_out"):
|
||||
zoom_out()
|
||||
elif event.is_action_pressed("zoom_in") && !Globals.camera_freeze:
|
||||
elif event.is_action_pressed("zoom_in"):
|
||||
zoom_in()
|
||||
if event.is_action_pressed("drag") && !Globals.camera_freeze:
|
||||
drag = true
|
||||
if event.is_action_pressed("drag"):
|
||||
self.drag = true
|
||||
elif event.is_action_released("drag"):
|
||||
drag = false
|
||||
if drag && event is InputEventMouseMotion:
|
||||
global_position -= event.relative / zoom
|
||||
self.drag = false
|
||||
if self.drag && event is InputEventMouseMotion:
|
||||
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():
|
||||
if image_viewport.image_original_tex != null:
|
||||
var ui_container_size = user_interface_container.size
|
||||
var image_size = image_viewport.image_original_tex.get_size()
|
||||
if Filesystem.original_image != null:
|
||||
var image_size = Filesystem.original_image.get_size()
|
||||
var viewport_size = get_viewport_rect().size
|
||||
var zoomf = (viewport_size.x - ui_container_size.x) / image_size.x / 1.1
|
||||
if zoomf * image_size.y > viewport_size.y:
|
||||
zoomf = viewport_size.y / image_size.y / 1.1
|
||||
zoom = Vector2(zoomf, zoomf)
|
||||
global_position = Vector2(-((ui_container_size.x) / 2 / zoom.x), 0)
|
||||
var zoomf = 1.0
|
||||
if viewport_size.x / image_size.x * image_size.y > viewport_size.y:
|
||||
zoomf = viewport_size.y / image_size.y / 1.25
|
||||
else:
|
||||
zoomf = viewport_size.x / image_size.x / 1.2
|
||||
self.zoom = Vector2(zoomf, zoomf)
|
||||
self.global_position = Vector2(0, 0)
|
||||
|
||||
func zoom_in():
|
||||
var old_mouse_pos = get_global_mouse_position()
|
||||
zoom *= 1.2
|
||||
global_position += old_mouse_pos - get_global_mouse_position()
|
||||
self.zoom *= 1.2
|
||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||
|
||||
func zoom_out():
|
||||
var old_mouse_pos = get_global_mouse_position()
|
||||
zoom *= 1/1.2
|
||||
global_position += old_mouse_pos - get_global_mouse_position()
|
||||
self.zoom *= 1/1.2
|
||||
self.global_position += old_mouse_pos - get_global_mouse_position()
|
||||
|
||||
func _on_fit_image_button_pressed():
|
||||
fit_image()
|
||||
|
|
1
src/Camera.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://b6r8rigubdctk
|
243
src/Editor.gd
|
@ -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
|
@ -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
|
@ -0,0 +1 @@
|
|||
uid://rlb041ygdwol
|
|
@ -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
|
@ -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
|
1
src/ImageCompositor.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://d106170kuigl3
|
|
@ -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
|
|
@ -1,9 +1,10 @@
|
|||
extends Sprite2D
|
||||
|
||||
@onready var camera = get_parent().get_node("Camera")
|
||||
func _ready() -> void:
|
||||
hide()
|
||||
|
||||
func _process(_delta):
|
||||
if camera.zoom.x >= 1.5:
|
||||
func update_zoom_texture_filter(zoom: Vector2):
|
||||
if zoom.x >= 1.5:
|
||||
texture_filter = TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
|
||||
else:
|
||||
texture_filter = TEXTURE_FILTER_LINEAR
|
1
src/ImageViewportDisplay.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://ctc4lhbdsoq7u
|
151
src/Main.gd
|
@ -1,37 +1,126 @@
|
|||
extends Node2D
|
||||
extends Node
|
||||
|
||||
@onready var camera = $Camera
|
||||
@onready var image_viewport = $ImageViewport
|
||||
@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
|
||||
var last_save_filepath = ""
|
||||
const BATCH_MODE_SUPPORTED_EXTS = [
|
||||
".bmp", ".dds", ".exr", ".hdr", ".jpeg", ".jpg", ".ktx", ".png", ".svg", ".webp"
|
||||
]
|
||||
|
||||
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
||||
|
||||
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():
|
||||
Globals.target_viewport = image_viewport
|
||||
|
||||
func _on_open_image_button_pressed():
|
||||
ui_control_fileopen.show()
|
||||
|
||||
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
|
||||
var args = OS.get_cmdline_args()
|
||||
if len(args) > 0 and args[0] in ["apply", "help"]:
|
||||
# use the commandline interface
|
||||
cli(args)
|
||||
else:
|
||||
print("An error occured!")
|
||||
prepare_gui()
|
||||
|
||||
func _on_save_image_button_pressed():
|
||||
if image_viewport.get_result() != null:
|
||||
ui_control_filesave.current_path = last_save_filepath
|
||||
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!")
|
||||
func update_title(current_file: String = ""):
|
||||
if current_file == "":
|
||||
get_window().title = app_name + " - Viewer"
|
||||
else:
|
||||
last_save_filepath = path
|
||||
get_window().title = current_file + " - " + app_name + " - Viewer"
|
||||
|
|
1
src/Main.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://5sbslwysin5a
|
100
src/MainUI.gd
Normal 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
|
@ -0,0 +1 @@
|
|||
uid://bxgmf2ny7yuc8
|
24
src/ShaderDirectiveParser.gd
Normal 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]))
|
1
src/ShaderDirectiveParser.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dw8bep14j4j3w
|
|
@ -1,4 +0,0 @@
|
|||
extends Label
|
||||
|
||||
func _ready():
|
||||
text = ProjectSettings.get_setting("application/config/version")
|
8
src/VersionLabel.gd
Normal 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
|
@ -0,0 +1 @@
|
|||
uid://bh0gpu3i2p47f
|
BIN
src/assets/bg.png
Normal file
After Width: | Height: | Size: 532 B |
34
src/assets/bg.png.import
Normal 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
|
@ -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 |
37
src/assets/error.svg.import
Normal 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
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
@ -3,15 +3,15 @@
|
|||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kqwc4avs2xdp"
|
||||
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
|
||||
path="res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
|
||||
source_file="res://src/assets/icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"]
|
||||
|
||||
[params]
|
||||
|
81
src/assets/okay.svg
Normal 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 |
37
src/assets/okay.svg.import
Normal 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
|
|
@ -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"
|
|
@ -1,5 +0,0 @@
|
|||
shader_type canvas_item;
|
||||
|
||||
void fragment() {
|
||||
// Called for every pixel the material is visible on.
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|