Sfoglia il codice sorgente

Fix signed distance field font rendering

This fix works in both GLES3 and GLES2.

The rendering formula in the shader was adjusted to further improve the
sharpness/antialiasing quality balance.

Co-authored-by: Hugo Locurcio <[email protected]>
lawnjelly 1 anno fa
parent
commit
bc607fb607

+ 1 - 0
drivers/gles2/rasterizer_canvas_base_gles2.cpp

@@ -58,6 +58,7 @@ void RasterizerCanvasBaseGLES2::canvas_begin() {
 	state.using_large_vertex = false;
 	state.using_modulate = false;
 
+	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false);

+ 19 - 0
drivers/gles2/rasterizer_canvas_gles2.cpp

@@ -1225,6 +1225,7 @@ void RasterizerCanvasGLES2::canvas_render_items_implementation(Item *p_item_list
 	ris.item_group_modulate = p_modulate;
 	ris.item_group_light = p_light;
 	ris.item_group_base_transform = p_base_transform;
+	ris.prev_distance_field = false;
 
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, false);
 
@@ -1558,6 +1559,12 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 		join = false;
 	}
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		r_ris.prev_distance_field = p_ci->distance_field;
+		join = false;
+		r_batch_break = true;
+	}
+
 	// non rects will break the batching anyway, we don't want to record item changes, detect this
 	if (!r_batch_break && _detect_item_batch_break(r_ris, p_ci, r_batch_break)) {
 		join = false;
@@ -1573,6 +1580,12 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 void RasterizerCanvasGLES2::_legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris) {
 	storage->info.render._2d_item_count++;
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, p_ci->distance_field);
+		r_ris.prev_distance_field = p_ci->distance_field;
+		r_ris.rebind_shader = true;
+	}
+
 	if (r_ris.current_clip != p_ci->final_clip_owner) {
 		r_ris.current_clip = p_ci->final_clip_owner;
 
@@ -1936,6 +1949,12 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
 	// all the joined items will share the same state with the first item
 	Item *ci = bdata.item_refs[p_bij.first_item_ref].item;
 
+	if (r_ris.prev_distance_field != ci->distance_field) {
+		state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, ci->distance_field);
+		r_ris.prev_distance_field = ci->distance_field;
+		r_ris.rebind_shader = true;
+	}
+
 	if (r_ris.current_clip != ci->final_clip_owner) {
 		r_ris.current_clip = ci->final_clip_owner;
 

+ 9 - 1
drivers/gles2/shaders/canvas.glsl

@@ -443,10 +443,18 @@ void main() {
 	uv = mod(uv, vec2(1.0, 1.0));
 #endif
 
+#ifdef USE_DISTANCE_FIELD
+	// Higher is smoother, but also more blurry. Lower is crisper, but also more aliased.
+	// TODO: Adjust automatically based on screen resolution/font size ratio.
+	const float smoothing = 0.125;
+	float dist = texture2D(color_texture, uv).a;
+	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
+#else
 #if !defined(COLOR_USED)
-	//default behavior, texture by color
+	// Default behavior, texture by color.
 	color *= texture2D(color_texture, uv);
 #endif
+#endif
 
 #ifdef SCREEN_UV_USED
 	vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;

+ 1 - 0
drivers/gles3/rasterizer_canvas_base_gles3.cpp

@@ -165,6 +165,7 @@ void RasterizerCanvasBaseGLES3::canvas_begin() {
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
 
+	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LIGHT_ANGLE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_MODULATE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LARGE_VERTEX, false);

+ 6 - 0
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -1878,6 +1878,12 @@ bool RasterizerCanvasGLES3::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 		join = false;
 	}
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		r_ris.prev_distance_field = p_ci->distance_field;
+		join = false;
+		r_batch_break = true;
+	}
+
 	// non rects will break the batching anyway, we don't want to record item changes, detect this
 	if (!r_batch_break && _detect_item_batch_break(r_ris, p_ci, r_batch_break)) {
 		join = false;

+ 7 - 8
drivers/gles3/shaders/canvas.glsl

@@ -581,18 +581,17 @@ void main() {
 
 #endif
 
-#if !defined(COLOR_USED)
-	//default behavior, texture by color
-
 #ifdef USE_DISTANCE_FIELD
-	const float smoothing = 1.0 / 32.0;
-	float distance = textureLod(color_texture, uv, 0.0).a;
-	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance) * color.a;
+	// Higher is smoother, but also more blurry. Lower is crisper, but also more aliased.
+	// TODO: Adjust automatically based on screen resolution/font size ratio.
+	const float smoothing = 0.125;
+	float dist = texture(color_texture, uv, 0.0).a;
+	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
 #else
+#if !defined(COLOR_USED)
+	// Default behavior, texture by color.
 	color *= texture(color_texture, uv);
-
 #endif
-
 #endif
 
 	vec3 normal;

+ 12 - 0
scene/2d/canvas_item.cpp

@@ -955,6 +955,17 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
 	VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid);
 }
 
