diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 1fdbe5ce4c1b..4d6516aeadcb 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -150,6 +150,13 @@ Determines when depth rendering takes place. See [enum DepthDrawMode]. See also [member transparency]. + + If [code]true[/code], offsets the per-pixel depth by the amount specified in [member depth_offset_amount]. This can be used to force the material to render in front of (or behind) other meshes that are at the exact same position, which can prevent Z-fighting artifacts. This can be useful for mesh-based decals, or when rendering meshes as overlays to other meshes. + [b]Note:[/b] Enabling depth offset forces the material to go through the transparent pipeline, which has a performance cost and can result in transparency sorting issues. + + + The depth offset to apply when [member depth_offset] is [code]true[/code]. Negative values will push the pixels towards the camera, while positive values will push the pixels away from the camera. Typical values are between [code]-0.01[/code] and [code]0.01[/code]. If you still notice Z-fighting after adjusting the depth offset, try using a lower value (if negative) or a higher value (if positive). + Texture that specifies the color of the detail overlay. [member detail_albedo]'s alpha channel is used as a mask, even when the material is opaque. To use a dedicated texture as a mask, see [member detail_mask]. [b]Note:[/b] [member detail_albedo] is [i]not[/i] modulated by [member albedo_color]. diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index b92abbcf79c0..fb3b0ddaddf8 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -78,6 +78,11 @@ void CollisionShape3DGizmoPlugin::create_collision_material(const String &p_name material->set_albedo(color); material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + + // Prevent collision shape triangles that overlap with geometry from Z-fighting. + material->set_depth_offset_enabled(true); + material->set_depth_offset(-0.005); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); material->set_cull_mode(StandardMaterial3D::CULL_BACK); material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index a1ee7b85dd53..c459ba44440e 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -147,6 +147,11 @@ Ref Shape3D::get_debug_collision_material() { material->set_albedo(Color(1.0, 1.0, 1.0)); material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + + // Prevent collision shape triangles that overlap with geometry from Z-fighting. + material->set_depth_offset_enabled(true); + material->set_depth_offset(-0.005); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); material->set_cull_mode(StandardMaterial3D::CULL_BACK); material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 77ac0569ff35..9a909024ec25 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -620,6 +620,7 @@ void BaseMaterial3D::init_shaders() { shader_names->heightmap_flip = "heightmap_flip"; shader_names->grow = "grow"; + shader_names->depth_offset = "depth_offset"; shader_names->ao_light_affect = "ao_light_affect"; @@ -894,6 +895,10 @@ uniform sampler2D texture_albedo : source_color, %s; code += "uniform float grow : hint_range(-16.0, 16.0, 0.001);\n"; } + if (depth_offset_enabled) { + code += "uniform float depth_offset : hint_range(-10.0, 10.0, 0.001);\n"; + } + if (proximity_fade_enabled) { code += "uniform float proximity_fade_distance : hint_range(0.0, 4096.0, 0.01);\n"; } @@ -1404,6 +1409,21 @@ void fragment() {)"; )"; } + if (depth_offset_enabled) { + code += R"( + // Depth Offset: Enabled + // Force transparency on the material (required for depth offset). + ALPHA = 1.0; + vec4 view_position = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2. - 1.0, FRAGCOORD.z, 1.0); + vec3 view_direction = normalize(VIEW); + view_position.xyz /= view_position.w; + view_position.xyz -= depth_offset * view_direction; + vec4 ndc_position = PROJECTION_MATRIX * vec4(view_position.xyz, 1.0); + ndc_position.xyz /= ndc_position.w; + DEPTH = ndc_position.z; +)"; + } + if (features[FEATURE_HEIGHT_MAPPING] && flags[FLAG_UV1_USE_TRIPLANAR]) { // Display both resource name and albedo texture name. // Materials are often built-in to scenes, so displaying the resource name alone may not be meaningful. @@ -2414,6 +2434,10 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { _validate_feature("refraction", FEATURE_REFRACTION, p_property); _validate_feature("detail", FEATURE_DETAIL, p_property); + if (p_property.name == "depth_offset_amount" && !depth_offset_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + if (p_property.name == "emission_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -2743,6 +2767,25 @@ float BaseMaterial3D::get_alpha_antialiasing_edge() const { return alpha_antialiasing_edge; } +void BaseMaterial3D::set_depth_offset_enabled(bool p_enabled) { + depth_offset_enabled = p_enabled; + _queue_shader_change(); + notify_property_list_changed(); +} + +bool BaseMaterial3D::is_depth_offset_enabled() const { + return depth_offset_enabled; +} + +void BaseMaterial3D::set_depth_offset(float p_offset) { + depth_offset = p_offset; + _material_set_param(shader_names->depth_offset, p_offset); +} + +float BaseMaterial3D::get_depth_offset() const { + return depth_offset; +} + void BaseMaterial3D::set_grow(float p_grow) { grow = p_grow; _material_set_param(shader_names->grow, p_grow); @@ -2964,6 +3007,12 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &BaseMaterial3D::set_alpha_antialiasing_edge); ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &BaseMaterial3D::get_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("set_depth_offset_enabled", "enabled"), &BaseMaterial3D::set_depth_offset_enabled); + ClassDB::bind_method(D_METHOD("is_depth_offset_enabled"), &BaseMaterial3D::is_depth_offset_enabled); + + ClassDB::bind_method(D_METHOD("set_depth_offset", "offset"), &BaseMaterial3D::set_depth_offset); + ClassDB::bind_method(D_METHOD("get_depth_offset"), &BaseMaterial3D::get_depth_offset); + ClassDB::bind_method(D_METHOD("set_shading_mode", "shading_mode"), &BaseMaterial3D::set_shading_mode); ClassDB::bind_method(D_METHOD("get_shading_mode"), &BaseMaterial3D::get_shading_mode); @@ -3166,6 +3215,8 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "depth_offset"), "set_depth_offset_enabled", "is_depth_offset_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_offset_amount", PROPERTY_HINT_RANGE, "-10,10,0.001,or_less,or_greater"), "set_depth_offset", "get_depth_offset"); ADD_GROUP("Shading", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode"); diff --git a/scene/resources/material.h b/scene/resources/material.h index 36d325bd33f8..5331cdd811d0 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -339,6 +339,7 @@ class BaseMaterial3D : public Material { uint64_t invalid_key : 1; uint64_t deep_parallax : 1; uint64_t grow : 1; + uint64_t depth_offset : 1; uint64_t proximity_fade : 1; uint64_t orm : 1; @@ -389,6 +390,7 @@ class BaseMaterial3D : public Material { mk.billboard_mode = billboard_mode; mk.deep_parallax = deep_parallax; mk.grow = grow_enabled; + mk.depth_offset = depth_offset_enabled; mk.proximity_fade = proximity_fade_enabled; mk.distance_fade = distance_fade; mk.emission_op = emission_op; @@ -444,6 +446,7 @@ class BaseMaterial3D : public Material { StringName uv1_blend_sharpness; StringName uv2_blend_sharpness; StringName grow; + StringName depth_offset; StringName proximity_fade_distance; StringName msdf_pixel_range; StringName msdf_outline_size; @@ -508,6 +511,8 @@ class BaseMaterial3D : public Material { float alpha_scissor_threshold = 0.0f; float alpha_hash_scale = 0.0f; float alpha_antialiasing_edge = 0.0f; + bool depth_offset_enabled = false; + float depth_offset = 0.0f; bool grow_enabled = false; float ao_light_affect = 0.0f; float grow = 0.0f; @@ -667,6 +672,12 @@ class BaseMaterial3D : public Material { void set_alpha_antialiasing_edge(float p_edge); float get_alpha_antialiasing_edge() const; + void set_depth_offset_enabled(bool p_enabled); + bool is_depth_offset_enabled() const; + + void set_depth_offset(float p_offset); + float get_depth_offset() const; + void set_shading_mode(ShadingMode p_shading_mode); ShadingMode get_shading_mode() const;