Add a per-light volumetric fog energy property
Per-light energy gives more control to the user on the final result of volumetric fog. Specific lights can be fully excluded from volumetric fog by setting their volumetric fog energy to 0, which improves performance slightly. This can also be used to prevent short-lived dynamic effects from poorly interacting with volumetric fog, as it's updated over several frames by default unless temporal reprojection is disabled. Volumetric fog shadows now obey Light3D's Shadow Opacity property as well. The shadow fog fade property was removed as it had little visible impact on the final scene's rendering.
This commit is contained in:
parent
e27b61d291
commit
09bedcead4
14 changed files with 100 additions and 106 deletions
|
|
@ -2871,6 +2871,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
light_data.color[2] = linear_col.b;
|
||||
|
||||
light_data.specular = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR);
|
||||
light_data.volumetric_fog_energy = light_storage->light_get_param(base, RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY);
|
||||
light_data.mask = light_storage->light_get_cull_mask(base);
|
||||
|
||||
float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
|
||||
|
|
@ -2952,7 +2953,6 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
float fade_start = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_FADE_START);
|
||||
light_data.fade_from = -light_data.shadow_split_offsets[3] * MIN(fade_start, 0.999); //using 1.0 would break smoothstep
|
||||
light_data.fade_to = -light_data.shadow_split_offsets[3];
|
||||
light_data.shadow_volumetric_fog_fade = 1.0 / light_storage->light_get_shadow_volumetric_fog_fade(base);
|
||||
|
||||
light_data.soft_shadow_scale = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR);
|
||||
light_data.softshadow_angle = angular_diameter;
|
||||
|
|
@ -3082,6 +3082,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
light_data.color[1] = linear_col.g * energy;
|
||||
light_data.color[2] = linear_col.b * energy;
|
||||
light_data.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0;
|
||||
light_data.volumetric_fog_energy = light_storage->light_get_param(base, RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY);
|
||||
light_data.bake_mode = light_storage->light_get_bake_mode(base);
|
||||
|
||||
float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE));
|
||||
|
|
@ -3176,7 +3177,6 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
|
|||
light_data.atlas_rect[3] = rect.size.height;
|
||||
|
||||
light_data.soft_shadow_scale = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR);
|
||||
light_data.shadow_volumetric_fog_fade = 1.0 / light_storage->light_get_shadow_volumetric_fog_fade(base);
|
||||
|
||||
if (type == RS::LIGHT_OMNI) {
|
||||
Transform3D proj = (inverse_transform * light_transform).inverse();
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ private:
|
|||
float soft_shadow_size;
|
||||
float soft_shadow_scale;
|
||||
uint32_t mask;
|
||||
float shadow_volumetric_fog_fade;
|
||||
float volumetric_fog_energy;
|
||||
uint32_t bake_mode;
|
||||
float projector_rect[4];
|
||||
};
|
||||
|
|
@ -638,7 +638,7 @@ private:
|
|||
float fade_to;
|
||||
uint32_t pad[2];
|
||||
uint32_t bake_mode;
|
||||
float shadow_volumetric_fog_fade;
|
||||
float volumetric_fog_energy;
|
||||
float shadow_bias[4];
|
||||
float shadow_normal_bias[4];
|
||||
float shadow_transmittance_bias[4];
|
||||
|
|
|
|||
|
|
@ -270,6 +270,9 @@ const vec3 halton_map[TEMPORAL_FRAMES] = vec3[](
|
|||
vec3(0.9375, 0.25925926, 0.12),
|
||||
vec3(0.03125, 0.59259259, 0.32));
|
||||
|
||||
// Higher values will make light in volumetric fog fade out sooner when it's occluded by shadow.
|
||||
const float INV_FOG_FADE = 10.0;
|
||||
|
||||
void main() {
|
||||
vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size);
|
||||
|
||||
|
|
@ -375,46 +378,48 @@ void main() {
|
|||
|
||||
if (total_density > 0.001) {
|
||||
for (uint i = 0; i < params.directional_light_count; i++) {
|
||||
vec3 shadow_attenuation = vec3(1.0);
|
||||
if (directional_lights.data[i].volumetric_fog_energy > 0.001) {
|
||||
vec3 shadow_attenuation = vec3(1.0);
|
||||
|
||||
if (directional_lights.data[i].shadow_opacity > 0.001) {
|
||||
float depth_z = -view_pos.z;
|
||||
if (directional_lights.data[i].shadow_opacity > 0.001) {
|
||||
float depth_z = -view_pos.z;
|
||||
|
||||
vec4 pssm_coord;
|
||||
vec3 light_dir = directional_lights.data[i].direction;
|
||||
vec4 v = vec4(view_pos, 1.0);
|
||||
float z_range;
|
||||
vec4 pssm_coord;
|
||||
vec3 light_dir = directional_lights.data[i].direction;
|
||||
vec4 v = vec4(view_pos, 1.0);
|
||||
float z_range;
|
||||
|
||||
if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.x;
|
||||
if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.x;
|
||||
|
||||
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.y;
|
||||
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.y;
|
||||
|
||||
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.z;
|
||||
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.z;
|
||||
|
||||
} else {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.w;
|
||||
} else {
|
||||
pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
|
||||
pssm_coord /= pssm_coord.w;
|
||||
z_range = directional_lights.data[i].shadow_z_range.w;
|
||||
}
|
||||
|
||||
float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r;
|
||||
float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * INV_FOG_FADE);
|
||||
|
||||
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance
|
||||
|
||||
shadow_attenuation = mix(vec3(1.0 - directional_lights.data[i].shadow_opacity), vec3(1.0), shadow);
|
||||
}
|
||||
|
||||
float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r;
|
||||
float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * directional_lights.data[i].shadow_volumetric_fog_fade);
|
||||
|
||||
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance
|
||||
|
||||
shadow_attenuation = mix(vec3(0.0), vec3(1.0), shadow);
|
||||
total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy * henyey_greenstein(dot(normalize(view_pos), normalize(directional_lights.data[i].direction)), params.phase_g) * directional_lights.data[i].volumetric_fog_energy;
|
||||
}
|
||||
|
||||
total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy * henyey_greenstein(dot(normalize(view_pos), normalize(directional_lights.data[i].direction)), params.phase_g);
|
||||
}
|
||||
|
||||
// Compute light from sky
|
||||
|
|
@ -481,7 +486,7 @@ void main() {
|
|||
float d = distance(omni_lights.data[light_index].position, view_pos);
|
||||
float shadow_attenuation = 1.0;
|
||||
|
||||
if (d * omni_lights.data[light_index].inv_radius < 1.0) {
|
||||
if (omni_lights.data[light_index].volumetric_fog_energy > 0.001 && d * omni_lights.data[light_index].inv_radius < 1.0) {
|
||||
float attenuation = get_omni_attenuation(d, omni_lights.data[light_index].inv_radius, omni_lights.data[light_index].attenuation);
|
||||
|
||||
vec3 light = omni_lights.data[light_index].color;
|
||||
|
|
@ -509,9 +514,9 @@ void main() {
|
|||
|
||||
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
|
||||
|
||||
shadow_attenuation = exp(min(0.0, (depth - pos.z)) / omni_lights.data[light_index].inv_radius * omni_lights.data[light_index].shadow_volumetric_fog_fade);
|
||||
shadow_attenuation = mix(1.0 - omni_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / omni_lights.data[light_index].inv_radius * INV_FOG_FADE));
|
||||
}
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g);
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g) * omni_lights.data[light_index].volumetric_fog_energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -562,7 +567,7 @@ void main() {
|
|||
float d = length(light_rel_vec);
|
||||
float shadow_attenuation = 1.0;
|
||||
|
||||
if (d * spot_lights.data[light_index].inv_radius < 1.0) {
|
||||
if (spot_lights.data[light_index].volumetric_fog_energy > 0.001 && d * spot_lights.data[light_index].inv_radius < 1.0) {
|
||||
float attenuation = get_omni_attenuation(d, spot_lights.data[light_index].inv_radius, spot_lights.data[light_index].attenuation);
|
||||
|
||||
vec3 spot_dir = spot_lights.data[light_index].direction;
|
||||
|
|
@ -595,9 +600,9 @@ void main() {
|
|||
|
||||
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
|
||||
|
||||
shadow_attenuation = exp(min(0.0, (depth - pos.z)) / spot_lights.data[light_index].inv_radius * spot_lights.data[light_index].shadow_volumetric_fog_fade);
|
||||
shadow_attenuation = mix(1.0 - spot_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / spot_lights.data[light_index].inv_radius * INV_FOG_FADE));
|
||||
}
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g);
|
||||
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g) * spot_lights.data[light_index].volumetric_fog_energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ struct LightData { //this structure needs to be as packed as possible
|
|||
highp float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle
|
||||
highp float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
|
||||
uint mask;
|
||||
mediump float shadow_volumetric_fog_fade;
|
||||
mediump float volumetric_fog_energy;
|
||||
uint bake_mode;
|
||||
highp vec4 projector_rect; //projector rect in srgb decal atlas
|
||||
};
|
||||
|
|
@ -65,7 +65,7 @@ struct DirectionalLightData {
|
|||
highp float fade_to;
|
||||
uvec2 pad;
|
||||
uint bake_mode;
|
||||
mediump float shadow_volumetric_fog_fade;
|
||||
mediump float volumetric_fog_energy;
|
||||
highp vec4 shadow_bias;
|
||||
highp vec4 shadow_normal_bias;
|
||||
highp vec4 shadow_transmittance_bias;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
|
|||
|
||||
light.param[RS::LIGHT_PARAM_ENERGY] = 1.0;
|
||||
light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0;
|
||||
light.param[RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY] = 1.0;
|
||||
light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5;
|
||||
light.param[RS::LIGHT_PARAM_RANGE] = 1.0;
|
||||
light.param[RS::LIGHT_PARAM_SIZE] = 0.0;
|
||||
|
|
@ -91,7 +92,6 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) {
|
|||
light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0;
|
||||
light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0;
|
||||
light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
|
||||
light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1;
|
||||
light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05;
|
||||
|
||||
light_owner.initialize_rid(p_light, light);
|
||||
|
|
|
|||
|
|
@ -250,13 +250,6 @@ public:
|
|||
return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const {
|
||||
const Light *light = light_owner.get_or_null(p_light);
|
||||
ERR_FAIL_COND_V(!light, 0.0);
|
||||
|
||||
return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE];
|
||||
}
|
||||
|
||||
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
|
||||
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override;
|
||||
virtual uint64_t light_get_version(RID p_light) const override;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue