diff --git a/SuperScaling/SuperScaling.tres b/SuperScaling/Averaging.tres similarity index 77% rename from SuperScaling/SuperScaling.tres rename to SuperScaling/Averaging.tres index 89d816f..1ad689d 100644 --- a/SuperScaling/SuperScaling.tres +++ b/SuperScaling/Averaging.tres @@ -10,7 +10,6 @@ render_mode unshaded; uniform sampler2D viewport; uniform vec2 view_resolution; varying vec2 view_pixel_size; -uniform float smoothness; // texture coords for directional sampling varying vec2 center_pixel; @@ -23,9 +22,6 @@ varying vec2 north_west_pixel; varying vec2 south_east_pixel; varying vec2 south_west_pixel; -// offset coordinates used for super sampling -const vec2 offset_uv = vec2(0.25, 0.75); - // finds the absolute distance from two floats float float_diff(float float_a, float float_b) { return abs(float_a - float_b); @@ -114,44 +110,6 @@ vec4 directional_average() { return result; } -// partial derivative on x-axis -vec2 deriv_x(vec2 pos, vec4 frag, vec2 pixel) { - vec2 offset = vec2(pixel.x, 0.0); - vec2 pos_plus = pos + offset; - vec2 pos_minus = pos - offset; - int coord = int(frag.x) / 2; - bool even = int(coord * 2) == int(frag.x); - return even ? (pos_plus - pos) : (pos - pos_minus); -} - -// partial derivative on y-axis -vec2 deriv_y(vec2 pos, vec4 frag, vec2 pixel) { - vec2 offset = vec2(0.0, pixel.y); - vec2 pos_plus = pos + offset; - vec2 pos_minus = pos - offset; - int coord = int(frag.y) / 2; - bool even = int(coord * 2) == int(frag.y); - return even ? (pos_plus - pos) : (pos - pos_minus); -} - -// take 4 samples in a rotated grid for super sampling -vec4 super_sample(vec2 base_uv, vec4 frag, vec2 pixel) { - vec2 dx = deriv_x(base_uv, frag, pixel); - vec2 dy = deriv_y(base_uv, frag, pixel); - vec2 uv = vec2(0.0); - vec4 color = vec4(0.0); - uv = base_uv + offset_uv.x * dx + offset_uv.y * dy; - color += textureLod(viewport, uv, 0.0); - uv = base_uv - offset_uv.x * dx - offset_uv.y * dy; - color += textureLod(viewport, uv, 0.0); - uv = base_uv + offset_uv.y * dx - offset_uv.x * dy; - color += textureLod(viewport, uv, 0.0); - uv = base_uv - offset_uv.y * dx + offset_uv.x * dy; - color += textureLod(viewport, uv, 0.0); - color *= 0.25; - return color; -} - // precompute the sample direction texture coordinates void vertex() { view_pixel_size = 1.0 / view_resolution; @@ -174,9 +132,7 @@ void vertex() { // draw the new color on the screen void fragment() { - vec4 dir_avg = directional_average(); - vec4 super = super_sample(UV, FRAGCOORD, view_pixel_size); - vec4 result = mix(dir_avg, super, smoothness); + vec4 result = directional_average(); result.a = 1.0; COLOR = result; } diff --git a/SuperScaling/Super.tres b/SuperScaling/Super.tres new file mode 100644 index 0000000..7d49b3c --- /dev/null +++ b/SuperScaling/Super.tres @@ -0,0 +1,65 @@ +[gd_resource type="Shader" format=2] + +[resource] +code = "// [Godot Super Scaling] +// created by Andres Hernandez +shader_type canvas_item; +render_mode unshaded; + +// viewport to sample and its resolution +uniform sampler2D viewport; +uniform vec2 view_resolution; +varying vec2 view_pixel_size; + +// offset coordinates used for super sampling +const vec2 offset_uv = vec2(0.28, 0.56); + +// partial derivative on x-axis +vec2 deriv_x(vec2 pos, vec4 frag, vec2 pixel) { + vec2 offset = vec2(pixel.x, 0.0); + vec2 pos_plus = pos + offset; + vec2 pos_minus = pos - offset; + int coord = int(frag.x) / 2; + bool even = int(coord * 2) == int(frag.x); + return even ? (pos_plus - pos) : (pos - pos_minus); +} + +// partial derivative on y-axis +vec2 deriv_y(vec2 pos, vec4 frag, vec2 pixel) { + vec2 offset = vec2(0.0, pixel.y); + vec2 pos_plus = pos + offset; + vec2 pos_minus = pos - offset; + int coord = int(frag.y) / 2; + bool even = int(coord * 2) == int(frag.y); + return even ? (pos_plus - pos) : (pos - pos_minus); +} + +// take 4 samples in a rotated grid for super sampling +vec4 super_sample(vec2 base_uv, vec4 frag, vec2 pixel) { + vec2 dx = deriv_x(base_uv, frag, pixel); + vec2 dy = deriv_y(base_uv, frag, pixel); + vec2 uv = vec2(0.0); + vec4 color = vec4(0.0); + uv = base_uv + offset_uv.x * dx + offset_uv.y * dy; + color += textureLod(viewport, uv, 0.0); + uv = base_uv - offset_uv.x * dx - offset_uv.y * dy; + color += textureLod(viewport, uv, 0.0); + uv = base_uv + offset_uv.y * dx - offset_uv.x * dy; + color += textureLod(viewport, uv, 0.0); + uv = base_uv - offset_uv.y * dx + offset_uv.x * dy; + color += textureLod(viewport, uv, 0.0); + color *= 0.25; + return color; +} + +// precompute the sample direction texture coordinates +void vertex() { + view_pixel_size = 1.0 / view_resolution; +} + +// draw the new color on the screen +void fragment() { + vec4 result = super_sample(UV, FRAGCOORD, view_pixel_size); + result.a = 1.0; + COLOR = result; +}" diff --git a/SuperScaling/SuperScaling.gd b/SuperScaling/SuperScaling.gd index e8490dd..51928a9 100644 --- a/SuperScaling/SuperScaling.gd +++ b/SuperScaling/SuperScaling.gd @@ -3,18 +3,21 @@ extends Node export (float, 0.1, 2.0) var scale_factor = 1.0 setget change_scale_factor -export (float, 0.0, 1.0) var smoothness = 0.5 setget change_smoothness export (bool) var enable_on_play = false export (NodePath) var game_world export (int, "3D", "2D") var usage = 0 export (int, "Disabled", "2X", "4X", "8X", "16X") var msaa = 0 setget change_msaa export (bool) var fxaa = false setget change_fxaa export (int, 1, 4096) var shadow_atlas = 4096 setget change_shadow_atlas -onready var sampler_shader = load(get_script().resource_path.get_base_dir() + "/SuperScaling.tres") -var sampler_material +onready var averaging_shader = load(get_script().resource_path.get_base_dir() + "/Averaging.tres") +onready var super_shader = load(get_script().resource_path.get_base_dir() + "/Super.tres") +var averaging_material +var super_material var game_node -var overlay +var averaging_overlay +var super_overlay var viewport +var super_viewport var viewport_size var root_viewport var native_resolution @@ -30,10 +33,14 @@ func _ready(): if game_node: get_parent().call_deferred("remove_child", game_node) get_screen_size() + create_sampler() + create_super() create_viewport() + create_super_viewport() set_shader_texture() viewport.call_deferred("add_child", game_node) - get_parent().call_deferred("add_child", viewport) + super_viewport.call_deferred("add_child", viewport) + get_parent().call_deferred("add_child", super_viewport) original_resolution = native_resolution original_aspect_ratio = native_aspect_ratio root_viewport = get_viewport() @@ -42,10 +49,8 @@ func _ready(): #warning-ignore:RETURN_VALUE_DISCARDED root_viewport.connect("size_changed", self, "on_window_resize") on_window_resize() - create_sampler() change_msaa(msaa) change_fxaa(fxaa) - change_smoothness(smoothness) set_process_input(false) set_process_unhandled_input(false) else: @@ -63,27 +68,51 @@ func create_viewport(): viewport.msaa = Viewport.MSAA_DISABLED viewport.shadow_atlas_size = shadow_atlas +func create_super_viewport(): + super_viewport = Viewport.new() + super_viewport.name = "SuperViewport" + super_viewport.size = native_resolution + super_viewport.usage = Viewport.USAGE_3D if usage == USAGE_3D else Viewport.USAGE_2D + super_viewport.render_target_clear_mode = Viewport.CLEAR_MODE_NEVER + super_viewport.render_target_update_mode = Viewport.UPDATE_ALWAYS + super_viewport.render_target_v_flip = true + super_viewport.size_override_stretch = true + super_viewport.msaa = Viewport.MSAA_DISABLED + super_viewport.shadow_atlas_size = shadow_atlas + func create_sampler(): - overlay = ColorRect.new() - overlay.name = "SamplerOverlay" - sampler_material = ShaderMaterial.new() - sampler_material.shader = sampler_shader - overlay.material = sampler_material - add_child(overlay) + averaging_overlay = ColorRect.new() + averaging_overlay.name = "AveragingOverlay" + averaging_material = ShaderMaterial.new() + averaging_material.shader = averaging_shader + averaging_overlay.material = averaging_material + averaging_overlay.visible = false + add_child(averaging_overlay) + +func create_super(): + super_overlay = ColorRect.new() + super_overlay.name = "SuperOverlay" + super_material = ShaderMaterial.new() + super_material.shader = super_shader + super_overlay.material = super_material + add_child(super_overlay) func set_shader_texture(): yield(VisualServer, "frame_post_draw") var view_texture = viewport.get_texture() view_texture.flags = 0 view_texture.viewport_path = viewport.get_path() - sampler_material.set_shader_param("viewport", view_texture) + averaging_material.set_shader_param("viewport", view_texture) + super_material.set_shader_param("viewport", view_texture) change_scale_factor(scale_factor) set_process_input(true) set_process_unhandled_input(true) func set_shader_resolution(): - if sampler_material: - sampler_material.set_shader_param("view_resolution", viewport_size) + if averaging_material: + averaging_material.set_shader_param("view_resolution", viewport_size) + if super_material: + super_material.set_shader_param("view_resolution", viewport_size) func get_screen_size(): var window = OS.window_size @@ -110,6 +139,8 @@ func set_viewport_size(): func resize_viewport(): if viewport: viewport.size = viewport_size + if super_viewport: + super_viewport.size = viewport_size func scale_viewport_canvas(): if viewport: @@ -132,26 +163,27 @@ func scale_viewport_canvas(): viewport.set_size_override(true, Vector2(round(original_resolution.x * aspect_diff), round(original_resolution.y))) elif aspect_diff < 1.0 - epsilon: viewport.set_size_override(true, Vector2(round(original_resolution.x), round(original_resolution.y / aspect_diff))) + super_viewport.set_size_override(true, viewport.size) func set_sampler_size(): - if overlay: + if averaging_overlay: var stretch_setting = get_stretch_setting() var aspect_setting = get_aspect_setting() var aspect_diff = native_aspect_ratio / original_aspect_ratio if usage == USAGE_2D: if aspect_diff < 1.0 - epsilon and aspect_setting == "keep_width": - overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.x / native_aspect_ratio)) + averaging_overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.x / native_aspect_ratio)) elif aspect_diff > 1.0 + epsilon and aspect_setting == "keep_height": - overlay.rect_size = Vector2(round(original_resolution.y * native_aspect_ratio), round(original_resolution.y)) + averaging_overlay.rect_size = Vector2(round(original_resolution.y * native_aspect_ratio), round(original_resolution.y)) else: - overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.y)) + averaging_overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.y)) elif usage == USAGE_3D: - overlay.rect_size = Vector2(round(native_resolution.x), round(native_resolution.y)) + averaging_overlay.rect_size = Vector2(round(native_resolution.x), round(native_resolution.y)) if aspect_diff > 1.0 + epsilon: - overlay.rect_size.x = round(native_resolution.y * original_aspect_ratio) + averaging_overlay.rect_size.x = round(native_resolution.y * original_aspect_ratio) elif aspect_diff < 1.0 - epsilon: - overlay.rect_size.y = round(native_resolution.x / original_aspect_ratio) - var overlay_size = overlay.rect_size + averaging_overlay.rect_size.y = round(native_resolution.x / original_aspect_ratio) + var overlay_size = averaging_overlay.rect_size var screen_size = Vector2(0.0, 0.0) if usage == USAGE_2D: screen_size = original_resolution @@ -159,57 +191,54 @@ func set_sampler_size(): screen_size = native_resolution if stretch_setting == "disabled" or usage == USAGE_2D: if aspect_setting == "keep": - overlay.rect_position.x = 0 - overlay.rect_position.y = 0 + averaging_overlay.rect_position.x = 0 + averaging_overlay.rect_position.y = 0 elif aspect_setting == "keep_width" or aspect_setting == "keep_height": - overlay.rect_position.x = 0 - overlay.rect_position.y = 0 + averaging_overlay.rect_position.x = 0 + averaging_overlay.rect_position.y = 0 if usage == USAGE_3D: if aspect_diff > 1.0 + epsilon: - overlay.rect_position.x = round((screen_size.x * aspect_diff - overlay_size.x) * 0.5) + averaging_overlay.rect_position.x = round((screen_size.x * aspect_diff - overlay_size.x) * 0.5) elif aspect_diff < 1.0 - epsilon: - overlay.rect_position.y = round((screen_size.y / aspect_diff - overlay_size.y) * 0.5) + averaging_overlay.rect_position.y = round((screen_size.y / aspect_diff - overlay_size.y) * 0.5) elif aspect_setting == "expand": if usage == USAGE_3D: - overlay.rect_size = screen_size + averaging_overlay.rect_size = screen_size elif aspect_diff > 1.0 + epsilon: - overlay.rect_size = Vector2(round(screen_size.x * aspect_diff), round(screen_size.y)) + averaging_overlay.rect_size = Vector2(round(screen_size.x * aspect_diff), round(screen_size.y)) elif aspect_diff < 1.0 - epsilon: - overlay.rect_size = Vector2(round(screen_size.x), round(screen_size.y / aspect_diff)) + averaging_overlay.rect_size = Vector2(round(screen_size.x), round(screen_size.y / aspect_diff)) else: - overlay.rect_size = screen_size + averaging_overlay.rect_size = screen_size elif aspect_setting == "ignore": if usage == USAGE_3D: - overlay.rect_size = screen_size + averaging_overlay.rect_size = screen_size elif stretch_setting == "viewport": - overlay.rect_size = native_resolution + averaging_overlay.rect_size = native_resolution elif stretch_setting == "2d": - overlay.rect_size = original_resolution - overlay_size = overlay.rect_size - overlay.rect_position.x = 0 - overlay.rect_position.y = 0 + averaging_overlay.rect_size = original_resolution + overlay_size = averaging_overlay.rect_size + averaging_overlay.rect_position.x = 0 + averaging_overlay.rect_position.y = 0 if aspect_setting == "expand": if aspect_diff > 1.0 + epsilon: - overlay.rect_size = Vector2(round(original_resolution.y * native_aspect_ratio), round(original_resolution.y)) + averaging_overlay.rect_size = Vector2(round(original_resolution.y * native_aspect_ratio), round(original_resolution.y)) elif aspect_diff < 1.0 - epsilon: - overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.x / native_aspect_ratio)) + averaging_overlay.rect_size = Vector2(round(original_resolution.x), round(original_resolution.x / native_aspect_ratio)) elif aspect_setting == "keep_width": - overlay.rect_position.x = 0.0 + averaging_overlay.rect_position.x = 0.0 if aspect_diff < 1.0 - epsilon: - overlay.rect_position.y = round((overlay_size.y / aspect_diff - overlay_size.y) * 0.5) + averaging_overlay.rect_position.y = round((overlay_size.y / aspect_diff - overlay_size.y) * 0.5) elif aspect_setting == "keep_height": - overlay.rect_position.y = 0.0 + averaging_overlay.rect_position.y = 0.0 if aspect_diff > 1.0 + epsilon: - overlay.rect_position.x = round((overlay_size.x * aspect_diff - overlay_size.x) * 0.5) + averaging_overlay.rect_position.x = round((overlay_size.x * aspect_diff - overlay_size.x) * 0.5) + super_overlay.rect_size = averaging_overlay.rect_size + super_overlay.rect_position = averaging_overlay.rect_position func change_scale_factor(val): scale_factor = val on_window_resize() - -func change_smoothness(val): - smoothness = val - if sampler_material: - sampler_material.set_shader_param("smoothness", smoothness) func change_msaa(val): msaa = val