Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable geometry fade/transparency in the Mobile renderer #91672

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -511,12 +511,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 @@ -3751,9 +3751,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 @@ -3777,7 +3777,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 @@ -1088,7 +1088,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, 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 @@ -1841,6 +1841,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 @@ -1946,6 +1960,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 @@ -2161,6 +2180,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 @@ -2170,6 +2190,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 @@ -2184,7 +2207,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
} break;
}

PipelineCacheRD *pipeline = &shader->pipelines[cull_variant][primitive][shader_version];
PipelineCacheRD *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 @@ -394,6 +394,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 @@ -829,7 +829,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
Loading