Add resolution-independent pixelation

This commit is contained in:
Alexander Sellite 2026-07-27 23:45:11 -04:00
parent 46175ee405
commit 6f51f91324
10 changed files with 220 additions and 33 deletions

View file

@ -30,6 +30,9 @@ const UIPixelationPresenterType = preload(
const PixelationResetOverlayType = preload(
"res://ui/pixelation_reset_overlay.gd"
)
const WorldPixelationPostprocessType = preload(
"res://main/world_pixelation_postprocess.gd"
)
const TITLE_MUSIC_SILENCE_DB: float = -80.0
@ -53,6 +56,9 @@ const TITLE_MUSIC_SILENCE_DB: float = -80.0
@onready var _pixelation_reset: PixelationResetOverlayType = (
%PixelationResetOverlay
)
@onready var _world_pixelation: WorldPixelationPostprocessType = (
%WorldPixelationPostprocess
)
var _gameplay_started: bool = false
var _shop_interaction: FishingShopInteractionType
@ -241,14 +247,7 @@ func _process(_delta: float) -> void:
func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
if settings == null:
return
var root_viewport: Viewport = get_viewport()
root_viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_NEAREST
root_viewport.scaling_3d_scale = PlayerSettingsType.get_world_render_scale(
settings.world_pixel_size
)
root_viewport.msaa_3d = Viewport.MSAA_DISABLED
root_viewport.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
root_viewport.use_taa = false
_apply_world_pixelation(settings.world_pixel_size)
_ui_pixelation.set_pixel_size(settings.ui_pixel_size)
_game_ui.get_title_screen().set_world_pixelation(
settings.world_pixel_size
@ -264,6 +263,16 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
)
func _apply_world_pixelation(pixel_size: int) -> void:
var root_viewport: Viewport = get_viewport()
root_viewport.scaling_3d_mode = Viewport.SCALING_3D_MODE_NEAREST
root_viewport.scaling_3d_scale = 1.0
root_viewport.msaa_3d = Viewport.MSAA_DISABLED
root_viewport.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
root_viewport.use_taa = false
_world_pixelation.set_pixel_size(pixel_size)
func _on_effective_ui_pixel_size_changed(
_requested_pixel_size: int,
effective_pixel_size: int,
@ -281,6 +290,7 @@ func _reset_pixelation() -> void:
func _set_gameplay_active(active: bool) -> void:
_gameplay_started = active
_world_pixelation.set_gameplay_active(active)
_ui_pixelation.set_gameplay_active(active)
if not active:
_player.item_effects.reset_all()

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=16 format=3]
[gd_scene load_steps=17 format=3]
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
@ -15,6 +15,7 @@
[ext_resource type="AudioStream" path="res://audio/music/title/as_in_four_wolves.ogg" id="13_title_music"]
[ext_resource type="Script" path="res://ui/ui_pixelation_presenter.gd" id="14_pixelation"]
[ext_resource type="PackedScene" path="res://ui/pixelation_reset_overlay.tscn" id="15_reset"]
[ext_resource type="PackedScene" path="res://main/world_pixelation_postprocess.tscn" id="16_world_pixelation"]
[node name="Main" type="Node3D"]
script = ExtResource("3_main")
@ -53,6 +54,9 @@ stream = ExtResource("13_title_music")
volume_db = -80.0
bus = &"Music"
[node name="WorldPixelationPostprocess" parent="." instance=ExtResource("16_world_pixelation")]
unique_name_in_owner = true
[node name="UIPresentation" type="SubViewportContainer" parent="."]
unique_name_in_owner = true
texture_filter = 1

View file

@ -0,0 +1,51 @@
class_name WorldPixelationPostprocess
extends CanvasLayer
@onready var _screen_grid: ColorRect = %ScreenGrid
var _pixel_size: int = PlayerSettings.DEFAULT_WORLD_PIXEL_SIZE
var _gameplay_active: bool = false
func _ready() -> void:
get_viewport().size_changed.connect(_refresh_grid)
_refresh_grid()
func set_pixel_size(pixel_size: int) -> void:
_pixel_size = clampi(
pixel_size,
PlayerSettings.MIN_WORLD_PIXEL_SIZE,
PlayerSettings.MAX_WORLD_PIXEL_SIZE
)
_refresh_grid()
func set_gameplay_active(active: bool) -> void:
_gameplay_active = active
_refresh_grid()
func get_grid_size() -> Vector2i:
return PlayerSettings.get_world_grid_size(
_pixel_size,
get_window().size
)
func _refresh_grid() -> void:
if not is_node_ready():
return
var effect_enabled: bool = (
_gameplay_active
and _pixel_size != PlayerSettings.MIN_WORLD_PIXEL_SIZE
)
_screen_grid.visible = effect_enabled
if not effect_enabled:
return
var shader_material := _screen_grid.material as ShaderMaterial
if shader_material != null:
shader_material.set_shader_parameter(
"logical_grid_size",
Vector2(get_grid_size())
)

View file

@ -0,0 +1 @@
uid://c3n47x8egoc8x

View file

@ -0,0 +1,15 @@
shader_type canvas_item;
render_mode unshaded;
uniform sampler2D screen_texture
: hint_screen_texture, filter_nearest, repeat_disable;
uniform vec2 logical_grid_size = vec2(640.0, 360.0);
void fragment() {
vec2 safe_grid = max(logical_grid_size, vec2(1.0));
vec2 snapped_uv = (
floor(SCREEN_UV * safe_grid) + vec2(0.5)
) / safe_grid;
COLOR = texture(screen_texture, snapped_uv);
}

View file

@ -0,0 +1 @@
uid://sq224d8sn1b4

View file

@ -0,0 +1,21 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://main/world_pixelation_postprocess.gd" id="1_script"]
[ext_resource type="Shader" path="res://main/world_pixelation_postprocess.gdshader" id="2_shader"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_world_pixelation"]
shader = ExtResource("2_shader")
[node name="WorldPixelationPostprocess" type="CanvasLayer"]
layer = -1
script = ExtResource("1_script")
[node name="ScreenGrid" type="ColorRect" parent="."]
unique_name_in_owner = true
material = SubResource("ShaderMaterial_world_pixelation")
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2