+void CanvasItem::select_font(const Ref<Font> &p_font) {
+	// Purely to keep canvas item SDF state up to date for now.
+	bool new_font_sdf_selected = p_font.is_valid() && p_font->is_distance_field_hint();
+
+	if (font_sdf_selected != new_font_sdf_selected) {
+		ERR_FAIL_COND(!get_canvas_item().is_valid());
+		font_sdf_selected = new_font_sdf_selected;
+		VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(), font_sdf_selected);
+	}
+}
+
 void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) {
 	ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
 
@@ -1351,6 +1362,7 @@ CanvasItem::CanvasItem() :
 	global_invalid = true;
 	notify_local_transform = false;
 	notify_transform = false;
+	font_sdf_selected = false;
 	light_mask = 1;
 
 	C = nullptr;

+ 13 - 11
scene/2d/canvas_item.h

@@ -191,21 +191,22 @@ private:
 
 	int light_mask;
 
-	bool first_draw;
-	bool visible;
-	bool pending_update;
-	bool toplevel;
-	bool drawing;
-	bool block_transform_notify;
-	bool behind;
-	bool use_parent_material;
-	bool notify_local_transform;
-	bool notify_transform;
+	bool first_draw : 1;
+	bool visible : 1;
+	bool pending_update : 1;
+	bool toplevel : 1;
+	bool drawing : 1;
+	bool block_transform_notify : 1;
+	bool behind : 1;
+	bool use_parent_material : 1;
+	bool notify_local_transform : 1;
+	bool notify_transform : 1;
+	bool font_sdf_selected : 1;
+	mutable bool global_invalid : 1;
 
 	Ref<Material> material;
 
 	mutable Transform2D global_transform;
-	mutable bool global_invalid;
 
 	void _toplevel_raise_self();
 	void _toplevel_visibility_changed(bool p_visible);
@@ -335,6 +336,7 @@ public:
 	void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
 	void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
 
+	void select_font(const Ref<Font> &p_font);
 	void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);
 	float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1));
 

+ 1 - 0
scene/gui/button.cpp

@@ -286,6 +286,7 @@ void Button::_notification(int p_what) {
 
 				text_ofs.y += font->get_ascent();
 				text_ofs.y += line_height * (((float)i) - (((float)(num_lines - 1)) / 2.0));
+				select_font(font);
 				font->draw(ci, text_ofs.floor(), line_text, color, clip_text ? text_clip : -1);
 			}
 		} break;

+ 1 - 0
scene/gui/dialogs.cpp

@@ -206,6 +206,7 @@ void WindowDialog::_notification(int p_what) {
 			int font_height = title_font->get_height() - title_font->get_descent() * 2;
 			int x = (size.x - title_font->get_string_size(xl_title).x) / 2;
 			int y = (-title_height + font_height) / 2;
+			select_font(title_font);
 			title_font->draw(canvas, Point2(x, y), xl_title, title_color, size.x - panel->get_minimum_size().x);
 		} break;
 

