- New Kuwahara filter node.

This commit is contained in:
Tanasart 2024-02-09 20:43:03 +07:00
parent ab3041f2fc
commit 8e8435cd14
25 changed files with 418 additions and 2 deletions

View file

@ -854,6 +854,7 @@
{"name":"s_node_3d_point_affector","order":21,"path":"sprites/s_node_3d_point_affector/s_node_3d_point_affector.yy",},
{"name":"polygon_points","order":2,"path":"scripts/polygon_points/polygon_points.yy",},
{"name":"transformBox","order":15,"path":"scripts/transformBox/transformBox.yy",},
{"name":"sh_kuwahara","order":51,"path":"shaders/sh_kuwahara/sh_kuwahara.yy",},
{"name":"sh_grey_alpha","order":12,"path":"shaders/sh_grey_alpha/sh_grey_alpha.yy",},
{"name":"node_mk_cable","order":4,"path":"scripts/node_mk_cable/node_mk_cable.yy",},
{"name":"s_node_time_map","order":36,"path":"sprites/s_node_time_map/s_node_time_map.yy",},
@ -899,6 +900,7 @@
{"name":"node_dust","order":3,"path":"scripts/node_dust/node_dust.yy",},
{"name":"node_noise_cell","order":2,"path":"scripts/node_noise_cell/node_noise_cell.yy",},
{"name":"s_node_pb_fx_radial","order":4,"path":"sprites/s_node_pb_fx_radial/s_node_pb_fx_radial.yy",},
{"name":"node_kuwahara","order":21,"path":"scripts/node_kuwahara/node_kuwahara.yy",},
{"name":"__background_get_internal","order":2,"path":"scripts/__background_get_internal/__background_get_internal.yy",},
{"name":"s_node_array_sample","order":18,"path":"sprites/s_node_array_sample/s_node_array_sample.yy",},
{"name":"o_pie_menu","order":14,"path":"objects/o_pie_menu/o_pie_menu.yy",},
@ -1311,6 +1313,7 @@
{"name":"__node_3d_mesh","order":1,"path":"scripts/__node_3d_mesh/__node_3d_mesh.yy",},
{"name":"d3d_surface_extrude","order":7,"path":"scripts/d3d_surface_extrude/d3d_surface_extrude.yy",},
{"name":"node_mesh_to_path","order":3,"path":"scripts/node_mesh_to_path/node_mesh_to_path.yy",},
{"name":"sh_kuwahara_ani","order":52,"path":"shaders/sh_kuwahara_ani/sh_kuwahara_ani.yy",},
{"name":"sh_convolution","order":24,"path":"shaders/sh_convolution/sh_convolution.yy",},
{"name":"FirebaseREST_HTTP_Failed_Firestore","order":18,"path":"scripts/FirebaseREST_HTTP_Failed_Firestore/FirebaseREST_HTTP_Failed_Firestore.yy",},
{"name":"number_function","order":14,"path":"scripts/number_function/number_function.yy",},
@ -1692,6 +1695,7 @@
{"name":"addon","order":1,"path":"objects/addon/addon.yy",},
{"name":"_f_h3","order":3,"path":"fonts/_f_h3/_f_h3.yy",},
{"name":"s_node_math","order":1,"path":"sprites/s_node_math/s_node_math.yy",},
{"name":"s_node_kuwahara","order":59,"path":"sprites/s_node_kuwahara/s_node_kuwahara.yy",},
{"name":"panel_text_editor","order":11,"path":"scripts/panel_text_editor/panel_text_editor.yy",},
{"name":"node_path_bake","order":15,"path":"scripts/node_path_bake/node_path_bake.yy",},
{"name":"node_iterator_index","order":2,"path":"scripts/node_iterator_index/node_iterator_index.yy",},

View file

