Skip to content

Commit

Permalink
Add Depth Offset property to BaseMaterial3D and fix collision shape g…
Browse files Browse the repository at this point in the history
…izmo flicker

This has several use cases:

- Prevent collision shape gizmos that overlap with geometry from Z-fighting,
  which causes flickering in motion.
- Draw meshes that overlap other meshes without Z-fighting, which is useful
  for mesh-based decals (without needing to offset them from the surface manually).
- In general, allow meshes to draw in front of other meshes even if they're
  actually slightly behind, which can be useful for some VFX.
  • Loading branch information
Calinou committed Dec 9, 2024
1 parent aa8d9b8 commit 3384e6e
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 0 deletions.
7 changes: 7 additions & 0 deletions doc/classes/BaseMaterial3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@
<member name="depth_draw_mode" type="int" setter="set_depth_draw_mode" getter="get_depth_draw_mode" enum="BaseMaterial3D.DepthDrawMode" default="0">
Determines when depth rendering takes place. See [enum DepthDrawMode]. See also [member transparency].
</member>
<member name="depth_offset" type="bool" setter="set_depth_offset_enabled" getter="is_depth_offset_enabled" default="false">
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.
</member>
<member name="depth_offset_amount" type="float" setter="set_depth_offset" getter="get_depth_offset" default="0.0">
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).
</member>
<member name="detail_albedo" type="Texture2D" setter="set_texture" getter="get_texture">
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].
Expand Down
6 changes: 6 additions & 0 deletions editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ 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.
// by pushing the pixels towards the camera slightly.
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);
Expand Down
6 changes: 6 additions & 0 deletions editor/plugins/node_3d_editor_gizmos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,12 @@ void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color
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
// by pushing the pixels towards the camera slightly.
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_DISABLED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
Expand Down
6 changes: 6 additions & 0 deletions scene/resources/3d/shape_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ Ref<Material> 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.
// by pushing the pixels towards the camera slightly.
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);
Expand Down
51 changes: 51 additions & 0 deletions scene/resources/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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";
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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");
Expand Down
11 changes: 11 additions & 0 deletions scene/resources/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit 3384e6e

Please sign in to comment.