Skip to content

Commit

Permalink
Enable geometry fade/transparency in the Mobile renderer
Browse files Browse the repository at this point in the history
This is mostly a copy-paste of similar changes in the Forward+ renderer,
as hinted in godotengine#76774 (comment) and godotengine#87231 (review),
along with a tweak to the shader and pipeline model.

The shader pipeline tweak involves distinguishing geometry instance-enabled
transparency from material-enabled transparency. A material cannot support
transparency on a per-instance level; a separate pipeline must be used when rendering.

Originally in c571e4a this was a non-trivial refactoring in Forward+
(which in the past few years has evolved a few more times to account for the multitude of combinations
allowed there).

I opted to follow a similar sentiment to `SceneShaderForwardClustered::PipelineColorPassFlags`
in a simpler `SceneShaderForwardMobile::PipelinePasses` enum, which only accounts
for don't-care or opaque pipelines vs. color transparency-enabled ones.

This multiplies the number of `PipelineCacheRD`s by two, but otherwise accomplishes
the goals without adding unnecessary changes or shooting myself or others in the foot (I hope).

BTW I also reorganized and removed some redundant checks in
`RenderForwardClustered::_geometry_instance_add_surface_with_material`
(since I was comparing this side-by-side with the `RenderForwardMobile` version, which seems
better-organized). There is no functional change since `has_alpha` is the only variable used
and includes all the checks anyway.
  • Loading branch information
eswartz committed May 28, 2024
1 parent 0500f94 commit 672ad72
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 51 deletions.
6 changes: 3 additions & 3 deletions doc/classes/GeometryInstance3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
The transparency applied to the whole geometry (as a multiplier of the materials' existing transparency). [code]0.0[/code] is fully opaque, while [code]1.0[/code] is fully transparent. Values greater than [code]0.0[/code] (exclusive) will force the geometry's materials to go through the transparent pipeline, which is slower to render and can exhibit rendering issues due to incorrect transparency sorting. However, unlike using a transparent material, setting [member transparency] to a value greater than [code]0.0[/code] (exclusive) will [i]not[/i] disable shadow rendering.
In spatial shaders, [code]1.0 - transparency[/code] is set as the default value of the [code]ALPHA[/code] built-in.
[b]Note:[/b] [member transparency] is clamped between [code]0.0[/code] and [code]1.0[/code], so this property cannot be used to make transparent materials more opaque than they originally are.
[b]Note:[/b] Only supported when using the Forward+ rendering method. When using the Mobile or Compatibility rendering method, [member transparency] is ignored and is considered as always being [code]0.0[/code].
[b]Note:[/b] Only supported when using the Forward+ or Mobile rendering methods. When using the Compatibility rendering method, [member transparency] is ignored and is considered as always being [code]0.0[/code].
</member>
<member name="visibility_range_begin" type="float" setter="set_visibility_range_begin" getter="get_visibility_range_begin" default="0.0" keywords="lod_begin, hlod_begin">
Starting distance from which the GeometryInstance3D will be visible, taking [member visibility_range_begin_margin] into account as well. The default value of 0 is used to disable the range check.
Expand Down Expand Up @@ -131,11 +131,11 @@
</constant>
<constant name="VISIBILITY_RANGE_FADE_SELF" value="1" enum="VisibilityRangeFadeMode">
Will fade-out itself when reaching the limits of its own visibility range. This is slower than [constant VISIBILITY_RANGE_FADE_DISABLED], but it can provide smoother transitions. The fading range is determined by [member visibility_range_begin_margin] and [member visibility_range_end_margin].
[b]Note:[/b] Only supported when using the Forward+ rendering method. When using the Mobile or Compatibility rendering method, this mode acts like [constant VISIBILITY_RANGE_FADE_DISABLED] but with hysteresis disabled.
[b]Note:[/b] Only supported when using the Forward+ or Mobile rendering methods. When using the Compatibility rendering method, this mode acts like [constant VISIBILITY_RANGE_FADE_DISABLED] but with hysteresis disabled.
</constant>
<constant name="VISIBILITY_RANGE_FADE_DEPENDENCIES" value="2" enum="VisibilityRangeFadeMode">
Will fade-in its visibility dependencies (see [member Node3D.visibility_parent]) when reaching the limits of its own visibility range. This is slower than [constant VISIBILITY_RANGE_FADE_DISABLED], but it can provide smoother transitions. The fading range is determined by [member visibility_range_begin_margin] and [member visibility_range_end_margin].
[b]Note:[/b] Only supported when using the Forward+ rendering method. When using the Mobile or Compatibility rendering method, this mode acts like [constant VISIBILITY_RANGE_FADE_DISABLED] but with hysteresis disabled.
[b]Note:[/b] Only supported when using the Forward+ or Mobile rendering methods. When using the Compatibility rendering method, this mode acts like [constant VISIBILITY_RANGE_FADE_DISABLED] but with hysteresis disabled.
</constant>
</constants>
</class>
8 changes: 4 additions & 4 deletions scene/3d/visual_instance_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,12 @@ PackedStringArray GeometryInstance3D::get_configuration_warnings() const {
warnings.push_back(RTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0."));
}

if (!Math::is_zero_approx(transparency) && OS::get_singleton()->get_current_rendering_method() != "forward_plus") {
warnings.push_back(RTR("GeometryInstance3D transparency is only available when using the Forward+ rendering method."));
if (!Math::is_zero_approx(transparency) && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("GeometryInstance3D transparency is only available when using the Forward+ or Mobile rendering methods."));
}

if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && OS::get_singleton()->get_current_rendering_method() != "forward_plus") {
warnings.push_back(RTR("GeometryInstance3D visibility range transparency fade is only available when using the Forward+ rendering method."));
if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("GeometryInstance3D visibility range transparency fade is only available when using the Forward+ or Mobile rendering methods."));
}

return warnings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3730,9 +3730,9 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();

bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
bool has_base_alpha = (p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing)) || has_read_screen_alpha;
bool has_base_alpha = (p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing));
bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
bool has_alpha = has_base_alpha || has_blend_alpha;
bool has_alpha = has_base_alpha || has_blend_alpha || has_read_screen_alpha;

uint32_t flags = 0;

Expand All @@ -3756,7 +3756,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}

if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED) {
if (has_alpha || p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED) {
//material is only meant for alpha pass
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA;
if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
// this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation
//_setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false);

RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR_TRANSPARENT, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
render_list_params.framebuffer_format = fb_format;
render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0.

Expand Down Expand Up @@ -1830,6 +1830,20 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const

bool uses_lightmap = false;
// bool uses_gi = false;
float fade_alpha = 1.0;

if (inst->fade_near || inst->fade_far) {
float fade_dist = inst->transform.origin.distance_to(p_render_data->scene_data->cam_transform.origin);
// Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player.
if (inst->fade_far && fade_dist > inst->fade_far_begin) {
fade_alpha = Math::smoothstep(0.0f, 1.0f, 1.0f - (fade_dist - inst->fade_far_begin) / (inst->fade_far_end - inst->fade_far_begin));
} else if (inst->fade_near && fade_dist < inst->fade_near_end) {
fade_alpha = Math::smoothstep(0.0f, 1.0f, (fade_dist - inst->fade_near_begin) / (inst->fade_near_end - inst->fade_near_begin));
}
}

fade_alpha *= inst->force_alpha * inst->parent_fade_alpha;
flags |= (uint32_t(fade_alpha * 255.0) << INSTANCE_DATA_FLAGS_FADE_SHIFT) & INSTANCE_DATA_FLAGS_FADE_MASK;

if (p_render_list == RENDER_LIST_OPAQUE) {
if (inst->lightmap_instance.is_valid()) {
Expand Down Expand Up @@ -1935,6 +1949,11 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
#else
bool force_alpha = false;
#endif

if (fade_alpha < 0.999) {
force_alpha = true;
}

if (!force_alpha && (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE)) {
rl->add_element(surf);
}
Expand Down Expand Up @@ -2146,6 +2165,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
RID xforms_uniform_set = surf->owner->transforms_uniform_set;

SceneShaderForwardMobile::ShaderVersion shader_version = SceneShaderForwardMobile::SHADER_VERSION_MAX; // Assigned to silence wrong -Wmaybe-initialized.
SceneShaderForwardMobile::PipelinePasses color_pass = SceneShaderForwardMobile::PIPELINE_PASS_NOT_COLOR_OR_OPAQUE;

switch (p_params->pass_mode) {
case PASS_MODE_COLOR:
Expand All @@ -2155,6 +2175,9 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
} else {
shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
}
if (p_params->pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
color_pass = SceneShaderForwardMobile::PIPELINE_PASS_COLOR_TRANSPARENT;
}
} break;
case PASS_MODE_SHADOW: {
shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
Expand All @@ -2171,7 +2194,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr

PipelineCacheRD *pipeline = nullptr;

pipeline = &shader->pipelines[cull_variant][primitive][shader_version];
pipeline = &shader->pipelines[cull_variant][primitive][shader_version][color_pass];

RD::VertexFormatID vertex_format = -1;
RID vertex_array_rd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ class RenderForwardMobile : public RendererSceneRenderRD {
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT = 16,
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_MASK = 0xFF,
INSTANCE_DATA_FLAGS_FADE_SHIFT = 24,
INSTANCE_DATA_FLAGS_FADE_MASK = 0xFFUL << INSTANCE_DATA_FLAGS_FADE_SHIFT
};

struct GeometryInstanceLightmapSH {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,46 +314,49 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
raster_state.cull_mode = cull_mode_rd;
raster_state.wireframe = wireframe;

RD::PipelineColorBlendState blend_state;
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
RD::PipelineMultisampleState multisample_state;

if (uses_alpha || uses_blend_alpha) {
// only allow these flags to go through if we have some form of msaa
if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
multisample_state.enable_alpha_to_coverage = true;
} else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
multisample_state.enable_alpha_to_coverage = true;
multisample_state.enable_alpha_to_one = true;
}
for (int l = 0; l < PIPELINE_PASS_COUNT; l++) {
RD::PipelineColorBlendState blend_state;
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
RD::PipelineMultisampleState multisample_state;

if (l == PIPELINE_PASS_COLOR_TRANSPARENT) {
// only allow these flags to go through if we have some form of msaa
if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
multisample_state.enable_alpha_to_coverage = true;
} else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
multisample_state.enable_alpha_to_coverage = true;
multisample_state.enable_alpha_to_one = true;
}

if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_blend;
if (depth_draw == DEPTH_DRAW_OPAQUE && !uses_alpha_clip) {
depth_stencil.enable_depth_write = false; //alpha does not draw depth
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_blend;
if (depth_draw == DEPTH_DRAW_OPAQUE && !uses_alpha_clip) {
depth_stencil.enable_depth_write = false; //alpha does not draw depth
}
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, blend state contains nothing
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
pipelines[i][j][k][l].clear();
continue; // do not use this version (will error if using it is attempted)
}
} else { // PIPELINE_PASS_NOT_COLOR_OR_OPAQUE

if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_opaque;
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, leave empty
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
// ???
}
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, blend state contains nothing
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
pipelines[i][j][k].clear();
continue; // do not use this version (will error if using it is attempted)
}
} else {
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_opaque;
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, leave empty
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
// ???
}
}

