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

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