@ -1126,6 +1126,7 @@
{"id":{"name":"s_node_3d_point_affector","path":"sprites/s_node_3d_point_affector/s_node_3d_point_affector.yy",},},
{"id":{"name":"polygon_points","path":"scripts/polygon_points/polygon_points.yy",},},
{"id":{"name":"transformBox","path":"scripts/transformBox/transformBox.yy",},},
{"id":{"name":"sh_kuwahara","path":"shaders/sh_kuwahara/sh_kuwahara.yy",},},
{"id":{"name":"s_node_fluidSim_group","path":"sprites/s_node_fluidSim_group/s_node_fluidSim_group.yy",},},
{"id":{"name":"sh_grey_alpha","path":"shaders/sh_grey_alpha/sh_grey_alpha.yy",},},
{"id":{"name":"node_mk_cable","path":"scripts/node_mk_cable/node_mk_cable.yy",},},
@ -1177,6 +1178,7 @@
{"id":{"name":"node_dust","path":"scripts/node_dust/node_dust.yy",},},
{"id":{"name":"node_noise_cell","path":"scripts/node_noise_cell/node_noise_cell.yy",},},
{"id":{"name":"s_node_pb_fx_radial","path":"sprites/s_node_pb_fx_radial/s_node_pb_fx_radial.yy",},},
{"id":{"name":"node_kuwahara","path":"scripts/node_kuwahara/node_kuwahara.yy",},},
{"id":{"name":"__background_get_internal","path":"scripts/__background_get_internal/__background_get_internal.yy",},},
{"id":{"name":"s_node_array_sample","path":"sprites/s_node_array_sample/s_node_array_sample.yy",},},
{"id":{"name":"o_pie_menu","path":"objects/o_pie_menu/o_pie_menu.yy",},},
@ -1642,6 +1644,7 @@
{"id":{"name":"sh_mk_tile18_edge_b","path":"shaders/sh_mk_tile18_edge_b/sh_mk_tile18_edge_b.yy",},},
{"id":{"name":"node_mesh_to_path","path":"scripts/node_mesh_to_path/node_mesh_to_path.yy",},},
{"id":{"name":"node_fluid_sim","path":"scripts/node_fluid_sim/node_fluid_sim.yy",},},
{"id":{"name":"sh_kuwahara_ani","path":"shaders/sh_kuwahara_ani/sh_kuwahara_ani.yy",},},
{"id":{"name":"sh_convolution","path":"shaders/sh_convolution/sh_convolution.yy",},},
{"id":{"name":"FirebaseREST_HTTP_Failed_Firestore","path":"scripts/FirebaseREST_HTTP_Failed_Firestore/FirebaseREST_HTTP_Failed_Firestore.yy",},},
{"id":{"name":"number_function","path":"scripts/number_function/number_function.yy",},},
@ -2083,6 +2086,7 @@
{"id":{"name":"string_decimal","path":"scripts/string_decimal/string_decimal.yy",},},
{"id":{"name":"_f_h3","path":"fonts/_f_h3/_f_h3.yy",},},
{"id":{"name":"s_node_math","path":"sprites/s_node_math/s_node_math.yy",},},
{"id":{"name":"s_node_kuwahara","path":"sprites/s_node_kuwahara/s_node_kuwahara.yy",},},
{"id":{"name":"panel_text_editor","path":"scripts/panel_text_editor/panel_text_editor.yy",},},
{"id":{"name":"node_path_bake","path":"scripts/node_path_bake/node_path_bake.yy",},},
{"id":{"name":"node_iterator_index","path":"scripts/node_iterator_index/node_iterator_index.yy",},},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -13,7 +13,7 @@
randomize();
#endregion
#region main
#region ======================================================================= MAIN =======================================================================
globalvar OS, DEBUG, THEME, COLOR_KEYS;
globalvar CMD, CMDIN;

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "filter",
"path": "folders/nodes/data/filter.yy",
},
"resourceVersion": "1.0",
"name": "node_alpha_to_grey",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "filter",
"path": "folders/nodes/data/filter.yy",
},
"resourceVersion": "1.0",
"name": "node_bw",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "filter",
"path": "folders/nodes/data/filter.yy",
},
"resourceVersion": "1.0",
"name": "node_color_adjustment",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "process",
"path": "folders/nodes/data/process.yy",
},
"resourceVersion": "1.0",
"name": "node_color_replacement",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "filter",
"path": "folders/nodes/data/filter.yy",
},
"resourceVersion": "1.0",
"name": "node_greyscale",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -0,0 +1,28 @@
function Node_Kuwahara(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
name = "Kuwahara";
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, 0);
inputs[| 1] = nodeValue("Active", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);
active_index = 1;
inputs[| 2] = nodeValue("Radius", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 2);
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
input_display_list = [ 1, 0, 2 ];
attribute_surface_depth();
static processData = function(_outSurf, _data, _output_index, _array_index) { #region
surface_set_shader(_outSurf, sh_kuwahara);
shader_set_f("dimension", surface_get_dimension(_data[0]));
shader_set_i("radius", _data[2]);
draw_surface_safe(_data[0]);
surface_reset_shader();
return _outSurf;
} #endregion
}

View file

@ -0,0 +1,11 @@
{
"resourceType": "GMScript",
"resourceVersion": "1.0",
"name": "node_kuwahara",
"isCompatibility": false,
"isDnD": false,
"parent": {
"name": "effects",
"path": "folders/nodes/data/filter/effects.yy",
},
}

View file

@ -0,0 +1,12 @@
{
"isDnD": false,
"isCompatibility": false,
"parent": {
"name": "process",
"path": "folders/nodes/data/process.yy",
},
"resourceVersion": "1.0",
"name": "node_outline",
"tags": [],
"resourceType": "GMScript",
}

View file

@ -538,6 +538,7 @@ function __initNodes() {
addNodeObject(filter, "Chromatic Aberration", s_node_chromatic_abarration, "Node_Chromatic_Aberration", [1, Node_Chromatic_Aberration],, "Apply chromatic aberration effect to the image.");
addNodeObject(filter, "Vignette", s_node_vignette, "Node_Vignette", [1, Node_Vignette],, "Apply vignette effect to the border.").setVersion(11630);
addNodeObject(filter, "FXAA", s_node_FXAA, "Node_FXAA", [1, Node_FXAA],, "Apply fast approximate anti-aliasing to the image.");
addNodeObject(filter, "Kuwahara", s_node_kuwahara, "Node_Kuwahara", [1, Node_Kuwahara]);
//addNodeObject(filter, "Blend Edge", s_node_FXAA, "Node_Blend_Edge", [1, Node_Blend_Edge]).setVersion(11640);
ds_list_add(filter, "Colors");
@ -936,7 +937,7 @@ function __initNodes() {
var actions = ds_list_create();
addNodeCatagory("Action", actions);
__initNodeActions(actions);
var customs = ds_list_create();
addNodeCatagory("Custom", customs);
__initNodeCustom(customs);

View file

@ -0,0 +1,109 @@
/*
Generalized Kuwahara filter, based on work of Acerola
Basically Acerola code rewritten to Shadertoy
- https://www.youtube.com/watch?v=LDhN-JK3U9g
- https://github.com/GarrettGunnell/Post-Processing/tree/main/Assets/Kuwahara%20Filter
*/
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
#define PI 3.14159265358979323846;
#define MAX_RAD 64
const float q = 18.;
uniform vec2 dimension;
uniform int radius;
void main () {
vec2 tx = 1. / dimension;
float zeta = 2. / float(radius);
float zeroCross = 2.;
float sinZeroCross = sin(zeroCross);
float eta = 0.;
vec4 m[8];
vec3 s[8];
for (int k = 0; k < 8; ++k) {
m[k] = vec4(0.);
s[k] = vec3(0.);
}
for (int y = -MAX_RAD; y <= MAX_RAD; y++)
for (int x = -MAX_RAD; x <= MAX_RAD; x++) {
if(y < -radius) continue;
if(x < -radius) continue;
if(x > radius) continue;
if(y > radius) break;
vec2 v = vec2(x, y) / float(radius);
vec3 c = texture2D( gm_BaseTexture, v_vTexcoord + vec2(x, y) * tx).xyz;
c = clamp(c, 0., 1.);
float sum = 0.;
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.0) / 2.0 * 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.125 * dot(v, v)) / (sum + 0.0001);
for (int k = 0; k < 8; k++) {
float wk = w[k] * g;
m[k] += vec4(c * wk, wk);
s[k] += c * c * wk;
}
}
vec4 ou = vec4(0.);
for (int k = 0; k < 8; 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.0 / (1.0 + pow(1000.0 * sigma2, 0.5 * q));
ou += vec4(m[k].rgb * w, w);
}
gl_FragColor = clamp((ou / ou.w), 0.0, 1.0);
}

View file

@ -0,0 +1,18 @@
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position; // (x,y,z)
//attribute vec3 in_Normal; // (x,y,z) unused in this shader.
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}

View file

@ -0,0 +1,10 @@
{
"resourceType": "GMShader",
"resourceVersion": "1.0",
"name": "sh_kuwahara",
"parent": {
"name": "filter",
"path": "folders/shader/filter.yy",
},
"type": 1,
}

View file

