diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 33e8c6d78f6..5cea58b9a24 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1377,6 +1377,10 @@ Lower-end override for [member rendering/quality/shading/force_vertex_shading] on mobile devices, due to performance concerns or driver support. + + If [code]true[/code], enables new physical light attenuation for [OmniLight]s and [SpotLight]s. This results in more realistic lighting appearance with a very small performance cost. When physical light attenuation is enabled, lights will appear to be darker as a result of the new attenuation formula. This can be compensated by adjusting the lights' energy or attenuation values. + Changes to this setting will only be applied upon restarting the application. + Size for cubemap into which the shadow is rendered before being copied into the shadow atlas. A higher number can result in higher resolution shadows when used with a higher [member rendering/quality/shadow_atlas/size]. Setting higher than a quarter of the [member rendering/quality/shadow_atlas/size] will not result in a perceptible increase in visual quality. diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 35d87d56342..9e2b5147ab9 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -2426,6 +2426,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, storage->info.render.surface_switch_count++; } + state.scene_shader.set_conditional(SceneShaderGLES2::USE_PHYSICAL_LIGHT_ATTENUATION, storage->config.use_physical_light_attenuation); + bool octahedral_compression = ((RasterizerStorageGLES2::Surface *)e->geometry)->format & VisualServer::ArrayFormat::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION; if (octahedral_compression != prev_octahedral_compression) { state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_OCTAHEDRAL_COMPRESSION, octahedral_compression); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index a3ac7fc129b..28c0a65690a 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -6508,6 +6508,8 @@ void RasterizerStorageGLES2::initialize() { GLOBAL_DEF_RST("rendering/quality/lightmapping/use_bicubic_sampling.mobile", false); config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling"); + config.use_physical_light_attenuation = GLOBAL_GET("rendering/quality/shading/use_physical_light_attenuation"); + int orphan_mode = GLOBAL_GET("rendering/2d/opengl/legacy_orphan_buffers"); switch (orphan_mode) { default: { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index b1b4c0a8a62..3ffe9ab7099 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -58,6 +58,7 @@ public: bool use_anisotropic_filter; bool use_skeleton_software; bool use_lightmap_filter_bicubic; + bool use_physical_light_attenuation; int max_vertex_texture_image_units; int max_texture_image_units; diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 899e5cc467c..a125eb8d0fe 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -206,6 +206,15 @@ uniform highp float light_spot_attenuation; uniform highp float light_spot_range; uniform highp float light_spot_angle; +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} + void light_compute( vec3 N, vec3 L, @@ -546,9 +555,12 @@ VERTEX_SHADER_CODE float normalized_distance = light_length / light_range; if (normalized_distance < 1.0) { +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + float omni_attenuation = get_omni_attenuation(light_length, 1.0 / light_range, light_attenuation); +#else float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); +#endif - vec3 attenuation = vec3(omni_attenuation); light_att = vec3(omni_attenuation); } else { light_att = vec3(0.0); @@ -565,7 +577,12 @@ VERTEX_SHADER_CODE float normalized_distance = light_length / light_range; if (normalized_distance < 1.0) { +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + float spot_attenuation = get_omni_attenuation(light_length, 1.0 / light_range, light_attenuation); +#else float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); +#endif + vec3 spot_dir = light_direction; float spot_cutoff = light_spot_angle; @@ -1222,6 +1239,17 @@ float GTR1(float NdotH, float a) { return (a2 - 1.0) / (M_PI * log(a2) * t); } +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} +#endif + void light_compute( vec3 N, vec3 L, @@ -1852,7 +1880,11 @@ FRAGMENT_SHADER_CODE float normalized_distance = light_length / light_range; if (normalized_distance < 1.0) { +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + float omni_attenuation = get_omni_attenuation(light_length, 1.0 / light_range, light_attenuation); +#else float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); +#endif light_att = vec3(omni_attenuation); } else { @@ -2134,7 +2166,12 @@ FRAGMENT_SHADER_CODE float normalized_distance = light_length / light_range; if (normalized_distance < 1.0) { +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + float spot_attenuation = get_omni_attenuation(light_length, 1.0 / light_range, light_attenuation); +#else float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); +#endif + vec3 spot_dir = light_direction; float spot_cutoff = light_spot_angle; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 34b1218514c..19af12b33d7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2111,6 +2111,8 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_ } } + state.scene_shader.set_conditional(SceneShaderGLES3::USE_PHYSICAL_LIGHT_ATTENUATION, storage->config.use_physical_light_attenuation); + bool octahedral_compression = ((RasterizerStorageGLES3::Surface *)e->geometry)->format & VisualServer::ArrayFormat::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION; if (octahedral_compression != prev_octahedral_compression) { state.scene_shader.set_conditional(SceneShaderGLES3::ENABLE_OCTAHEDRAL_COMPRESSION, octahedral_compression); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index b22708eb105..0cb06f24777 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -8252,6 +8252,8 @@ void RasterizerStorageGLES3::initialize() { GLOBAL_DEF("rendering/quality/lightmapping/use_bicubic_sampling.mobile", false); config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling"); + config.use_physical_light_attenuation = GLOBAL_GET("rendering/quality/shading/use_physical_light_attenuation"); + config.use_depth_prepass = bool(GLOBAL_GET("rendering/quality/depth_prepass/enable")); if (config.use_depth_prepass) { String vendors = GLOBAL_GET("rendering/quality/depth_prepass/disable_for_vendors"); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 6eb139aa269..bccdc6f7665 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -74,6 +74,7 @@ public: bool use_fast_texture_filter; bool use_anisotropic_filter; bool use_lightmap_filter_bicubic; + bool use_physical_light_attenuation; bool s3tc_supported; bool latc_supported; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 607a31b1854..72d6d120f79 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -234,11 +234,27 @@ void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, float roughness, in } } +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} +#endif + void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, inout vec3 diffuse, inout vec3 specular) { vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz - vertex; float light_length = length(light_rel_vec); + +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + vec3 light_attenuation = vec3(get_omni_attenuation(light_length, omni_lights[idx].light_pos_inv_radius.w, omni_lights[idx].light_direction_attenuation.w)); +#else float normalized_distance = light_length * omni_lights[idx].light_pos_inv_radius.w; vec3 light_attenuation = vec3(pow(max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w)); +#endif light_compute(normal, normalize(light_rel_vec), eye_vec, omni_lights[idx].light_color_energy.rgb * light_attenuation, roughness, diffuse, specular); } @@ -246,8 +262,14 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float r void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, inout vec3 diffuse, inout vec3 specular) { vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz - vertex; float light_length = length(light_rel_vec); + +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + vec3 light_attenuation = vec3(get_omni_attenuation(light_length, spot_lights[idx].light_pos_inv_radius.w, spot_lights[idx].light_direction_attenuation.w)); +#else float normalized_distance = light_length * spot_lights[idx].light_pos_inv_radius.w; vec3 light_attenuation = vec3(pow(max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w)); +#endif + vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz; float spot_cutoff = spot_lights[idx].light_params.y; float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff); @@ -1250,13 +1272,28 @@ in highp float dp_clip; #endif +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} +#endif + void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float specular, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light, inout float alpha) { vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz - vertex; float light_length = length(light_rel_vec); float normalized_distance = light_length * omni_lights[idx].light_pos_inv_radius.w; float omni_attenuation; if (normalized_distance < 1.0) { +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + omni_attenuation = get_omni_attenuation(light_length, omni_lights[idx].light_pos_inv_radius.w, omni_lights[idx].light_direction_attenuation.w); +#else omni_attenuation = pow(1.0 - normalized_distance, omni_lights[idx].light_direction_attenuation.w); +#endif } else { omni_attenuation = 0.0; } @@ -1316,7 +1353,11 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi float normalized_distance = light_length * spot_lights[idx].light_pos_inv_radius.w; float spot_attenuation; if (normalized_distance < 1.0) { - spot_attenuation = pow(1.0 - normalized_distance, spot_lights[idx].light_direction_attenuation.w); +#ifdef USE_PHYSICAL_LIGHT_ATTENUATION + spot_attenuation = get_omni_attenuation(light_length, spot_lights[idx].light_pos_inv_radius.w, spot_lights[idx].light_direction_attenuation.w); +#else + spot_attenuation = pow(1.0 - normalized_distance, omni_lights[idx].light_direction_attenuation.w); +#endif } else { spot_attenuation = 0.0; } diff --git a/modules/lightmapper_cpu/lightmapper_cpu.cpp b/modules/lightmapper_cpu/lightmapper_cpu.cpp index 75cb45b0a03..48385471de8 100644 --- a/modules/lightmapper_cpu/lightmapper_cpu.cpp +++ b/modules/lightmapper_cpu/lightmapper_cpu.cpp @@ -704,6 +704,15 @@ _ALWAYS_INLINE_ float uniform_rand() { return float(state) / float(UINT32_MAX); } +float LightmapperCPU::_get_omni_attenuation(float distance, float inv_range, float decay) const { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = MAX(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * powf(MAX(distance, 0.0001f), -decay); +} + void LightmapperCPU::_compute_direct_light(uint32_t p_idx, void *r_lightmap) { LightmapTexel *lightmap = (LightmapTexel *)r_lightmap; for (unsigned int i = 0; i < lights.size(); ++i) { @@ -734,7 +743,11 @@ void LightmapperCPU::_compute_direct_light(uint32_t p_idx, void *r_lightmap) { soft_shadowing_disk_size = light.size / dist; if (light.type == LIGHT_TYPE_OMNI) { - attenuation = powf(1.0 - dist / light.range, light.attenuation); + if (parameters.use_physical_light_attenuation) { + attenuation = _get_omni_attenuation(dist, 1.0f / light.range, light.attenuation); + } else { + attenuation = powf(1.0 - dist / light.range, light.attenuation); + } } else /* (light.type == LIGHT_TYPE_SPOT) */ { float angle = Math::acos(light.direction.dot(light_to_point)); @@ -743,7 +756,12 @@ void LightmapperCPU::_compute_direct_light(uint32_t p_idx, void *r_lightmap) { } float normalized_dist = dist * (1.0f / MAX(0.001f, light.range)); - float norm_light_attenuation = Math::pow(MAX(1.0f - normalized_dist, 0.001f), light.attenuation); + float norm_light_attenuation; + if (parameters.use_physical_light_attenuation) { + norm_light_attenuation = _get_omni_attenuation(dist, 1.0f / light.range, light.attenuation); + } else { + norm_light_attenuation = Math::pow(MAX(1.0f - normalized_dist, 0.001f), light.attenuation); + } float spot_cutoff = Math::cos(light.spot_angle); float scos = MAX(light_to_point.dot(light.direction), spot_cutoff); @@ -1279,6 +1297,7 @@ LightmapperCPU::BakeError LightmapperCPU::bake(BakeQuality p_quality, bool p_use // Collect parameters parameters.use_denoiser = p_use_denoiser; + parameters.use_physical_light_attenuation = bool(GLOBAL_GET("rendering/quality/shading/use_physical_light_attenuation")); parameters.bias = p_bias; parameters.bounces = p_bounces; parameters.bounce_indirect_energy = p_bounce_indirect_energy; diff --git a/modules/lightmapper_cpu/lightmapper_cpu.h b/modules/lightmapper_cpu/lightmapper_cpu.h index 7f64b3f8611..bcd27cef8d0 100644 --- a/modules/lightmapper_cpu/lightmapper_cpu.h +++ b/modules/lightmapper_cpu/lightmapper_cpu.h @@ -85,6 +85,7 @@ class LightmapperCPU : public Lightmapper { float bounce_indirect_energy; int samples; bool use_denoiser = true; + bool use_physical_light_attenuation = false; Ref environment_panorama; Basis environment_transform; }; @@ -151,6 +152,8 @@ class LightmapperCPU : public Lightmapper { Vector3 _fix_sample_position(const Vector3 &p_position, const Vector3 &p_texel_center, const Vector3 &p_normal, const Vector3 &p_tangent, const Vector3 &p_bitangent, const Vector2 &p_texel_size); void _plot_triangle(const Vector2 *p_vertices, const Vector3 *p_positions, const Vector3 *p_normals, const Vector2 *p_uvs, const Ref &p_albedo_texture, const Ref &p_emission_texture, Vector2i p_size, LocalVector &r_texels, LocalVector &r_lightmap_indices); + float _get_omni_attenuation(float distance, float inv_range, float decay) const; + void _compute_direct_light(uint32_t p_idx, void *r_lightmap); void _compute_indirect_light(uint32_t p_idx, void *r_lightmap); diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 21d5a0a4d6c..53900bd9807 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2636,6 +2636,8 @@ VisualServer::VisualServer() { GLOBAL_DEF_RST("rendering/misc/mesh_storage/split_stream", false); + GLOBAL_DEF_RST("rendering/quality/shading/use_physical_light_attenuation", false); + GLOBAL_DEF("rendering/quality/depth_prepass/enable", true); GLOBAL_DEF("rendering/quality/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno,Apple");