Backport new 3D point light attenuation as an option

This provides more realistic lighting with a very small performance cost.
The option is available in both GLES3 and GLES2, and can be enabled in
the Project Settings. This goes well with the ACES Fitted tonemapping mode
that was recently added.

When enabled, this also makes upgrading Godot 3.x projects to Godot 4.0 easier,
since lighting in 3.x will better match how it'll look in Godot 4.0.
This commit is contained in:
Hugo Locurcio 2021-09-21 02:58:50 +02:00
parent 3e2bb415a9
commit 85e080fcc0
No known key found for this signature in database
GPG key ID: 39E8F8BE30B0A49C
12 changed files with 120 additions and 4 deletions

View file

@ -1377,6 +1377,10 @@
<member name="rendering/quality/shading/force_vertex_shading.mobile" type="bool" setter="" getter="" default="true">
Lower-end override for [member rendering/quality/shading/force_vertex_shading] on mobile devices, due to performance concerns or driver support.
</member>
<member name="rendering/quality/shading/use_physical_light_attenuation" type="bool" setter="" getter="" default="false">
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.
</member>
<member name="rendering/quality/shadow_atlas/cubemap_size" type="int" setter="" getter="" default="512">
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.
</member>

View file

@ -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);

View file

@ -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: {

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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<Image> 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<Image> &p_albedo_texture, const Ref<Image> &p_emission_texture, Vector2i p_size, LocalVector<LightmapTexel> &r_texels, LocalVector<int> &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);

View file

@ -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");