+ 1 - 0
scene/gui/item_list.cpp

@@ -767,6 +767,7 @@ void ItemList::_notification(int p_what) {
 		Ref<StyleBox> cursor = has_focus() ? get_stylebox("cursor") : get_stylebox("cursor_unfocused");
 
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color guide_color = get_color("guide_color");
 		Color font_color = get_color("font_color");
 		Color font_color_selected = get_color("font_color_selected");

+ 1 - 2
scene/gui/label.cpp

@@ -90,6 +90,7 @@ void Label::_notification(int p_what) {
 		Size2 size = get_size();
 		Ref<StyleBox> style = get_stylebox("normal");
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color font_color = get_color("font_color");
 		Color font_color_shadow = get_color("font_color_shadow");
 		bool use_outline = get_constant("shadow_as_outline");
@@ -99,8 +100,6 @@ void Label::_notification(int p_what) {
 
 		style->draw(ci, Rect2(Point2(0, 0), get_size()));
 
-		VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(), font.is_valid() && font->is_distance_field_hint());
-
 		int font_h = font->get_height() + line_spacing;
 
 		int lines_visible = (size.y + line_spacing) / font_h;

+ 1 - 0
scene/gui/line_edit.cpp

@@ -813,6 +813,7 @@ void LineEdit::_notification(int p_what) {
 			}
 
 			Ref<Font> font = get_font("font");
+			select_font(font);
 
 			style->draw(ci, Rect2(Point2(), size));
 

+ 1 - 0
scene/gui/link_button.cpp

@@ -128,6 +128,7 @@ void LinkButton::_notification(int p_what) {
 			}
 
 			Ref<Font> font = get_font("font");
+			select_font(font);
 
 			draw_string(font, Vector2(0, font->get_ascent()), xl_text, color);
 

+ 2 - 0
scene/gui/popup_menu.cpp

@@ -466,6 +466,8 @@ void PopupMenu::_draw_items() {
 	Ref<StyleBox> style = get_stylebox("panel");
 	Ref<StyleBox> hover = get_stylebox("hover");
 	Ref<Font> font = get_font("font");
+	select_font(font);
+
 	// In Item::checkable_type enum order (less the non-checkable member)
 	Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") };
 	Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };

+ 1 - 0
scene/gui/progress_bar.cpp

@@ -52,6 +52,7 @@ void ProgressBar::_notification(int p_what) {
 		Ref<StyleBox> bg = get_stylebox("bg");
 		Ref<StyleBox> fg = get_stylebox("fg");
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color font_color = get_color("font_color");
 
 		draw_style_box(bg, Rect2(Point2(), get_size()));

+ 1 - 0
scene/gui/rich_text_label.cpp

@@ -1077,6 +1077,7 @@ void RichTextLabel::_notification(int p_what) {
 			}
 			int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
 			Ref<Font> base_font = get_font("normal_font");
+			select_font(base_font);
 			Color base_color = get_color("default_color");
 			Color font_color_shadow = get_color("font_color_shadow");
 			bool use_outline = get_constant("shadow_as_outline");

+ 1 - 0
scene/gui/tab_container.cpp

@@ -279,6 +279,7 @@ void TabContainer::_notification(int p_what) {
 			Ref<Texture> menu = get_icon("menu");
 			Ref<Texture> menu_hl = get_icon("menu_highlight");
 			Ref<Font> font = get_font("font");
+			select_font(font);
 			Color font_color_fg = get_color("font_color_fg");
 			Color font_color_bg = get_color("font_color_bg");
 			Color font_color_disabled = get_color("font_color_disabled");

+ 1 - 0
scene/gui/tabs.cpp

@@ -243,6 +243,7 @@ void Tabs::_notification(int p_what) {
 			Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
 			Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
 			Ref<Font> font = get_font("font");
+			select_font(font);
 			Color color_fg = get_color("font_color_fg");
 			Color color_bg = get_color("font_color_bg");
 			Color color_disabled = get_color("font_color_disabled");