From 1bd11434e46f25b14ca616bb82b4c9170f5e448a Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Wed, 13 Nov 2024 00:17:08 -0700 Subject: [PATCH] Added retro postprocessing shader for later use --- levels/debug_level/debug_level.tscn | 1 + project.godot | 1 - src/player/shot_setup/shot_setup.tscn | 23 ++- src/shaders/canvas_retro.gdshader | 168 ++++++++++++++++++++++ src/shaders/retro_postprocessing.gdshader | 143 ++++++++++++++++++ src/world/world.tscn | 31 ++++ 6 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 src/shaders/canvas_retro.gdshader create mode 100644 src/shaders/retro_postprocessing.gdshader create mode 100644 src/world/world.tscn diff --git a/levels/debug_level/debug_level.tscn b/levels/debug_level/debug_level.tscn index aa6271f..e3446fa 100644 --- a/levels/debug_level/debug_level.tscn +++ b/levels/debug_level/debug_level.tscn @@ -177,6 +177,7 @@ camera_attributes = SubResource("CameraAttributesPractical_ypy22") [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(0.782608, -0.611691, 0.11558, 0, 0.185667, 0.982613, -0.622515, -0.769001, 0.145304, 0, 0, 0) shadow_enabled = true +shadow_blur = 4.0 [node name="Buildings" type="Node3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 129.404, -9.53674e-07, 309.4) diff --git a/project.godot b/project.godot index ac65392..a9aa130 100644 --- a/project.godot +++ b/project.godot @@ -161,5 +161,4 @@ jolt_3d/limits/max_temporary_memory=64 textures/canvas_textures/default_texture_filter=0 anti_aliasing/quality/msaa_3d=3 anti_aliasing/quality/screen_space_aa=1 -anti_aliasing/quality/use_taa=true anti_aliasing/quality/use_debanding=true diff --git a/src/player/shot_setup/shot_setup.tscn b/src/player/shot_setup/shot_setup.tscn index 655ae2c..070b744 100644 --- a/src/player/shot_setup/shot_setup.tscn +++ b/src/player/shot_setup/shot_setup.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=40 format=3 uid="uid://cy7t2tc4y3b4"] +[gd_scene load_steps=42 format=3 uid="uid://cy7t2tc4y3b4"] [ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"] [ext_resource type="PackedScene" uid="uid://dfttci386ohip" path="res://src/player/physics_ball/physics_ball.tscn" id="2_1i5j5"] @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://445qd7m4qe2j" path="res://src/player/shot_setup/club_selector/club_selector.tscn" id="4_56ape"] [ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/projectile_arc/projectile_arc.tscn" id="4_ry2ho"] [ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/projected_target.tscn" id="6_mynqj"] +[ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="7_h6c4m"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lnol1"] albedo_color = Color(0, 0.537255, 1, 1) @@ -480,6 +481,15 @@ _data = { "display": SubResource("Animation_g52q7") } +[sub_resource type="ShaderMaterial" id="ShaderMaterial_afsun"] +shader = ExtResource("7_h6c4m") +shader_parameter/change_color_depth = true +shader_parameter/target_color_depth = 6 +shader_parameter/dithering = true +shader_parameter/scale_resolution = true +shader_parameter/target_resolution_scale = 2 +shader_parameter/enable_recolor = false + [sub_resource type="Animation" id="Animation_pk1s7"] length = 0.001 tracks/0/type = "bezier" @@ -852,6 +862,17 @@ libraries = { "": SubResource("AnimationLibrary_gbnnr") } +[node name="ColorRect" type="ColorRect" parent="ShotUI"] +visible = false +material = SubResource("ShaderMaterial_afsun") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + [node name="ShotGauges" type="Control" parent="ShotUI"] layout_mode = 1 anchor_left = 0.4 diff --git a/src/shaders/canvas_retro.gdshader b/src/shaders/canvas_retro.gdshader new file mode 100644 index 0000000..2696dfb --- /dev/null +++ b/src/shaders/canvas_retro.gdshader @@ -0,0 +1,168 @@ +// CanvasItem Retro Post-processing +// Adapted from https://godotshaders.com/shader/retro-post-processing/ + +shader_type canvas_item; + + +// Handles the resolution changes, color depth, and dithering +uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; + + +group_uniforms resolution_and_colors; +uniform bool change_color_depth = false; +uniform int target_color_depth : hint_range(1, 8) = 5; +uniform bool dithering = false; +uniform bool scale_resolution = false; +uniform int target_resolution_scale = 3; + + +// Handles the LUTish recoloring +group_uniforms gradient_recoloring; +uniform bool enable_recolor = false; +uniform sampler2D to_gradient: hint_default_black; + + +int dithering_pattern(ivec2 fragcoord) { + const int pattern[] = { + -4, +0, -3, +1, + +2, -2, +3, -1, + -3, +1, -4, +0, + +3, -1, +2, -2 + }; + + int x = fragcoord.x % 4; + int y = fragcoord.y % 4; + + return pattern[y * 4 + x]; +} + + +vec3 rgb2hsv(vec3 rgb) { //Converts RGB values to HSV + float r = rgb.r; + float g = rgb.g; + float b = rgb.b; + + + float cmax = max(r,max(g,b)); + float cmin = min(r,min(g,b)); + float delta = cmax - cmin; + + + float h = 0.f; //hue + + + if (delta > 0.f){ + if (cmax == r){ + h = (g-b)/delta; + h = mod(h,6.f); + } else if (cmax == g){ + h = ((b - r) / delta) + 2.f; + } else { + h = ((r-g)/delta) + 4.f; + } + h = h * 60.f; + } + + + float s = 0.f; //saturation + if (cmax > 0.f){ + s = delta / cmax; + } + + + return vec3(h,s,cmax); // Keep original alpha value + + +} + + +vec3 hsv2rgb(vec3 hsv) { //Converts HSV values to RGB + float h = hsv.r; + float s = hsv.g; + float v = hsv.b; + float c = v * s; + //X = C × (1 - |(H / 60°) mod 2 - 1|) + float x = h / 60.f; + x = mod(x,2.f); + x = abs(x - 1.f); + x = c * (1.f - x); + + + float m = v - c; + + + vec3 rgb = vec3(0.f,0.f,0.f); + + + if (h < 60.f) { + rgb = vec3(c,x,0.f); + } else if (h < 120.f){ + rgb = vec3(x,c,0.f); + } else if (h < 180.f){ + rgb = vec3(0.f,c,x); + } else if (h < 240.f){ + rgb = vec3(0.f,x,c); + } else if (h < 300.f){ + rgb = vec3(x,0.f,c); + } else if (h < 360.f){ + rgb = vec3(c,0.f,x); + } + rgb[0] = rgb[0] + m; + rgb[1] = rgb[1] + m; + rgb[2] = rgb[2] + m; + + + return rgb; +} + + +void fragment() { + + vec2 iResolution = 1.0 / SCREEN_PIXEL_SIZE; + vec2 q = FRAGCOORD.xy / iResolution.xy; + ivec2 uv = ivec2(q); + + vec3 color = texture(screen_texture, vec2(q.x,q.y) ).xyz; + + if(scale_resolution){ + uv = ivec2(FRAGCOORD.xy / float(target_resolution_scale)); + color = texelFetch(screen_texture, uv * target_resolution_scale, 0).rgb; + } else { + uv = ivec2(FRAGCOORD.xy); + color = texelFetch(screen_texture, uv, 0).rgb; + } + + if(enable_recolor){ + vec3 hsv = rgb2hsv(color); + float color_pos = (hsv.x / 360.0); + vec3 new_color = texture(to_gradient, vec2((color_pos), 0.5)).rgb; + vec3 new_hsv = rgb2hsv(new_color); + hsv.x = new_hsv.x; + vec3 final_rgb = hsv2rgb(hsv); + + + color.rgb = final_rgb; + } + + + + // Convert from [0.0, 1.0] range to [0, 255] range + ivec3 c = ivec3(round(color * 255.0)); + + // Apply the dithering pattern + if (dithering) { + c += ivec3(dithering_pattern(uv)); + } + + vec3 final_color; + if(change_color_depth){ + // Truncate from 8 bits to color_depth bits + c >>= (8 - target_color_depth); + final_color = vec3(c) / float(1 << target_color_depth); + } else { + final_color = vec3(c) / float(1 << 8); + } + + // Convert back to [0.0, 1.0] range + COLOR.rgb = final_color; +} \ No newline at end of file diff --git a/src/shaders/retro_postprocessing.gdshader b/src/shaders/retro_postprocessing.gdshader new file mode 100644 index 0000000..c9abdef --- /dev/null +++ b/src/shaders/retro_postprocessing.gdshader @@ -0,0 +1,143 @@ +// Retro Post-Processing shader +// From https://godotshaders.com/shader/retro-post-processing/ + +shader_type canvas_item; + +// Handles the resolution changes, color depth, and dithering +group_uniforms resolution_and_colors; +uniform bool change_color_depth = false; +uniform int target_color_depth : hint_range(1, 8) = 5; +uniform bool dithering = false; +uniform bool scale_resolution = false; +uniform int target_resolution_scale = 3; + +// Handles the LUTish recoloring +group_uniforms gradient_recoloring; +uniform bool enable_recolor = false; +uniform sampler2D to_gradient: hint_default_black; + +int dithering_pattern(ivec2 fragcoord) { + const int pattern[] = { + -4, +0, -3, +1, + +2, -2, +3, -1, + -3, +1, -4, +0, + +3, -1, +2, -2 + }; + + int x = fragcoord.x % 4; + int y = fragcoord.y % 4; + + return pattern[y * 4 + x]; +} + +vec3 rgb2hsv(vec3 rgb) { //Converts RGB values to HSV + float r = rgb.r; + float g = rgb.g; + float b = rgb.b; + + float cmax = max(r,max(g,b)); + float cmin = min(r,min(g,b)); + float delta = cmax - cmin; + + float h = 0.f; //hue + + if (delta > 0.f){ + if (cmax == r){ + h = (g-b)/delta; + h = mod(h,6.f); + } else if (cmax == g){ + h = ((b - r) / delta) + 2.f; + } else { + h = ((r-g)/delta) + 4.f; + } + h = h * 60.f; + } + + float s = 0.f; //saturation + if (cmax > 0.f){ + s = delta / cmax; + } + + return vec3(h,s,cmax); // Keep original alpha value + +} + +vec3 hsv2rgb(vec3 hsv) { //Converts HSV values to RGB + float h = hsv.r; + float s = hsv.g; + float v = hsv.b; + float c = v * s; + //X = C × (1 - |(H / 60°) mod 2 - 1|) + float x = h / 60.f; + x = mod(x,2.f); + x = abs(x - 1.f); + x = c * (1.f - x); + + float m = v - c; + + vec3 rgb = vec3(0.f,0.f,0.f); + + if (h < 60.f) { + rgb = vec3(c,x,0.f); + } else if (h < 120.f){ + rgb = vec3(x,c,0.f); + } else if (h < 180.f){ + rgb = vec3(0.f,c,x); + } else if (h < 240.f){ + rgb = vec3(0.f,x,c); + } else if (h < 300.f){ + rgb = vec3(x,0.f,c); + } else if (h < 360.f){ + rgb = vec3(c,0.f,x); + } + rgb[0] = rgb[0] + m; + rgb[1] = rgb[1] + m; + rgb[2] = rgb[2] + m; + + return rgb; +} + +void fragment() { + ivec2 uv; + vec3 color; + + if(scale_resolution){ + uv = ivec2(FRAGCOORD.xy / float(target_resolution_scale)); + color = texelFetch(TEXTURE, uv * target_resolution_scale, 0).rgb; + } else { + uv = ivec2(FRAGCOORD.xy); + color = texelFetch(TEXTURE, uv, 0).rgb; + } + + if(enable_recolor){ + vec3 hsv = rgb2hsv(color); + float color_pos = (hsv.x / 360.0); + vec3 new_color = texture(to_gradient, vec2((color_pos), 0.5)).rgb; + vec3 new_hsv = rgb2hsv(new_color); + hsv.x = new_hsv.x; + vec3 final_rgb = hsv2rgb(hsv); + + color.rgb = final_rgb; + } + + + // Convert from [0.0, 1.0] range to [0, 255] range + ivec3 c = ivec3(round(color * 255.0)); + + // Apply the dithering pattern + if (dithering) { + c += ivec3(dithering_pattern(uv)); + } + + vec3 final_color; + if(change_color_depth){ + // Truncate from 8 bits to color_depth bits + c >>= (8 - target_color_depth); + final_color = vec3(c) / float(1 << target_color_depth); + } else { + final_color = vec3(c) / float(1 << 8); + } + + // Convert back to [0.0, 1.0] range + COLOR.rgb = final_color; +} \ No newline at end of file diff --git a/src/world/world.tscn b/src/world/world.tscn new file mode 100644 index 0000000..42b5f21 --- /dev/null +++ b/src/world/world.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=4 format=3 uid="uid://cwnwcd8kushl3"] + +[ext_resource type="Shader" path="res://src/shaders/retro_postprocessing.gdshader" id="1_3gv54"] +[ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="1_pge3b"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_sc4r2"] +shader = ExtResource("1_3gv54") +shader_parameter/change_color_depth = true +shader_parameter/target_color_depth = 8 +shader_parameter/dithering = true +shader_parameter/scale_resolution = true +shader_parameter/target_resolution_scale = 3 +shader_parameter/enable_recolor = false + +[node name="World" type="Node"] + +[node name="SubViewportContainer" type="SubViewportContainer" parent="."] +material = SubResource("ShaderMaterial_sc4r2") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stretch = true + +[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"] +size = Vector2i(1280, 720) + +[node name="Level" type="Node3D" parent="SubViewportContainer/SubViewport"] + +[node name="TestLevel" parent="SubViewportContainer/SubViewport/Level" instance=ExtResource("1_pge3b")]