@ -0,0 +1,57 @@
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
#define MAX_RAD 64
uniform vec2 dimension;
uniform int radius;
void main () {
gl_FragColor = texture2D(gm_BaseTexture, v_vTexcoord);
vec2 tx = 1. / dimension;
float n = float((radius + 1) * (radius + 1));
vec3 m[4];
vec3 s[4];
vec3 c;
for (int k = 0; k < 4; ++k) {
m[k] = vec3(0.0);
s[k] = vec3(0.0);
}
for (int j = 0; j <= MAX_RAD; j++)
for (int i = 0; i <= MAX_RAD; i++) {
if(i > radius) continue;
if(j > radius) break;
c = texture2D(gm_BaseTexture, v_vTexcoord + vec2(-i, -j) * tx).rgb;
m[0] += c;
s[0] += c * c;
c = texture2D(gm_BaseTexture, v_vTexcoord + vec2( i, -j) * tx).rgb;
m[1] += c;
s[1] += c * c;
c = texture2D(gm_BaseTexture, v_vTexcoord + vec2( i, j) * tx).rgb;
m[2] += c;
s[2] += c * c;
c = texture2D(gm_BaseTexture, v_vTexcoord + vec2(-i, j) * tx).rgb;
m[3] += c;
s[3] += c * c;
}
float min_sigma2 = 100.;
for (int k = 0; k < 4; k++) {
m[k] /= n;
s[k] = abs(s[k] / n - m[k] * m[k]);
float sigma2 = s[k].r + s[k].g + s[k].b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
gl_FragColor = vec4(m[k], 1.0);
}
}
}

View file

@ -0,0 +1,18 @@
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position; // (x,y,z)
//attribute vec3 in_Normal; // (x,y,z) unused in this shader.
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main() {
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}

View file

@ -0,0 +1,10 @@
{
"resourceType": "GMShader",
"resourceVersion": "1.0",
"name": "sh_kuwahara_ani",
"parent": {
"name": "filter",
"path": "folders/shader/filter.yy",
},
"type": 1,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,74 @@
{
"resourceType": "GMSprite",
"resourceVersion": "1.0",
"name": "s_node_kuwahara",
"bbox_bottom": 63,
"bbox_left": 0,
"bbox_right": 63,
"bbox_top": 0,
"bboxMode": 0,
"collisionKind": 1,
"collisionTolerance": 0,
"DynamicTexturePage": false,
"edgeFiltering": false,
"For3D": false,
"frames": [
{"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"d32dffb4-8395-4ac5-800e-6d35f00f9167",},
],
"gridX": 0,
"gridY": 0,
"height": 64,
"HTile": false,
"layers": [
{"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"7f08cf06-022b-4f31-ad2e-39f9ebdaa522","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,},
],
"nineSlice": null,
"origin": 4,
"parent": {
"name": "filter",
"path": "folders/nodes/icons/filter.yy",
},
"preMultiplyAlpha": false,
"sequence": {
"resourceType": "GMSequence",
"resourceVersion": "1.4",
"name": "s_node_kuwahara",
"autoRecord": true,
"backdropHeight": 768,
"backdropImageOpacity": 0.5,
"backdropImagePath": "",
"backdropWidth": 1366,
"backdropXOffset": 0.0,
"backdropYOffset": 0.0,
"events": {"resourceType":"KeyframeStore<MessageEventKeyframe>","resourceVersion":"1.0","Keyframes":[],},
"eventStubScript": null,
"eventToFunction": {},
"length": 1.0,
"lockOrigin": false,
"moments": {"resourceType":"KeyframeStore<MomentsEventKeyframe>","resourceVersion":"1.0","Keyframes":[],},
"playback": 1,
"playbackSpeed": 30.0,
"playbackSpeedType": 0,
"showBackdrop": true,
"showBackdropImage": false,
"timeUnits": 1,
"tracks": [
{"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore<SpriteFrameKeyframe>","resourceVersion":"1.0","Keyframes":[
{"resourceType":"Keyframe<SpriteFrameKeyframe>","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"d32dffb4-8395-4ac5-800e-6d35f00f9167","path":"sprites/s_node_kuwahara/s_node_kuwahara.yy",},},},"Disabled":false,"id":"e7a17aeb-e2e6-4c0b-a6aa-ec494d2715ae","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,},
],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,},
],
"visibleRange": null,
"volume": 1.0,
"xorigin": 32,
"yorigin": 32,
},
"swatchColours": null,
"swfPrecision": 2.525,
"textureGroupId": {
"name": "Default",
"path": "texturegroups/Default",
},
"type": 0,
"VTile": false,
"width": 64,
}