diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 9f64cbf6103a44..173e8a29768716 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -4510,7 +4510,11 @@
Use AMD FidelityFX Super Resolution 2.2 upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using FSR2. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use FSR2 at native resolution as a TAA solution.
-
+
+ Use nearest-neighbor filtering for the viewport's 3D buffer. This looks crisper than [constant VIEWPORT_SCALING_3D_MODE_BILINEAR] and has no additional rendering cost. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] disables scaling.
+ [b]Note:[/b] To avoid uneven pixel scaling, it's highly recommended to use a value equal to an integer divisor of 1, such as [code]1.0 / 2 = 0.5[/code]. For example, it's best to use a scale of [code]0.5[/code] (2×2), [code]0.3333[/code] (3×3), [code]0.25[/code] (4×4), [code]0.2[/code] (5×5) and so on.
+
+
Represents the size of the [enum ViewportScaling3DMode] enum.
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 1b5f7148ac219d..6b4c25762aa980 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -446,7 +446,11 @@
Use AMD FidelityFX Super Resolution 2.2 upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using FSR2. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use FSR2 at native resolution as a TAA solution.
-
+
+ Use nearest-neighbor filtering for the viewport's 3D buffer. This looks crisper than [constant SCALING_3D_MODE_BILINEAR] and has no additional rendering cost. The amount of scaling can be set using [member scaling_3d_scale]. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] disables scaling.
+ [b]Note:[/b] To avoid uneven pixel scaling, it's highly recommended to use a value equal to an integer divisor of 1, such as [code]1.0 / 2 = 0.5[/code]. For example, it's best to use a scale of [code]0.5[/code] (2×2), [code]0.3333[/code] (3×3), [code]0.25[/code] (4×4), [code]0.2[/code] (5×5) and so on.
+
+
Represents the size of the [enum Scaling3DMode] enum.
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 3a4fe9a059588e..2a0cdcaace01be 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -4505,10 +4505,10 @@ void Viewport::_bind_methods() {
#ifndef _3D_DISABLED
ADD_GROUP("Scaling 3D", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow),Nearest (Fastest)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.1,2.0,0.0001"), "set_scaling_3d_scale", "get_scaling_3d_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_fsr_sharpness", "get_fsr_sharpness");
#endif
ADD_GROUP("Variable Rate Shading", "vrs_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode");
@@ -4557,6 +4557,7 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR2);
+ BIND_ENUM_CONSTANT(SCALING_3D_MODE_NEAREST);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);
BIND_ENUM_CONSTANT(MSAA_DISABLED);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index a32077a489e85f..dcd15da2e950b8 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -99,6 +99,7 @@ class Viewport : public Node {
SCALING_3D_MODE_BILINEAR,
SCALING_3D_MODE_FSR,
SCALING_3D_MODE_FSR2,
+ SCALING_3D_MODE_NEAREST,
SCALING_3D_MODE_MAX
};
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
index 48c651140891e3..82a5948cb71574 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -81,7 +81,7 @@ ToneMapper::~ToneMapper() {
tonemap.shader.version_free(tonemap.shader_version);
}
-void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings) {
+void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings, RS::CanvasItemTextureFilter p_filter_mode) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -137,7 +137,8 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- RD::Uniform u_source_color(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_source_color }));
+ RID viewport_scale_sampler = material_storage->sampler_rd_get_default(p_filter_mode, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ RD::Uniform u_source_color(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ viewport_scale_sampler, p_source_color }));
RD::Uniform u_exposure_texture;
u_exposure_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
@@ -178,7 +179,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
RD::get_singleton()->draw_list_end();
}
-void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings) {
+void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings, RS::CanvasItemTextureFilter p_filter_mode) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -216,9 +217,11 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ RID viewport_scale_sampler = material_storage->sampler_rd_get_default(p_filter_mode, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_color;
u_source_color.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT;
u_source_color.binding = 0;
+ u_source_color.append_id(viewport_scale_sampler);
u_source_color.append_id(p_source_color);
RD::Uniform u_exposure_texture;
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index a1a99f931fc32a..27fe734a9b035d 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -150,8 +150,8 @@ class ToneMapper {
bool convert_to_srgb = false;
};
- void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
- void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings);
+ void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings, RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
+ void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings, RS::CanvasItemTextureFilter p_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 7696bddbca8511..ba350016922976 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -557,6 +557,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target);
RID dest_fb;
+ RS::CanvasItemTextureFilter filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
bool use_intermediate_fb = use_fsr;
if (use_intermediate_fb) {
// If we use FSR to upscale we need to write our result into an intermediate buffer.
@@ -568,9 +569,14 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
// Target size in this case is lying as we never get our real target size communicated.
// Bit nasty but...
dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
+
+ if (rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_NEAREST) {
+ // Make the hardware perform nearest-neighbor filtering when scaling the viewport 3D buffer.
+ filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST;
+ }
}
- tone_mapper->tonemapper(color_texture, dest_fb, tonemap);
+ tone_mapper->tonemapper(color_texture, dest_fb, tonemap, filter_mode);
RD::get_singleton()->draw_command_end_label();
}
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 09737d03a024f9..5f1fc35e4645d0 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -121,8 +121,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
bool scaling_3d_is_fsr = (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) || (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2);
bool use_taa = p_viewport->use_taa;
- if (scaling_3d_is_fsr && (scaling_3d_scale > 1.0)) {
- // FSR is not designed for downsampling.
+ if ((scaling_3d_is_fsr || scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_NEAREST) && (scaling_3d_scale > 1.0)) {
+ // FSR and nearest-neighbor scaling are not designed for downsampling.
// Fall back to bilinear scaling.
WARN_PRINT_ONCE("FSR 3D resolution scaling is not designed for downsampling. Falling back to bilinear 3D resolution scaling.");
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
@@ -150,6 +150,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
switch (scaling_3d_mode) {
case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR:
+ case RS::VIEWPORT_SCALING_3D_MODE_NEAREST:
// Clamp 3D rendering resolution to reasonable values supported on most hardware.
// This prevents freezing the engine or outright crashing on lower-end GPUs.
width = CLAMP(p_viewport->size.width * scaling_3d_scale, 1, 16384);
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index b7c40600cb3ad5..86d90524d99f23 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2253,6 +2253,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_BILINEAR);
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_FSR);
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_FSR2);
+ BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_NEAREST);
BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_MAX);
BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_DISABLED);
@@ -2961,9 +2962,9 @@ void RenderingServer::init() {
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"), 0.25);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.18);
- GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow)"), 0);
- GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), 1.0);
- GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), 0.2f);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow),Nearest (Fastest)"), 0);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/scale", PROPERTY_HINT_RANGE, "0.1,2.0,0.0001"), 1.0);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.2f);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/textures/default_filters/texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), 0.0f);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/textures/decals/filter", PROPERTY_HINT_ENUM, "Nearest (Fast),Linear (Fast),Nearest Mipmap (Fast),Linear Mipmap (Fast),Nearest Mipmap Anisotropic (Average),Linear Mipmap Anisotropic (Average)"), DECAL_FILTER_LINEAR_MIPMAPS);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 6b2ba562ce9bf6..edf90f1fed8fb0 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -808,6 +808,7 @@ class RenderingServer : public Object {
VIEWPORT_SCALING_3D_MODE_BILINEAR,
VIEWPORT_SCALING_3D_MODE_FSR,
VIEWPORT_SCALING_3D_MODE_FSR2,
+ VIEWPORT_SCALING_3D_MODE_NEAREST,
VIEWPORT_SCALING_3D_MODE_MAX,
VIEWPORT_SCALING_3D_MODE_OFF = 255, // for internal use only
};