From 93bbdb7c7bec6a41b67f8cd8d4c9876abf69027f Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Mon, 10 Feb 2025 20:51:06 +0200 Subject: [PATCH] Unify the two gradient shaders into one --- ...arDithering.gdshader => Gradient.gdshader} | 53 +++++++++------ src/Shaders/Effects/Gradients/Linear.gdshader | 64 ------------------- src/UI/Dialogs/ImageEffects/GradientDialog.gd | 28 +++----- 3 files changed, 42 insertions(+), 103 deletions(-) rename src/Shaders/Effects/{Gradients/LinearDithering.gdshader => Gradient.gdshader} (60%) delete mode 100644 src/Shaders/Effects/Gradients/Linear.gdshader diff --git a/src/Shaders/Effects/Gradients/LinearDithering.gdshader b/src/Shaders/Effects/Gradient.gdshader similarity index 60% rename from src/Shaders/Effects/Gradients/LinearDithering.gdshader rename to src/Shaders/Effects/Gradient.gdshader index dcb1ed9d1..6888fdbca 100644 --- a/src/Shaders/Effects/Gradients/LinearDithering.gdshader +++ b/src/Shaders/Effects/Gradient.gdshader @@ -2,14 +2,21 @@ shader_type canvas_item; render_mode unshaded; uniform sampler2D selection : filter_nearest; +// The gradient texture itself, with interpolation. uniform sampler2D gradient_texture : filter_nearest; +// A Nx1 image, where N is the amount of the different colors of gradient_texture, +// without interpolating between them. Used in dithering. +uniform sampler2D gradient_texture_no_interpolation : filter_nearest; +// A Nx1 grayscale image, where N is the amount of the different colors of gradient_texture, +// where each pixel contains the offset, ranging from 0-1 of each color. Used in dithering. +uniform sampler2D gradient_offset_texture : filter_nearest; uniform sampler2D dither_texture : filter_nearest, repeat_enable; -uniform sampler2D offset_texture : filter_nearest; uniform float position : hint_range(-0.5, 0.5) = 0.0; uniform float size : hint_range(0.01, 2.0) = 1.0; uniform float angle : hint_range(0.0, 360.0) = 0.0; uniform vec2 center = vec2(0.5); uniform vec2 radius = vec2(1.0); +uniform bool use_dithering = false; uniform int shape = 0; // 0 = linear, 1 = radial uniform int repeat = 0; // 0 = none, 1 = repeat, 2 = mirrored, 3 = truncate @@ -62,36 +69,42 @@ float dither(vec2 uv, float modified_uv, ivec2 image_size) { void fragment() { vec4 original_color = texture(TEXTURE, UV); vec4 selection_color = texture(selection, UV); + //vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; + //vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); float modified_uv = modify_uv(UV); if (repeat == 1) modified_uv = fract(modified_uv); else if (repeat == 2) modified_uv = mirror_fract(modified_uv); - int n_of_colors = textureSize(offset_texture, 0).x; - float colors_minus = float(n_of_colors - 1); vec4 output; - for (int i = 1; i <= n_of_colors; i++) { - float off = texture(offset_texture, vec2(float(i) / colors_minus)).r; - float off_prev = texture(offset_texture, vec2(float(i - 1) / colors_minus)).r; - vec4 first = texture(gradient_texture, vec2(float((i - 1)) / colors_minus)); - vec4 second = texture(gradient_texture, vec2(float(i) / colors_minus)); - if (modified_uv < off_prev) { - if (i == 1) { - output = first; + if (use_dithering) { + int n_of_colors = textureSize(gradient_offset_texture, 0).x; + float colors_minus = float(n_of_colors - 1); + for (int i = 1; i <= n_of_colors; i++) { + float off = texture(gradient_offset_texture, vec2(float(i) / colors_minus)).r; + float off_prev = texture(gradient_offset_texture, vec2(float(i - 1) / colors_minus)).r; + vec4 first = texture(gradient_texture_no_interpolation, vec2(float((i - 1)) / colors_minus)); + vec4 second = texture(gradient_texture_no_interpolation, vec2(float(i) / colors_minus)); + if (modified_uv < off_prev) { + if (i == 1) { + output = first; + } + continue; } - continue; - } - if (modified_uv > off) { - if (i == n_of_colors) { - output = second; + if (modified_uv > off) { + if (i == n_of_colors) { + output = second; + } + continue; } - continue; + float uvt = (modified_uv - off_prev) / (off - off_prev); + float col_sample = dither(UV, uvt, textureSize(TEXTURE, 0)); + output = mix(first, second, col_sample); } - float uvt = (modified_uv - off_prev) / (off - off_prev); - float col_sample = dither(UV, uvt, textureSize(TEXTURE, 0)); - output = mix(first, second, col_sample); } + else + output = texture(gradient_texture, vec2(modified_uv)); if (repeat == 3) output.a = min(step(modified_uv, 1.0) * step(0.0, modified_uv), output.a); output = mix(original_color, output, output.a); diff --git a/src/Shaders/Effects/Gradients/Linear.gdshader b/src/Shaders/Effects/Gradients/Linear.gdshader deleted file mode 100644 index 6579bbaa5..000000000 --- a/src/Shaders/Effects/Gradients/Linear.gdshader +++ /dev/null @@ -1,64 +0,0 @@ -shader_type canvas_item; -render_mode unshaded; - -uniform sampler2D selection : filter_nearest; -uniform sampler2D gradient_texture : filter_nearest; -uniform float position : hint_range(-0.5, 0.5) = 0.0; -uniform float size : hint_range(0.01, 2.0) = 1.0; -uniform float angle : hint_range(0.0, 360.0) = 0.0; -uniform vec2 center = vec2(0.5); -uniform vec2 radius = vec2(1.0); -uniform int shape = 0; // 0 = linear, 1 = radial -uniform int repeat = 0; // 0 = none, 1 = repeat, 2 = mirrored, 3 = truncate - - -float modify_uv(vec2 uv) { - float modified_uv; - if (shape == 0) { // Linear - vec2 uv_offsetted = uv - 0.5; - float angle_cos = cos(radians(angle)); - float angle_sin = sin(radians(angle)); - modified_uv = uv_offsetted.x * angle_cos - uv_offsetted.y * angle_sin; - modified_uv /= abs(angle_cos) + abs(angle_sin); - modified_uv /= size; - float pivot = position / size; - modified_uv -= pivot - 0.5; - } - else { // Radial - vec2 uv_offsetted = uv * 2.0 - 1.0; - uv_offsetted -= (center * 2.0) - vec2(1.0); - uv_offsetted /= radius; - modified_uv = length(uv_offsetted); - } - return modified_uv; -} - - -float mirror_fract(float uv) { - int sign_uv = (int(sign(uv)) - 1) / 2; // returns -1 for negative sign and 0 for positive - if (int((uv)) % 2 == sign_uv) - uv = fract(uv); - else - uv = fract(1.0 - uv); - - return uv; -} - - -void fragment() { - vec4 original_color = texture(TEXTURE, UV); - vec4 selection_color = texture(selection, UV); - - vec2 tex_size = 1.0 / TEXTURE_PIXEL_SIZE; - vec2 uv = floor(UV * tex_size) / (tex_size - 1.0); - float modified_uv = modify_uv(uv); - if (repeat == 1) - modified_uv = fract(modified_uv); - else if (repeat == 2) - modified_uv = mirror_fract(modified_uv); - vec4 output = texture(gradient_texture, vec2(modified_uv)); - if (repeat == 3) - output.a = min(step(modified_uv, 1.0) * step(0.0, modified_uv), output.a); - output = mix(original_color, output, output.a); - COLOR = mix(original_color, output, selection_color.a); -} diff --git a/src/UI/Dialogs/ImageEffects/GradientDialog.gd b/src/UI/Dialogs/ImageEffects/GradientDialog.gd index 2652b82fc..156366ac4 100644 --- a/src/UI/Dialogs/ImageEffects/GradientDialog.gd +++ b/src/UI/Dialogs/ImageEffects/GradientDialog.gd @@ -3,10 +3,7 @@ extends ImageEffect enum { LINEAR, RADIAL, LINEAR_DITHERING, RADIAL_DITHERING } enum Animate { POSITION, SIZE, ANGLE, CENTER_X, CENTER_Y, RADIUS_X, RADIUS_Y } -var shader_linear := preload("res://src/Shaders/Effects/Gradients/Linear.gdshader") -var shader_linear_dither := preload("res://src/Shaders/Effects/Gradients/LinearDithering.gdshader") - -var shader := shader_linear +var shader := preload("res://src/Shaders/Effects/Gradient.gdshader") var selected_dither_matrix := ShaderLoader.dither_matrices[0] @onready var options_cont: Container = $VBoxContainer/GradientOptions @@ -51,11 +48,11 @@ func commit_action(cel: Image, project := Global.current_project) -> void: var offsets := gradient.offsets offsets.sort() var n_of_colors := offsets.size() - # Pass the gradient offsets as an array to the shader - # ...but we can't provide arrays with variable sizes as uniforms, instead we construct - # a nx1 grayscale texture with each offset stored in each pixel, and pass it to the shader + # Pass the gradient offsets as an array to the shader, + # but we can't provide arrays with variable sizes as uniforms, instead we construct + # a Nx1 grayscale texture with each offset stored in each pixel, and pass it to the shader. var offsets_image := Image.create(n_of_colors, 1, false, Image.FORMAT_L8) - # Construct an image that contains the selected colors of the gradient without interpolation + # Construct an image that contains the selected colors of the gradient without interpolation. var gradient_image := Image.create(n_of_colors, 1, false, Image.FORMAT_RGBA8) for i in n_of_colors: var c := offsets[i] @@ -64,12 +61,6 @@ func commit_action(cel: Image, project := Global.current_project) -> void: if actual_index == -1: actual_index = i gradient_image.set_pixel(i, 0, gradient.colors[actual_index]) - var offsets_tex := ImageTexture.create_from_image(offsets_image) - var gradient_tex: Texture2D - if shader == shader_linear: - gradient_tex = gradient_edit.texture - else: - gradient_tex = ImageTexture.create_from_image(gradient_image) var center := Vector2( animate_panel.get_animated_value(commit_idx, Animate.CENTER_X), animate_panel.get_animated_value(commit_idx, Animate.CENTER_Y) @@ -79,8 +70,10 @@ func commit_action(cel: Image, project := Global.current_project) -> void: animate_panel.get_animated_value(commit_idx, Animate.RADIUS_Y) ) var params := { - "gradient_texture": gradient_tex, - "offset_texture": offsets_tex, + "gradient_texture": gradient_edit.texture, + "gradient_texture_no_interpolation": ImageTexture.create_from_image(gradient_image), + "gradient_offset_texture": ImageTexture.create_from_image(offsets_image), + "use_dithering": dithering_option_button.selected > 0, "selection": selection_tex, "repeat": repeat_option_button.selected, "position": (animate_panel.get_animated_value(commit_idx, Animate.POSITION) / 100.0) - 0.5, @@ -124,10 +117,7 @@ func _value_v2_changed(_value: Vector2) -> void: func _on_DitheringOptionButton_item_selected(index: int) -> void: if index > 0: - shader = shader_linear_dither selected_dither_matrix = ShaderLoader.dither_matrices[index - 1] - else: - shader = shader_linear update_preview()