commit
701efcaed6
18 changed files with 332 additions and 130 deletions
41
LICENSE
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
|
Redistribution and use in source and binary forms, with or without
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
modification, are permitted provided that the following conditions are met:
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
copies or substantial portions of the Software.
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
this list of conditions and the following disclaimer in the documentation
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
and/or other materials provided with the distribution.
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
contributors may be used to endorse or promote products derived from
|
||||||
SOFTWARE.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
30
README.md
30
README.md
|
@ -74,3 +74,33 @@ void fragment() {
|
||||||
COLOR = hsv_offset(COLOR, 0.32, 0.2, 0.0);
|
COLOR = hsv_offset(COLOR, 0.32, 0.2, 0.0);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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 cmd --shader PATH [--load-image PATH]
|
||||||
|
|
||||||
|
--shader PATH The path to the shader
|
||||||
|
--output PATH Where to write the resulting image to
|
||||||
|
--load-image PATH The path to the image. This will overwrite the
|
||||||
|
load directive of the shader file (optional)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also run `./Fragmented cmd help` to show the help message.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
./Fragmented cmd --shader ./examples/oklab.gdshader --output ./output.png
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
./Fragmented cmd --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
|
||||||
|
```
|
||||||
|
|
8
examples/blur.gdshader
Normal file
8
examples/blur.gdshader
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
//!load ./images/swamp.jpg
|
||||||
|
#include "res://shaderlib/blur.gdshaderinc"
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
COLOR = gaussian_blur(TEXTURE, UV, 48, 24.0);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
#include "res://shaderlib/hsv.gdshaderinc"
|
#include "res://shaderlib/colorspaces.gdshaderinc"
|
||||||
#include "res://shaderlib/effects.gdshaderinc"
|
#include "res://shaderlib/effects.gdshaderinc"
|
||||||
|
|
||||||
//!load ./images/swamp.jpg
|
//!load ./images/swamp.jpg
|
||||||
|
@ -8,6 +8,7 @@ shader_type canvas_item;
|
||||||
void fragment() {
|
void fragment() {
|
||||||
COLOR = pixelate(TEXTURE, UV, 200.0);
|
COLOR = pixelate(TEXTURE, UV, 200.0);
|
||||||
vec4 hsv = rgb2hsv(COLOR);
|
vec4 hsv = rgb2hsv(COLOR);
|
||||||
COLOR = hsv_offset(COLOR, 0.65, .42-(hsv.y*.3), -.125);
|
hsv.xyz += vec3(0.65, .42-(hsv.y*.3), -.125);
|
||||||
COLOR = hsv_multiply(COLOR, 1.0, 1.0, 1.25);
|
hsv.xyz *= vec3(1.0, 1.0, 1.25);
|
||||||
|
COLOR = hsv2rgb(hsv);
|
||||||
}
|
}
|
||||||
|
|
12
examples/oklab.gdshader
Normal file
12
examples/oklab.gdshader
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
#include "res://shaderlib/colorspaces.gdshaderinc"
|
||||||
|
|
||||||
|
//!load ./images/swamp.jpg
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec4 oklab = rgb2oklab(COLOR);
|
||||||
|
vec4 oklch = oklab2oklch(oklab);
|
||||||
|
oklch.z -= 2.0;
|
||||||
|
COLOR = oklab2rgb(oklch2oklab(oklch));
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ config_version=5
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Fragmented"
|
config/name="Fragmented"
|
||||||
config/version="v6.2"
|
config/version="v7.0"
|
||||||
run/main_scene="res://scenes/main.tscn"
|
run/main_scene="res://scenes/main.tscn"
|
||||||
config/features=PackedStringArray("4.3", "Mobile")
|
config/features=PackedStringArray("4.3", "Mobile")
|
||||||
run/low_processor_mode=true
|
run/low_processor_mode=true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_scene load_steps=10 format=3 uid="uid://bjah7k4bxo044"]
|
[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/Main.gd" id="1_2625y"]
|
||||||
[ext_resource type="Script" path="res://src/Compositor.gd" id="2_hvo65"]
|
[ext_resource type="Script" path="res://src/ImageCompositor.gd" id="2_4thch"]
|
||||||
[ext_resource type="Shader" path="res://src/shader/ivd_outline.gdshader" id="3_6xihe"]
|
[ext_resource type="Shader" path="res://src/shader/ivd_outline.gdshader" id="3_6xihe"]
|
||||||
[ext_resource type="Script" path="res://src/ImageViewportDisplay.gd" id="3_n4itb"]
|
[ext_resource type="Script" path="res://src/ImageViewportDisplay.gd" id="3_n4itb"]
|
||||||
[ext_resource type="Script" path="res://src/UIWindow.gd" id="6_8k0ha"]
|
[ext_resource type="Script" path="res://src/UIWindow.gd" id="6_8k0ha"]
|
||||||
|
@ -20,14 +20,7 @@ script = ExtResource("1_2625y")
|
||||||
|
|
||||||
[node name="Compositor" type="SubViewport" parent="."]
|
[node name="Compositor" type="SubViewport" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
disable_3d = true
|
script = ExtResource("2_4thch")
|
||||||
transparent_bg = true
|
|
||||||
canvas_item_default_texture_filter = 0
|
|
||||||
render_target_update_mode = 4
|
|
||||||
script = ExtResource("2_hvo65")
|
|
||||||
|
|
||||||
[node name="ImageSprite" type="Sprite2D" parent="Compositor"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
|
|
||||||
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
|
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
@ -44,6 +37,7 @@ unique_name_in_owner = true
|
||||||
disable_3d = true
|
disable_3d = true
|
||||||
position = Vector2i(48, 36)
|
position = Vector2i(48, 36)
|
||||||
size = Vector2i(704, 704)
|
size = Vector2i(704, 704)
|
||||||
|
visible = false
|
||||||
script = ExtResource("6_8k0ha")
|
script = ExtResource("6_8k0ha")
|
||||||
|
|
||||||
[node name="UserInterfaceContainer" parent="EditorWindow" instance=ExtResource("7_5ci0e")]
|
[node name="UserInterfaceContainer" parent="EditorWindow" instance=ExtResource("7_5ci0e")]
|
||||||
|
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 635 KiB After Width: | Height: | Size: 630 KiB |
32
shaderlib/blur.gdshaderinc
Normal file
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;
|
||||||
|
}
|
90
shaderlib/colorspaces.gdshaderinc
Normal file
90
shaderlib/colorspaces.gdshaderinc
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
Color space conversion functions always work with vec4.
|
||||||
|
The fourth value is always alpha.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "res://shaderlib/common.gdshaderinc"
|
||||||
|
|
||||||
|
/*
|
||||||
|
rgb2hsv and hsv2rgb functions adapted
|
||||||
|
from https://godotshaders.com/shader/hsv-adjustment/
|
||||||
|
original code by https://godotshaders.com/author/al1-ce/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
OkLab and OkLCh
|
||||||
|
For more details on oklab, see
|
||||||
|
- https://bottosson.github.io/posts/oklab/
|
||||||
|
- https://en.wikipedia.org/wiki/Oklab_color_space
|
||||||
|
*/
|
||||||
|
|
||||||
|
vec4 rgb2oklab(vec4 c) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
return vec4(
|
||||||
|
c.x,
|
||||||
|
sqrt((c.y * c.y) + (c.z * c.z)),
|
||||||
|
atan(c.z, c.y),
|
||||||
|
c.a
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 oklch2oklab(vec4 c) {
|
||||||
|
return vec4(
|
||||||
|
c.x,
|
||||||
|
c.y * cos(c.z),
|
||||||
|
c.y * sin(c.z),
|
||||||
|
c.a
|
||||||
|
);
|
||||||
|
}
|
5
shaderlib/common.gdshaderinc
Normal file
5
shaderlib/common.gdshaderinc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
// inefficient cuberoot function
|
||||||
|
float cbrt(float x) {
|
||||||
|
return pow(x, 1.0/3.0);
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
/* glslSmartDenoise by Michele Morrone, adapted
|
/*
|
||||||
|
glslSmartDenoise by Michele Morrone, adapted
|
||||||
original code: https://github.com/BrutPitt/glslSmartDeNoise
|
original code: https://github.com/BrutPitt/glslSmartDeNoise
|
||||||
license of the original code:
|
license of the original code:
|
||||||
|
|
||||||
BSD 2-Clause License
|
BSD 2-Clause License
|
||||||
|
|
||||||
Copyright (c) 2019-2020 Michele Morrone
|
Copyright (c) 2019-2020 Michele Morrone
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
|
|
||||||
// rgb2hsv and hsv2rgb functions adapted
|
|
||||||
// from https://godotshaders.com/shader/hsv-adjustment/
|
|
||||||
// original code by https://godotshaders.com/author/al1-ce/
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offset the hue, saturation and brightness of a RGB color
|
|
||||||
vec4 hsv_offset(
|
|
||||||
vec4 rgba,
|
|
||||||
float offset_hue,
|
|
||||||
float offset_saturation,
|
|
||||||
float offset_brightness
|
|
||||||
) {
|
|
||||||
vec4 c = rgb2hsv(rgba);
|
|
||||||
c.x += offset_hue;
|
|
||||||
c.y += offset_saturation;
|
|
||||||
c.z += offset_brightness;
|
|
||||||
return hsv2rgb(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply the hue, saturation and brightness of a RGB color
|
|
||||||
vec4 hsv_multiply(
|
|
||||||
vec4 rgba,
|
|
||||||
float mult_hue,
|
|
||||||
float mult_saturation,
|
|
||||||
float mult_brightness
|
|
||||||
) {
|
|
||||||
vec4 c = rgb2hsv(rgba);
|
|
||||||
c.x *= mult_hue;
|
|
||||||
c.y *= mult_saturation;
|
|
||||||
c.z *= mult_brightness;
|
|
||||||
return hsv2rgb(c);
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
// Load in a texture from a sampler2D with an offset and scale
|
/*
|
||||||
// See examples/place_texture.gdshader
|
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) {
|
vec4 place_texture(sampler2D sampler, vec2 uv, vec2 texture_pixel_size, vec2 offset, vec2 scale) {
|
||||||
vec2 texture_size = vec2(textureSize(sampler, 0));
|
vec2 texture_size = vec2(textureSize(sampler, 0));
|
||||||
// position of current pixel; sample color c
|
// position of current pixel; sample color c
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
// Alpha Blending a over b after Bruce A. Wallace
|
/*
|
||||||
// source: https://en.wikipedia.org/wiki/Alpha_compositing
|
Alpha Blending a over b after Bruce A. Wallace
|
||||||
|
source: https://en.wikipedia.org/wiki/Alpha_compositing
|
||||||
|
*/
|
||||||
vec4 alpha_blend(vec4 b, vec4 a) {
|
vec4 alpha_blend(vec4 b, vec4 a) {
|
||||||
float alpha = a.a + (b.a * (1.0 - a.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);
|
vec3 col = ((a.rgb*a.a) + ((b.rgb*b.a) * (1.0 - a.a)) / alpha);
|
||||||
|
|
|
@ -129,18 +129,20 @@ const gdshader_preprocessor = [
|
||||||
]
|
]
|
||||||
# shaderlib
|
# shaderlib
|
||||||
var shaderlib_regex = {
|
var shaderlib_regex = {
|
||||||
"hsv": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/hsv\.gdshaderinc\"'),
|
"colorspaces": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/colorspaces\.gdshaderinc\"'),
|
||||||
"transform": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transform\.gdshaderinc\"'),
|
"transform": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transform\.gdshaderinc\"'),
|
||||||
"transparency": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transparency\.gdshaderinc\"'),
|
"transparency": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/transparency\.gdshaderinc\"'),
|
||||||
"effects": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/effects\.gdshaderinc\"'),
|
"effects": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/effects\.gdshaderinc\"'),
|
||||||
"denoise": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/denoise\.gdshaderinc\"')
|
"denoise": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/denoise\.gdshaderinc\"'),
|
||||||
|
"blur": RegEx.create_from_string(r'\s*\#include\s+\"res\:\/\/shaderlib\/blur\.gdshaderinc\"'),
|
||||||
}
|
}
|
||||||
const shaderlib_functions = {
|
const shaderlib_functions = {
|
||||||
"hsv": ["rgb2hsv", "hsv2rgb", "hsv_offset", "hsv_multiply"],
|
"colorspaces": ["rgb2hsv", "hsv2rgb", "oklab2rgb", "rgb2oklab", "oklab2oklch", "oklch2oklab"],
|
||||||
"transform": ["place_texture"],
|
"transform": ["place_texture"],
|
||||||
"transparency": ["alpha_blend"],
|
"transparency": ["alpha_blend"],
|
||||||
"effects": ["pixelate"],
|
"effects": ["pixelate"],
|
||||||
"denoise": ["smart_denoise"]
|
"denoise": ["smart_denoise"],
|
||||||
|
"blur": ["gaussian_blur"],
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
# configure Highlighter
|
# configure Highlighter
|
||||||
|
|
|
@ -1,9 +1,22 @@
|
||||||
extends SubViewport
|
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 camera = %Camera
|
||||||
@onready var image_sprite = %ImageSprite
|
|
||||||
@onready var image_viewport_display = %ImageViewportDisplay
|
@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*')
|
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:
|
func validate_shader_compilation(shader: Shader) -> bool:
|
||||||
|
@ -31,7 +44,7 @@ func inject_step_uniform(shader_code: String) -> Shader:
|
||||||
shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;")
|
shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;")
|
||||||
return shader
|
return shader
|
||||||
|
|
||||||
func update() -> Array: # returns error messages (strings)
|
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
|
||||||
# inject STEP uniform & get number of steps
|
# inject STEP uniform & get number of steps
|
||||||
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
|
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
|
||||||
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
|
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
|
||||||
|
@ -40,11 +53,15 @@ func update() -> Array: # returns error messages (strings)
|
||||||
return ["Shader compilation failed!"]
|
return ["Shader compilation failed!"]
|
||||||
var errors = []
|
var errors = []
|
||||||
# load texture(s) from //!load directive -> TEXTURE
|
# load texture(s) from //!load directive -> TEXTURE
|
||||||
|
var original_image_path = ""
|
||||||
|
if overwrite_image_path == "":
|
||||||
var m = ShaderDirectiveParser.parse_load_directive(shader.code)
|
var m = ShaderDirectiveParser.parse_load_directive(shader.code)
|
||||||
if len(m) < 1:
|
if len(m) < 1:
|
||||||
errors.append("Didn't find a load directive!")
|
errors.append("Didn't find a load directive!")
|
||||||
return errors
|
return errors
|
||||||
var original_image_path = Filesystem.get_absolute_path(m[1])
|
original_image_path = Filesystem.get_absolute_path(m[1])
|
||||||
|
else:
|
||||||
|
original_image_path = overwrite_image_path
|
||||||
var fit_image = false
|
var fit_image = false
|
||||||
if original_image_path != Filesystem.last_original_image_path:
|
if original_image_path != Filesystem.last_original_image_path:
|
||||||
fit_image = true
|
fit_image = true
|
48
src/Main.gd
48
src/Main.gd
|
@ -4,7 +4,54 @@ extends Node
|
||||||
@onready var ui_container = %UserInterfaceContainer
|
@onready var ui_container = %UserInterfaceContainer
|
||||||
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
@onready var app_name = ProjectSettings.get_setting("application/config/name")
|
||||||
|
|
||||||
|
func show_help():
|
||||||
|
print(
|
||||||
|
"Usage:\n\n",
|
||||||
|
"./Fragmented cmd --shader PATH [--load-image PATH]\n\n",
|
||||||
|
" --shader PATH The path to the shader\n",
|
||||||
|
" --output PATH Where to write the resulting image to\n",
|
||||||
|
" --load-image PATH The path to the image. This will overwrite the\n",
|
||||||
|
" load directive of the shader file (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 _ready():
|
func _ready():
|
||||||
|
var args = OS.get_cmdline_args()
|
||||||
|
if "cmd" in args: # commandline interface
|
||||||
|
if "help" in args:
|
||||||
|
show_help()
|
||||||
|
get_tree().quit(1)
|
||||||
|
else:
|
||||||
|
var kwargs: Dictionary = parse_custom_cmdline(args)
|
||||||
|
if kwargs["--shader"] == null or kwargs["--output"] == null:
|
||||||
|
show_help()
|
||||||
|
get_tree().quit(1)
|
||||||
|
else:
|
||||||
|
Filesystem.load_shader(kwargs["--shader"])
|
||||||
|
var errors = []
|
||||||
|
if kwargs["--load-image"] == null:
|
||||||
|
errors = await $Compositor.update()
|
||||||
|
else:
|
||||||
|
errors = await $Compositor.update(kwargs["--load-image"])
|
||||||
|
if errors.size() > 0:
|
||||||
|
print("One or more errors occurred.")
|
||||||
|
for e in errors:
|
||||||
|
printerr(e)
|
||||||
|
get_tree().quit(1)
|
||||||
|
else:
|
||||||
|
Filesystem.save_result(kwargs["--output"])
|
||||||
|
get_tree().quit(0)
|
||||||
|
else:
|
||||||
update_title()
|
update_title()
|
||||||
# position windows
|
# position windows
|
||||||
get_window().position = Vector2i(
|
get_window().position = Vector2i(
|
||||||
|
@ -12,6 +59,7 @@ func _ready():
|
||||||
editor_window.position.y)
|
editor_window.position.y)
|
||||||
get_window().min_size = Vector2i(400, 400)
|
get_window().min_size = Vector2i(400, 400)
|
||||||
editor_window.min_size = Vector2i(560, 400)
|
editor_window.min_size = Vector2i(560, 400)
|
||||||
|
editor_window.show()
|
||||||
# Load last opened file
|
# Load last opened file
|
||||||
Filesystem.remember_last_opened_file()
|
Filesystem.remember_last_opened_file()
|
||||||
if Filesystem.last_shader_savepath != "":
|
if Filesystem.last_shader_savepath != "":
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue