From 1d03d11a2325b352ee0c3294cb9fc43de097f856 Mon Sep 17 00:00:00 2001 From: Hendrik Brucker Date: Mon, 6 Jan 2025 14:18:29 +0100 Subject: [PATCH] (rebased) first version of specular occlusion --- doc/classes/BaseMaterial3D.xml | 8 ++- doc/classes/ProjectSettings.xml | 7 +++ drivers/gles3/shaders/scene.glsl | 52 ++++++++++++---- drivers/gles3/storage/material_storage.cpp | 13 ++++ scene/resources/material.cpp | 6 ++ scene/resources/material.h | 1 + .../scene_shader_forward_clustered.cpp | 13 ++++ .../scene_shader_forward_mobile.cpp | 13 ++++ .../scene_forward_clustered.glsl | 59 +++++++++++++++---- .../forward_mobile/scene_forward_mobile.glsl | 56 ++++++++++++++---- servers/rendering/shader_types.cpp | 1 + servers/rendering_server.cpp | 1 + 12 files changed, 195 insertions(+), 35 deletions(-) diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index c68f3b23bf92..483e5bb136e3 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -182,6 +182,9 @@ If [code]true[/code], the object receives no shadow that would otherwise be cast onto it. + + If [code]true[/code], disables specular occlusion of objects under the influence of a [LightmapGI] node regardless of [member ProjectSettings.rendering/lightmapping/specular_occlusion/mode]. + Distance at which the object appears fully opaque. [b]Note:[/b] If [member distance_fade_max_distance] is less than [member distance_fade_min_distance], the behavior will be reversed. The object will start to fade away at [member distance_fade_max_distance] and will fully disappear once it reaches [member distance_fade_min_distance]. @@ -706,7 +709,10 @@ Disables receiving depth-based or volumetric fog. - + + Disables specular occlusion produced by non-directional [LightmapGI] nodes. + + Represents the size of the [enum Flags] enum. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 45ff4ff74f2a..d1481a0e5c81 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2782,6 +2782,13 @@ The framerate-independent update speed when representing dynamic object lighting from [LightmapProbe]s. Higher values make dynamic object lighting update faster. Higher values can prevent fast-moving objects from having "outdated" indirect lighting displayed on them, at the cost of possible flickering when an object moves from a bright area to a shaded area. + + The specular occlusion method for object lighting from [LightmapGI]s. + - Disabled: No specular occlusion is used + - Reduce: Reduce the energy by the current [LightmapGI] pixel brightness + - Conservative: Conserve most of the energy by mixing metallic and roughness values, resulting in more realistic surface + [b]Note:[/b] When [member LightmapGI.directional] is set to [code]true[/code], this feature is disabled. + Use 16 bits for the directional shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index f61bb73a85a4..7d791c1ff9b9 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -2058,17 +2058,32 @@ void main() { const float c3 = 0.743125; const float c4 = 0.886227; const float c5 = 0.247708; - ambient_light += (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures[0].rgb - - c5 * lightmap_captures[6].rgb + - 2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * + vec3 c = (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures[0].rgb - + c5 * lightmap_captures[6].rgb + + 2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * scene_data.emissive_exposure_normalization; + +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * scene_data.emissive_exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + ambient_light += c; } #else #ifdef USE_LIGHTMAP @@ -2102,7 +2117,24 @@ void main() { #ifdef LIGHTMAP_BICUBIC_FILTER ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization; #else +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + vec3 c = textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; + + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * lightmap_exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; + ambient_light += c; +#else // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) #endif #endif } diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index abaa507453e7..16992e1224fc 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1375,6 +1375,19 @@ MaterialStorage::MaterialStorage() { } actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n"; + int specular_occlusion_mode = GLOBAL_GET("rendering/lightmapping/specular_occlusion/mode"); + + switch (specular_occlusion_mode) { + case 0: + break; // disabled, skip + case 1: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION\n"; + break; + case 2: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION_CONSERVATIVE\n"; + break; + } + actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index a9dd89289c01..8779913d7f87 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -836,6 +836,7 @@ void BaseMaterial3D::_update_shader() { case SPECULAR_MAX: break; // Internal value, skip. } + if (features[FEATURE_SUBSURFACE_SCATTERING] && flags[FLAG_SUBSURFACE_MODE_SKIN]) { code += ", sss_mode_skin"; } @@ -864,6 +865,9 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_DISABLE_FOG]) { code += ", fog_disabled"; } + if (!flags[FLAG_DISABLE_SPECULAR_OCCLUSION]) { + code += ",specular_occlusion"; + } if (transparency == TRANSPARENCY_ALPHA_DEPTH_PRE_PASS) { code += ", depth_prepass_alpha"; @@ -3175,6 +3179,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Toon,Disabled"), "set_specular_mode", "get_specular_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_fog"), "set_flag", "get_flag", FLAG_DISABLE_FOG); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_specular_occlusion"), "set_flag", "get_flag", FLAG_DISABLE_SPECULAR_OCCLUSION); ADD_GROUP("Vertex Color", "vertex_color"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR); @@ -3429,6 +3434,7 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE); BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_MSDF); BIND_ENUM_CONSTANT(FLAG_DISABLE_FOG); + BIND_ENUM_CONSTANT(FLAG_DISABLE_SPECULAR_OCCLUSION); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); diff --git a/scene/resources/material.h b/scene/resources/material.h index 36d325bd33f8..09e4d036eada 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -268,6 +268,7 @@ class BaseMaterial3D : public Material { FLAG_PARTICLE_TRAILS_MODE, FLAG_ALBEDO_TEXTURE_MSDF, FLAG_DISABLE_FOG, + FLAG_DISABLE_SPECULAR_OCCLUSION, FLAG_MAX }; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 102d15206fe0..e7ad45b0bb73 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -765,6 +765,19 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n"; actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n"; + int specular_occlusion_mode = GLOBAL_GET("rendering/lightmapping/specular_occlusion/mode"); + + switch (specular_occlusion_mode) { + case 0: + break; // disabled, skip + case 1: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION\n"; + break; + case 2: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION_CONSERVATIVE\n"; + break; + } + actions.base_texture_binding_index = 1; actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET; actions.base_uniform_string = "material."; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 4d888d99d22a..07ebc217d4c0 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -698,6 +698,19 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n"; actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n"; + int specular_occlusion_mode = GLOBAL_GET("rendering/lightmapping/specular_occlusion/mode"); + + switch (specular_occlusion_mode) { + case 0: + break; // disabled, skip + case 1: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION\n"; + break; + case 2: + actions.render_mode_defines["specular_occlusion"] = "#define SPECULAR_OCCLUSION_CONSERVATIVE\n"; + break; + } + actions.base_texture_binding_index = 1; actions.texture_layout_set = RenderForwardMobile::MATERIAL_UNIFORM_SET; actions.base_uniform_string = "material."; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 92a29ebcfba8..ac49c9aa688a 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1654,18 +1654,34 @@ void fragment_shader(in SceneData scene_data) { const float c3 = 0.743125; const float c4 = 0.886227; const float c5 = 0.247708; - ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + + vec3 c = (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * scene_data.emissive_exposure_normalization; +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * scene_data.emissive_exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + ambient_light += c; + } else if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); uint ofs = instances.data[instance_index].gi_offset & 0xFFFF; @@ -1702,11 +1718,30 @@ void fragment_shader(in SceneData scene_data) { ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * en * 4.0); } else { + vec3 c; if (sc_use_lightmap_bicubic_filter()) { - ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + c = textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + c = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; } +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + // vec3 c = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * lightmaps.data[ofs].exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; + ambient_light += c; +#else // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + ambient_light += c; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) } } #else diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index c403a8fa48e3..fc612f0c582c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1316,18 +1316,33 @@ void main() { const float c3 = 0.743125; const float c4 = 0.886227; const float c5 = 0.247708; - ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + vec3 c = (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * scene_data.emissive_exposure_normalization; +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * scene_data.emissive_exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + ambient_light += c; + } else if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); uint ofs = instances.data[draw_call.instance_index].gi_offset & 0xFFFF; @@ -1363,11 +1378,28 @@ void main() { ambient_light += lm_light_l1_0 * n.z * (lm_light_l0 * exposure_normalization * 4.0); ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * exposure_normalization * 4.0); } else { + vec3 c; if (sc_use_lightmap_bicubic_filter()) { - ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + c = textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + c = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; } +#if defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float specular_occlusion = (c.r * 0.3 + c.g * 0.59 + c.b * 0.11) * 2.0; // brightness from lightmap color + specular_occlusion = min(1.0, specular_occlusion * lightmaps.data[ofs].exposure_normalization); + +#if defined(SPECULAR_OCCLUSION_CONSERVATIVE) + float reflective_f = (1.0 - roughness) * metallic; + // 10.0 is a number picked by hand, it gives the intended effect in most scenarios + // (low enough for occlusion, high enough for reaction to lights and shadows) + specular_occlusion = max(min(reflective_f * specular_occlusion * 10.0, 1.0), specular_occlusion); +#endif // defined(SPECULAR_OCCLUSION_CONSERVATIVE) + + specular_light *= specular_occlusion; + ambient_light += c; +#else // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) + ambient_light += c; +#endif // defined(SPECULAR_OCCLUSION) || defined(SPECULAR_OCCLUSION_CONSERVATIVE) } } diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 32fc3ffa9115..e96c7a7e3dfd 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -229,6 +229,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("alpha_to_coverage_and_one") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("debug_shadow_splits") }); shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("fog_disabled") }); + shader_modes[RS::SHADER_SPATIAL].modes.push_back({ PNAME("specular_occlusion") }); } /************ CANVAS ITEM **************************/ diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 96c49e505b82..989f41cebd55 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3684,6 +3684,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/limits/global_shader_variables/buffer_size", PROPERTY_HINT_RANGE, "16,1048576,1"), 65536); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/lightmapping/specular_occlusion/mode", PROPERTY_HINT_ENUM, "Disabled (Fastest),Reduce (Faster),Conservative (Fast)"), 2); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2); GLOBAL_DEF("rendering/lightmapping/lightmap_gi/use_bicubic_filter", true);