mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +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:
parent
2e3f0a2696
commit
cfbe851da5
|
@ -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)
|
||||
|
|
82
src/Shaders/Effects/ConvolutionMatrix.gdshader
Normal file
82
src/Shaders/Effects/ConvolutionMatrix.gdshader
Normal 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;
|
||||
}
|
65
src/UI/Nodes/BasisSliders.gd
Normal file
65
src/UI/Nodes/BasisSliders.gd
Normal 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)
|
40
src/UI/Nodes/BasisSliders.tscn
Normal file
40
src/UI/Nodes/BasisSliders.tscn
Normal 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"]
|
|
@ -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):
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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")),
|
||||
|
|
Loading…
Reference in a new issue