RID shader_variant = shader_singleton->shader.version_get_shader(version, k);
pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
RID shader_variant = shader_singleton->shader.version_get_shader(version, k);
pipelines[i][j][k][l].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
}
}
}
}
Expand Down Expand Up @@ -757,7 +760,9 @@ void SceneShaderForwardMobile::set_default_specialization_constants(const Vector
for (int i = 0; i < ShaderData::CULL_VARIANT_MAX; i++) {
for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
for (int k = 0; k < SHADER_VERSION_MAX; k++) {
E->self()->pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
for (int l = 0; l < PIPELINE_PASS_COUNT; l++) {
E->self()->pipelines[i][j][k][l].update_specialization_constants(default_specialization_constants);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ class SceneShaderForwardMobile {
SHADER_VERSION_MAX
};

enum PipelinePasses {
PIPELINE_PASS_NOT_COLOR_OR_OPAQUE = 0,
PIPELINE_PASS_COLOR_TRANSPARENT = 1,
PIPELINE_PASS_COUNT = 2,
};

struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
enum BlendMode { //used internally
BLEND_MODE_MIX,
Expand Down Expand Up @@ -99,7 +105,7 @@ class SceneShaderForwardMobile {
bool valid = false;
RID version;
uint64_t vertex_input_mask = 0;
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX];
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX][PIPELINE_PASS_COUNT];

Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ void main() {
float ao = 1.0;
float ao_light_affect = 0.0;

float alpha = 1.0;
float alpha = float((instances.data[draw_call.instance_index].flags >> INSTANCE_FLAGS_FADE_SHIFT) & INSTANCE_FLAGS_FADE_MASK) / float(255.0);

#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
vec3 binormal = normalize(binormal_interp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15)
#define INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT 16
#define INSTANCE_FLAGS_FADE_SHIFT 24
//3 bits of stride
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
#define INSTANCE_FLAGS_FADE_MASK 0xFF

layout(set = 0, binding = 3, std430) restrict readonly buffer OmniLights {
LightData data[];
Expand Down

0 comments on commit 672ad72

Please sign in to comment.