EEVEE-Next: Fix missing shadow caused by ray going below surface
This should fix the light leaking present in most curved surfaces. This is completely ad-hoc and should be fixed better.
This commit is contained in:
@@ -185,6 +185,18 @@ ShadowMapTraceResult shadow_map_trace_finish(ShadowMapTracingState state)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* If the ray direction `L` is below the horizon defined by N (normalized) at the shading point,
|
||||
* push it just above the horizon so that this ray will never be below it and produce
|
||||
* over-shadowing (since light evaluation already clips the light shape). */
|
||||
vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N)
|
||||
{
|
||||
float distance_to_plan = dot(L, -N);
|
||||
if (distance_to_plan > 0.0) {
|
||||
L += N * (0.01 + distance_to_plan);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Directional Shadow Map Tracing
|
||||
* \{ */
|
||||
@@ -196,8 +208,10 @@ struct ShadowRayDirectional {
|
||||
LightData light;
|
||||
};
|
||||
|
||||
ShadowRayDirectional shadow_ray_generate_directional(
|
||||
LightData light, vec2 random_2d, vec3 lP, vec3 lNg, out bool r_is_above_surface)
|
||||
ShadowRayDirectional shadow_ray_generate_directional(LightData light,
|
||||
vec2 random_2d,
|
||||
vec3 lP,
|
||||
vec3 lNg)
|
||||
{
|
||||
float clip_near = orderedIntBitsToFloat(light.clip_near);
|
||||
float clip_far = orderedIntBitsToFloat(light.clip_far);
|
||||
@@ -210,15 +224,12 @@ ShadowRayDirectional shadow_ray_generate_directional(
|
||||
|
||||
vec3 disk_direction = sample_uniform_cone(sample_cylinder(random_2d),
|
||||
light_sun_data_get(light).shadow_angle);
|
||||
|
||||
disk_direction = shadow_ray_above_horizon_ensure(disk_direction, lNg);
|
||||
|
||||
/* Light shape is 1 unit away from the shading point. */
|
||||
vec4 direction = vec4(disk_direction, -1.0 / z_range);
|
||||
|
||||
r_is_above_surface = dot(lNg, direction.xyz) > 0.0;
|
||||
|
||||
/* TODO(fclem): Bias sample direction above the horizon (or below if transmission).
|
||||
* We don't really care about the shadow shape at such light elevation angles but more about
|
||||
* noise. */
|
||||
|
||||
/* It only make sense to trace where there can be occluder. Clamp by distance to near plane. */
|
||||
direction *= min(light_sun_data_get(light).shadow_trace_distance,
|
||||
dist_to_near_plane / disk_direction.z);
|
||||
@@ -285,8 +296,7 @@ struct ShadowRayPunctual {
|
||||
};
|
||||
|
||||
/* Return ray in UV clip space [0..1]. */
|
||||
ShadowRayPunctual shadow_ray_generate_punctual(
|
||||
LightData light, vec2 random_2d, vec3 lP, vec3 lNg, out bool r_is_above_surface)
|
||||
ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d, vec3 lP, vec3 lNg)
|
||||
{
|
||||
if (light.type == LIGHT_RECT) {
|
||||
random_2d = random_2d * 2.0 - 1.0;
|
||||
@@ -311,11 +321,7 @@ ShadowRayPunctual shadow_ray_generate_punctual(
|
||||
-projection_origin, point_on_light_shape, light_local_data_get(light).shadow_scale);
|
||||
|
||||
direction = point_on_light_shape - lP;
|
||||
r_is_above_surface = dot(direction, lNg) > 0.0;
|
||||
|
||||
/* TODO(fclem): Bias sample direction above the horizon (or below if transmission).
|
||||
* We don't really care about the shadow shape at such light elevation angles but more about
|
||||
* noise. */
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
|
||||
/* Clip the ray to not cross the near plane.
|
||||
* Scale it so that it encompass the whole cube (with a safety margin). */
|
||||
@@ -341,11 +347,7 @@ ShadowRayPunctual shadow_ray_generate_punctual(
|
||||
vec3 point_on_light_shape = right * random_2d.x + up * random_2d.y;
|
||||
|
||||
direction = point_on_light_shape - lP;
|
||||
r_is_above_surface = dot(direction, lNg) > 0.0;
|
||||
|
||||
/* TODO(fclem): Bias sample direction above the horizon (or below if transmission).
|
||||
* We don't really care about the shadow shape at such light elevation angles but more about
|
||||
* noise. */
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
|
||||
/* Clip the ray to not cross the light shape. */
|
||||
float clip_distance = light_spot_data_get(light).radius;
|
||||
@@ -547,34 +549,25 @@ ShadowEvalResult shadow_eval(LightData light,
|
||||
vec3 lNg = light_world_to_local(light, Ng);
|
||||
|
||||
float surface_hit = 0.0;
|
||||
float surface_ray_count = 0.0;
|
||||
for (int ray_index = 0; ray_index < ray_count && ray_index < SHADOW_MAX_RAY; ray_index++) {
|
||||
vec2 random_ray_2d = fract(hammersley_2d(ray_index, ray_count) + random_shadow_3d.xy);
|
||||
|
||||
/* We only consider rays above the surface for shadowing. This is because the LTC evaluation
|
||||
* already accounts for the clipping of the light shape. */
|
||||
bool is_above_surface;
|
||||
|
||||
ShadowMapTraceResult trace;
|
||||
if (is_directional) {
|
||||
ShadowRayDirectional clip_ray = shadow_ray_generate_directional(
|
||||
light, random_ray_2d, lP, lNg, is_above_surface);
|
||||
light, random_ray_2d, lP, lNg);
|
||||
trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
|
||||
}
|
||||
else {
|
||||
ShadowRayPunctual clip_ray = shadow_ray_generate_punctual(
|
||||
light, random_ray_2d, lP, lNg, is_above_surface);
|
||||
ShadowRayPunctual clip_ray = shadow_ray_generate_punctual(light, random_ray_2d, lP, lNg);
|
||||
trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
|
||||
}
|
||||
|
||||
if (is_above_surface != is_transmission) {
|
||||
surface_hit += float(trace.has_hit);
|
||||
surface_ray_count += 1.0;
|
||||
}
|
||||
surface_hit += float(trace.has_hit);
|
||||
}
|
||||
/* Average samples. */
|
||||
ShadowEvalResult result;
|
||||
result.light_visibilty = saturate(1.0 - surface_hit * safe_rcp(surface_ray_count));
|
||||
result.light_visibilty = saturate(1.0 - surface_hit * float(ray_count));
|
||||
result.occluder_distance = 0.0; /* Unused. Could reintroduced if needed. */
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user