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:
parent
3e2bb415a9
commit
85e080fcc0
12 changed files with 120 additions and 4 deletions
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue