1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 09:09:47 +00:00

Add a convolution matrix layer effect

Still WIP, could use some extra parameters such as RGBA channel, and I should also implement it as an image effect.
This commit is contained in:
Emmanouil Papadeas 2024-08-16 04:24:43 +03:00
parent 2e3f0a2696
commit cfbe851da5
7 changed files with 283 additions and 40 deletions

View file

@ -118,6 +118,7 @@ const CONFIG_SUBDIR_NAME := "pixelorama_data"
## The path of the directory where the UI layouts are being stored.
const LAYOUT_DIR := "user://layouts"
const VALUE_SLIDER_V2_TSCN := preload("res://src/UI/Nodes/ValueSliderV2.tscn")
const BASIS_SLIDERS_TSCN := preload("res://src/UI/Nodes/BasisSliders.tscn")
const GRADIENT_EDIT_TSCN := preload("res://src/UI/Nodes/GradientEdit.tscn")
## It is path to the executable's base drectory.
@ -1214,6 +1215,25 @@ func create_ui_for_shader_uniforms(
hbox.add_child(label)
hbox.add_child(color_button)
parent_node.add_child(hbox)
elif u_type == "mat3":
var label := Label.new()
label.text = humanized_u_name
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var basis := _mat3str_to_basis(u_value)
var sliders := BASIS_SLIDERS_TSCN.instantiate() as BasisSliders
sliders.allow_greater = true
sliders.allow_lesser = true
sliders.size_flags_horizontal = Control.SIZE_EXPAND_FILL
sliders.value = basis
if params.has(u_name):
sliders.value = params[u_name]
else:
params[u_name] = sliders.value
sliders.value_changed.connect(value_changed.bind(u_name))
var hbox := HBoxContainer.new()
hbox.add_child(label)
hbox.add_child(sliders)
parent_node.add_child(hbox)
elif u_type == "sampler2D":
if u_name == "selection":
continue
@ -1299,6 +1319,24 @@ func _vec2str_to_vector2(vec2: String) -> Vector2:
return vector2
func _vec3str_to_vector3(vec3: String) -> Vector3:
vec3 = vec3.replace("uvec3", "vec3")
vec3 = vec3.replace("ivec3", "vec3")
vec3 = vec3.replace("vec3(", "")
vec3 = vec3.replace(")", "")
var vec_values := vec3.split(",")
if vec_values.size() == 0:
return Vector3.ZERO
var y := float(vec_values[0])
var z := float(vec_values[0])
if vec_values.size() >= 2:
y = float(vec_values[1])
if vec_values.size() == 3:
z = float(vec_values[2])
var vector3 := Vector3(float(vec_values[0]), y, z)
return vector3
func _vec4str_to_color(vec4: String) -> Color:
vec4 = vec4.replace("vec4(", "")
vec4 = vec4.replace(")", "")
@ -1320,6 +1358,24 @@ func _vec4str_to_color(vec4: String) -> Color:
return color
func _mat3str_to_basis(mat3: String) -> Basis:
mat3 = mat3.replace("mat3(", "")
mat3 = mat3.replace("))", ")")
mat3 = mat3.replace("), ", ")")
var vec3_values := mat3.split("vec3", false)
var vec3_x := _vec3str_to_vector3(vec3_values[0])
var vec3_y := _vec3str_to_vector3(vec3_values[0])
if vec3_values.size() >= 2:
vec3_y = _vec3str_to_vector3(vec3_values[1])
var vec3_z := _vec3str_to_vector3(vec3_values[0])
if vec3_values.size() == 3:
vec3_z = _vec3str_to_vector3(vec3_values[2])
var basis := Basis(vec3_x, vec3_y, vec3_z)
return basis
func _shader_change_palette(value_changed: Callable, parameter_name: String) -> void:
var palette := Palettes.current_palette
_shader_update_palette_texture(palette, value_changed, parameter_name)

View file

@ -0,0 +1,82 @@
shader_type canvas_item;
// author : csblo
// Work made just by consulting :
// https://en.wikipedia.org/wiki/Kernel_(image_processing)
uniform mat3 kernel = mat3(vec3(0, 0, 0), vec3(0, 1, 0), vec3(0, 0, 0));
// Find coordinate of matrix element from index
vec2 kpos(int index, vec2 iResolution)
{
return vec2[9] (
vec2(-1, -1), vec2(0, -1), vec2(1, -1),
vec2(-1, 0), vec2(0, 0), vec2(1, 0),
vec2(-1, 1), vec2(0, 1), vec2(1, 1)
)[index] / iResolution.xy;
}
// Extract region of dimension 3x3 from sampler centered in uv
// sampler : texture sampler
// uv : current coordinates on sampler
// return : an array of mat3, each index corresponding with a color channel
mat3[3] region3x3(sampler2D sampler, vec2 uv, vec2 iResolution)
{
// Create each pixels for region
vec4[9] region;
for (int i = 0; i < 9; i++)
region[i] = texture(sampler, uv + kpos(i, iResolution));
// Create 3x3 region with 3 color channels (red, green, blue)
mat3[3] mRegion;
for (int i = 0; i < 3; i++)
mRegion[i] = mat3(
vec3(region[0][i], region[1][i], region[2][i]),
vec3(region[3][i], region[4][i], region[5][i]),
vec3(region[6][i], region[7][i], region[8][i])
);
return mRegion;
}
// Convolve a texture with kernel
// kernel : kernel used for convolution
// sampler : texture sampler
// uv : current coordinates on sampler
vec3 convolution(sampler2D sampler, vec2 uv, vec2 iResolution)
{
vec3 fragment;
// Extract a 3x3 region centered in uv
mat3[3] region = region3x3(sampler, uv, iResolution);
// for each color channel of region
for (int i = 0; i < 3; i++)
{
// get region channel
mat3 rc = region[i];
// component wise multiplication of kernel by region channel
mat3 c = matrixCompMult(kernel, rc);
// add each component of matrix
float r = c[0][0] + c[1][0] + c[2][0]
+ c[0][1] + c[1][1] + c[2][1]
+ c[0][2] + c[1][2] + c[2][2];
// for fragment at channel i, set result
fragment[i] = r;
}
return fragment;
}
void fragment()
{
// Convolve kernel with texture
vec3 col = convolution(TEXTURE, UV, 1.0 / TEXTURE_PIXEL_SIZE);
// Output to screen
COLOR.rgb = col;
}

View file

@ -0,0 +1,65 @@
@tool
class_name BasisSliders
extends HBoxContainer
signal value_changed(value: Basis)
@export var value: Basis:
set(val):
value = val
_can_emit_signal = false
get_sliders()[0].value = value.x
get_sliders()[1].value = value.y
get_sliders()[2].value = value.z
_can_emit_signal = true
@export var min_value := Vector3.ZERO:
set(val):
min_value = val
get_sliders()[0].min_value = val
get_sliders()[1].min_value = val
get_sliders()[2].min_value = val
@export var max_value := Vector3(100.0, 100.0, 100.0):
set(val):
max_value = val
get_sliders()[0].max_value = val
get_sliders()[1].max_value = val
get_sliders()[2].max_value = val
@export var step := 1.0:
set(val):
step = val
for slider in get_sliders():
slider.step = val
@export var allow_greater := false:
set(val):
allow_greater = val
for slider in get_sliders():
slider.allow_greater = val
@export var allow_lesser := false:
set(val):
allow_lesser = val
for slider in get_sliders():
slider.allow_lesser = val
var _can_emit_signal := true
func get_sliders() -> Array[ValueSliderV3]:
return [$XSlider, $YSlider, $ZSlider]
func _on_x_slider_value_changed(val: Vector3) -> void:
value.x = val
if _can_emit_signal:
value_changed.emit(value)
func _on_y_slider_value_changed(val: Vector3) -> void:
value.y = val
if _can_emit_signal:
value_changed.emit(value)
func _on_z_slider_value_changed(val: Vector3) -> void:
value.z = val
if _can_emit_signal:
value_changed.emit(value)

View file

@ -0,0 +1,40 @@
[gd_scene load_steps=3 format=3 uid="uid://d0d66oh6bw3kt"]
[ext_resource type="Script" path="res://src/UI/Nodes/BasisSliders.gd" id="1_sbf5t"]
[ext_resource type="PackedScene" uid="uid://dpoteid430evf" path="res://src/UI/Nodes/ValueSliderV3.tscn" id="2_7swri"]
[node name="BasisSliders" type="HBoxContainer"]
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("1_sbf5t")
[node name="XSlider" parent="." instance=ExtResource("2_7swri")]
layout_mode = 2
size_flags_horizontal = 3
value = Vector3(1, 0, 0)
slider_min_size = Vector2(64, 24)
prefix_x = "XX:"
prefix_y = "YX:"
prefix_z = "ZX:"
[node name="YSlider" parent="." instance=ExtResource("2_7swri")]
layout_mode = 2
size_flags_horizontal = 3
value = Vector3(0, 1, 0)
slider_min_size = Vector2(64, 24)
prefix_x = "XY:"
prefix_y = "YY:"
prefix_z = "ZY:"
[node name="ZSlider" parent="." instance=ExtResource("2_7swri")]
layout_mode = 2
size_flags_horizontal = 3
value = Vector3(0, 0, 1)
slider_min_size = Vector2(64, 24)
prefix_x = "XZ:"
prefix_y = "YZ:"
prefix_z = "ZZ:"
[connection signal="value_changed" from="XSlider" to="." method="_on_x_slider_value_changed"]
[connection signal="value_changed" from="YSlider" to="." method="_on_y_slider_value_changed"]
[connection signal="value_changed" from="ZSlider" to="." method="_on_z_slider_value_changed"]

View file

@ -4,8 +4,8 @@ extends HBoxContainer
## A class that combines three ValueSlider nodes, for easy usage with Vector3 values.
## Also supports aspect ratio locking.
signal value_changed(value)
signal ratio_toggled(button_pressed)
signal value_changed(value: Vector3)
signal ratio_toggled(button_pressed: bool)
@export var editable := true:
set(val):

View file

@ -1,86 +1,83 @@
[gd_scene load_steps=6 format=2]
[gd_scene load_steps=6 format=3 uid="uid://dpoteid430evf"]
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=1]
[ext_resource path="res://src/UI/Nodes/ValueSliderV3.gd" type="Script" id=2]
[ext_resource path="res://assets/graphics/misc/lock_aspect_2.png" type="Texture2D" id=3]
[ext_resource path="res://assets/graphics/misc/lock_aspect_guides.png" type="Texture2D" id=4]
[ext_resource path="res://assets/graphics/misc/lock_aspect.png" type="Texture2D" id=5]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="1"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSliderV3.gd" id="2"]
[ext_resource type="Texture2D" uid="uid://cancw70yw0pv7" path="res://assets/graphics/misc/lock_aspect_2.png" id="3"]
[ext_resource type="Texture2D" uid="uid://kd10jfc1dxf5" path="res://assets/graphics/misc/lock_aspect_guides.png" id="4"]
[ext_resource type="Texture2D" uid="uid://beqermx8s5q8y" path="res://assets/graphics/misc/lock_aspect.png" id="5"]
[node name="ValueSliderV3" type="HBoxContainer"]
offset_right = 45.0
offset_bottom = 52.0
script = ExtResource( 2 )
script = ExtResource("2")
[node name="GridContainer" type="GridContainer" parent="."]
offset_right = 45.0
offset_bottom = 80.0
layout_mode = 2
size_flags_horizontal = 3
[node name="X" type="TextureProgressBar" parent="GridContainer"]
offset_right = 45.0
offset_bottom = 24.0
custom_minimum_size = Vector2( 32, 24 )
mouse_default_cursor_shape = 2
custom_minimum_size = Vector2(32, 24)
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
focus_mode = 2
mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 1 )
script = ExtResource("1")
prefix = "X:"
[node name="Y" type="TextureProgressBar" parent="GridContainer"]
offset_top = 28.0
offset_right = 45.0
offset_bottom = 52.0
custom_minimum_size = Vector2( 32, 24 )
mouse_default_cursor_shape = 2
custom_minimum_size = Vector2(32, 24)
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
focus_mode = 2
mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 1 )
script = ExtResource("1")
prefix = "Y:"
[node name="Z" type="TextureProgressBar" parent="GridContainer"]
offset_top = 56.0
offset_right = 45.0
offset_bottom = 80.0
custom_minimum_size = Vector2( 32, 24 )
mouse_default_cursor_shape = 2
custom_minimum_size = Vector2(32, 24)
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
focus_mode = 2
mouse_default_cursor_shape = 2
theme_type_variation = &"ValueSlider"
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 1 )
script = ExtResource("1")
prefix = "Z:"
[node name="Ratio" type="Control" parent="."]
visible = false
offset_left = 36.0
offset_right = 52.0
offset_bottom = 80.0
custom_minimum_size = Vector2( 16, 0 )
custom_minimum_size = Vector2(16, 0)
layout_mode = 2
[node name="RatioGuides" type="NinePatchRect" parent="Ratio" groups=["UIButtons"]]
custom_minimum_size = Vector2(9, 0)
layout_mode = 0
anchor_bottom = 1.0
offset_right = 9.0
custom_minimum_size = Vector2( 9, 0 )
texture = ExtResource( 4 )
region_rect = Rect2( 0, 0, 9, 44 )
texture = ExtResource("4")
region_rect = Rect2(0, 0, 9, 44)
patch_margin_top = 15
patch_margin_bottom = 13
[node name="RatioButton" type="TextureButton" parent="Ratio" groups=["UIButtons"]]
unique_name_in_owner = true
layout_mode = 0
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
@ -92,8 +89,8 @@ offset_bottom = 8.0
tooltip_text = "Lock aspect ratio"
mouse_default_cursor_shape = 2
toggle_mode = true
texture_normal = ExtResource( 3 )
texture_pressed = ExtResource( 5 )
texture_normal = ExtResource("3")
texture_pressed = ExtResource("5")
[connection signal="value_changed" from="GridContainer/X" to="." method="_on_X_value_changed"]
[connection signal="value_changed" from="GridContainer/Y" to="." method="_on_Y_value_changed"]

View file

@ -4,6 +4,9 @@ const LAYER_EFFECT_BUTTON = preload("res://src/UI/Timeline/LayerEffects/LayerEff
const DELETE_TEXTURE := preload("res://assets/graphics/misc/close.svg")
var effects: Array[LayerEffect] = [
LayerEffect.new(
"Convolution Matrix", preload("res://src/Shaders/Effects/ConvolutionMatrix.gdshader")
),
LayerEffect.new("Offset", preload("res://src/Shaders/Effects/OffsetPixels.gdshader")),
LayerEffect.new("Outline", preload("res://src/Shaders/Effects/OutlineInline.gdshader")),
LayerEffect.new("Drop Shadow", preload("res://src/Shaders/Effects/DropShadow.gdshader")),