1 /* SPDX-FileCopyrightText: 2023 Blender Authors
3 * SPDX-License-Identifier: GPL-2.0-or-later */
6 * Evaluate shadowing using shadow map ray-tracing.
9 #pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
10 #pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
11 #pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
12 #pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
13 #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
14 #pragma BLENDER_REQUIRE(eevee_bxdf_sampling_lib.glsl)
15 #pragma BLENDER_REQUIRE(draw_view_lib.glsl)
16 #pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
18 float shadow_read_depth_at_tilemap_uv(int tilemap_index, vec2 tilemap_uv)
20 /* Prevent out of bound access. Assumes the input is already non negative. */
21 tilemap_uv = min(tilemap_uv, vec2(0.99999));
23 ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
24 /* Using bitwise ops is way faster than integer ops. */
25 const int page_shift = SHADOW_PAGE_LOD;
26 const int page_mask = ~(0xFFFFFFFF << SHADOW_PAGE_LOD);
28 ivec2 tile_coord = texel_coord >> page_shift;
29 ShadowSamplingTile tile = shadow_tile_load(shadow_tilemaps_tx, tile_coord, tilemap_index);
34 /* Shift LOD0 pixels so that they get wrapped at the right position for the given LOD. */
35 /* TODO convert everything to uint to avoid signed int operations. */
36 texel_coord += ivec2(tile.lod_offset << SHADOW_PAGE_LOD);
37 /* Scale to LOD pixels (merge LOD0 pixels together) then mask to get pixel in page. */
38 ivec2 texel_page = (texel_coord >> int(tile.lod)) & page_mask;
39 ivec3 texel = ivec3((ivec2(tile.page.xy) << page_shift) | texel_page, tile.page.z);
41 return uintBitsToFloat(texelFetch(shadow_atlas_tx, texel, 0).r);
44 /* ---------------------------------------------------------------------- */
45 /** \name Shadow Map Tracing loop
48 #define SHADOW_TRACING_INVALID_HISTORY -999.0
50 struct ShadowMapTracingState {
51 /* Receiver Z value at previous valid depth sample. */
52 float receiver_depth_history;
53 /* Occluder Z value at previous valid depth sample. */
54 float occluder_depth_history;
55 /* Ray time at previous valid depth sample. */
56 float ray_time_history;
57 /* Z slope (delta/time) between previous valid sample (N-1) and the one before that (N-2). */
58 float occluder_depth_slope;
59 /* Multiplier and bias to the ray step quickly compute ray time. */
62 /* State of the trace. */
67 ShadowMapTracingState shadow_map_trace_init(int sample_count, float step_offset)
69 ShadowMapTracingState state;
70 state.receiver_depth_history = -1.0;
71 state.occluder_depth_history = SHADOW_TRACING_INVALID_HISTORY;
72 state.ray_time_history = -1.0;
73 state.occluder_depth_slope = 0.0;
74 /* We trace the ray in reverse. From 1.0 (light) to 0.0 (shading point). */
75 state.ray_step_mul = -1.0 / float(sample_count);
76 state.ray_step_bias = 1.0 + step_offset * state.ray_step_mul;
81 struct ShadowTracingSample {
88 * This need to be instantiated for each `ShadowRay*` type.
89 * This way we can implement `shadow_map_trace_sample` for each type without too much code
91 * Most of the code is wrapped into functions to avoid to debug issues inside macro code.
93 #define SHADOW_MAP_TRACE_FN(ShadowRayType) \
94 ShadowMapTraceResult shadow_map_trace(ShadowRayType ray, int sample_count, float step_offset) \
96 ShadowMapTracingState state = shadow_map_trace_init(sample_count, step_offset); \
97 for (int i = 0; (i <= sample_count) && (i <= SHADOW_MAX_STEP) && (state.hit == false); i++) { \
98 /* Saturate to always cover the shading point position when i == sample_count. */ \
99 state.ray_time = square(saturate(float(i) * state.ray_step_mul + state.ray_step_bias)); \
101 ShadowTracingSample samp = shadow_map_trace_sample(state, ray); \
103 shadow_map_trace_hit_check(state, samp); \
105 return shadow_map_trace_finish(state); \
109 * We trace from a point on the light towards the shading point.
111 * This reverse tracing allows to approximate the geometry behind occluders while minimizing
114 void shadow_map_trace_hit_check(inout ShadowMapTracingState state, ShadowTracingSample samp)
116 /* Skip empty tiles since they do not contain actual depth information.
117 * Not doing so would change the z gradient history. */
118 if (samp.skip_sample) {
121 /* For the first sample, regular depth compare since we do not have history values. */
122 if (state.occluder_depth_history == SHADOW_TRACING_INVALID_HISTORY) {
123 if (samp.occluder_depth < samp.receiver_depth) {
127 state.occluder_depth_history = samp.occluder_depth;
128 state.receiver_depth_history = samp.receiver_depth;
129 state.ray_time_history = state.ray_time;
132 /* Delta between previous valid sample. */
133 float ray_depth_delta = samp.receiver_depth - state.receiver_depth_history;
134 /* Delta between previous valid sample not occluding the ray. */
135 float time_delta = state.ray_time - state.ray_time_history;
136 /* Arbitrary increase the threshold to avoid missing occluders because of precision issues.
137 * Increasing the threshold inflates the occluders. */
138 float compare_threshold = abs(ray_depth_delta) * 1.05;
139 /* Find out if the ray step is behind an occluder.
140 * To be consider behind (and ignore the occluder), the occluder must not be cross the ray.
141 * Use the full delta ray depth as threshold to make sure to not miss any occluder. */
142 bool is_behind = samp.occluder_depth < (samp.receiver_depth - compare_threshold);
145 /* Use last known valid occluder Z value and extrapolate to the sample position. */
146 samp.occluder_depth = state.occluder_depth_history + state.occluder_depth_slope * time_delta;
147 /* Intersection test will be against the extrapolated last known occluder. */
150 /* Compute current occluder slope and record history for when the ray goes behind a surface. */
151 state.occluder_depth_slope = (samp.occluder_depth - state.occluder_depth_history) / time_delta;
152 state.occluder_depth_slope = clamp(state.occluder_depth_slope, -100.0, 100.0);
153 state.occluder_depth_history = samp.occluder_depth;
154 state.ray_time_history = state.ray_time;
155 /* Intersection test will be against the current sample's occluder. */
158 if (samp.occluder_depth < samp.receiver_depth) {
159 state.occluder_depth_history = samp.occluder_depth;
163 /* No intersection. */
164 state.receiver_depth_history = samp.receiver_depth;
167 struct ShadowMapTraceResult {
169 float occluder_depth;
172 ShadowMapTraceResult shadow_map_trace_finish(ShadowMapTracingState state)
174 ShadowMapTraceResult result;
176 result.occluder_depth = state.occluder_depth_history;
177 result.has_hit = true;
180 result.occluder_depth = 0.0;
181 result.has_hit = false;
188 /* If the ray direction `L` is below the horizon defined by N (normalized) at the shading point,
189 * push it just above the horizon so that this ray will never be below it and produce
190 * over-shadowing (since light evaluation already clips the light shape). */
191 vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N)
193 float distance_to_plan = dot(L, -N);
194 if (distance_to_plan > 0.0) {
195 L += N * (0.01 + distance_to_plan);
200 /* ---------------------------------------------------------------------- */
201 /** \name Directional Shadow Map Tracing
204 struct ShadowRayDirectional {
205 /* Ray in local translated coordinate, with depth in [0..1] range in W component. */
211 ShadowRayDirectional shadow_ray_generate_directional(LightData light,
216 float clip_near = orderedIntBitsToFloat(light.clip_near);
217 float clip_far = orderedIntBitsToFloat(light.clip_far);
218 /* Assumed to be non-null. */
219 float z_range = clip_far - clip_near;
220 float dist_to_near_plane = -lP.z - clip_near;
222 /* `lP` is supposed to be in light rotated space. But not translated. */
223 vec4 origin = vec4(lP, dist_to_near_plane / z_range);
225 vec3 disk_direction = sample_uniform_cone(sample_cylinder(random_2d),
226 light_sun_data_get(light).shadow_angle);
228 disk_direction = shadow_ray_above_horizon_ensure(disk_direction, lNg);
230 /* Light shape is 1 unit away from the shading point. */
231 vec4 direction = vec4(disk_direction, -1.0 / z_range);
233 /* It only make sense to trace where there can be occluder. Clamp by distance to near plane. */
234 direction *= min(light_sun_data_get(light).shadow_trace_distance,
235 dist_to_near_plane / disk_direction.z);
237 ShadowRayDirectional ray;
239 ray.direction = direction;
244 ShadowTracingSample shadow_map_trace_sample(ShadowMapTracingState state,
245 inout ShadowRayDirectional ray)
247 /* Ray position is ray local position with origin at light origin. */
248 vec4 ray_pos = ray.origin + ray.direction * state.ray_time;
250 int level = shadow_directional_level(ray.light, ray_pos.xyz - ray.light._position);
251 /* This difference needs to be less than 32 for the later shift to be valid.
252 * This is ensured by ShadowDirectional::clipmap_level_range(). */
253 int level_relative = level - light_sun_data_get(ray.light).clipmap_lod_min;
255 int lod_relative = (ray.light.type == LIGHT_SUN_ORTHO) ?
256 light_sun_data_get(ray.light).clipmap_lod_min :
259 vec2 clipmap_origin = light_sun_data_get(ray.light).clipmap_origin;
260 vec2 clipmap_pos = ray_pos.xy - clipmap_origin;
261 vec2 tilemap_uv = clipmap_pos * exp2(-float(lod_relative)) + 0.5;
263 /* Compute offset in tile. */
264 ivec2 clipmap_offset = shadow_decompress_grid_offset(
266 light_sun_data_get(ray.light).clipmap_base_offset_neg,
267 light_sun_data_get(ray.light).clipmap_base_offset_pos,
269 /* Translate tilemap UVs to its origin. */
270 tilemap_uv -= vec2(clipmap_offset) / float(SHADOW_TILEMAP_RES);
271 /* Clamp to avoid out of tilemap access. */
272 tilemap_uv = saturate(tilemap_uv);
274 ShadowTracingSample samp;
275 samp.receiver_depth = ray_pos.w;
276 samp.occluder_depth = shadow_read_depth_at_tilemap_uv(ray.light.tilemap_index + level_relative,
278 samp.skip_sample = (samp.occluder_depth == -1.0);
282 SHADOW_MAP_TRACE_FN(ShadowRayDirectional)
286 /* ---------------------------------------------------------------------- */
287 /** \name Punctual Shadow Map Tracing
290 struct ShadowRayPunctual {
291 /* Ray in tile-map normalized coordinates [0..1]. */
294 /* Tile-map to sample. */
298 /* Return ray in UV clip space [0..1]. */
299 ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d, vec3 lP, vec3 lNg)
301 if (light.type == LIGHT_RECT) {
302 random_2d = random_2d * 2.0 - 1.0;
305 random_2d = sample_disk(random_2d);
308 float clip_far = intBitsToFloat(light.clip_far);
309 float clip_near = intBitsToFloat(light.clip_near);
310 float clip_side = light_local_data_get(light).clip_side;
312 /* TODO(fclem): 3D shift for jittered soft shadows. */
313 vec3 projection_origin = vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift);
315 if (is_area_light(light.type)) {
316 random_2d *= light_area_data_get(light).size;
318 vec3 point_on_light_shape = vec3(random_2d, 0.0);
319 /* Progressively blend the shape back to the projection origin. */
320 point_on_light_shape = mix(
321 -projection_origin, point_on_light_shape, light_local_data_get(light).shadow_scale);
323 direction = point_on_light_shape - lP;
324 direction = shadow_ray_above_horizon_ensure(direction, lNg);
326 /* Clip the ray to not cross the near plane.
327 * Scale it so that it encompass the whole cube (with a safety margin). */
328 float clip_distance = clip_near + 0.001;
329 float ray_length = max(abs(direction.x), max(abs(direction.y), abs(direction.z)));
330 direction *= saturate((ray_length - clip_distance) / ray_length);
334 vec3 L = normalize_and_get_length(lP, dist);
335 /* Disk rotated towards light vector. */
337 make_orthonormal_basis(L, right, up);
339 float shape_radius = light_spot_data_get(light).radius;
340 if (is_sphere_light(light.type)) {
341 /* FIXME(weizhen): this is not well-defined when `dist < light.spot.radius`. */
342 shape_radius = light_sphere_disk_radius(shape_radius, dist);
344 random_2d *= shape_radius;
346 random_2d *= light_local_data_get(light).shadow_scale;
347 vec3 point_on_light_shape = right * random_2d.x + up * random_2d.y;
349 direction = point_on_light_shape - lP;
350 direction = shadow_ray_above_horizon_ensure(direction, lNg);
352 /* Clip the ray to not cross the light shape. */
353 float clip_distance = light_spot_data_get(light).radius;
354 direction *= saturate((dist - clip_distance) / dist);
357 /* Apply shadow origin shift. */
358 vec3 local_ray_start = lP + projection_origin;
359 vec3 local_ray_end = local_ray_start + direction;
361 /* Use an offset in the ray direction to jitter which face is traced.
362 * This helps hiding some harsh discontinuity. */
363 int face_id = shadow_punctual_face_index_get(local_ray_start + direction * 0.5);
364 /* Local Light Space > Face Local (View) Space. */
365 vec3 view_ray_start = shadow_punctual_local_position_to_face_local(face_id, local_ray_start);
366 vec3 view_ray_end = shadow_punctual_local_position_to_face_local(face_id, local_ray_end);
368 /* Face Local (View) Space > Clip Space. */
369 /* TODO: Could be simplified since frustum is completely symmetrical. */
370 mat4 winmat = projection_perspective(
371 -clip_side, clip_side, -clip_side, clip_side, clip_near, clip_far);
372 vec3 clip_ray_start = project_point(winmat, view_ray_start);
373 vec3 clip_ray_end = project_point(winmat, view_ray_end);
374 /* Clip Space > UV Space. */
375 vec3 uv_ray_start = clip_ray_start * 0.5 + 0.5;
376 vec3 uv_ray_end = clip_ray_end * 0.5 + 0.5;
377 /* Compute the ray again. */
378 ShadowRayPunctual ray;
379 ray.origin = uv_ray_start;
380 ray.direction = uv_ray_end - uv_ray_start;
381 ray.tilemap_index = light.tilemap_index + face_id;
385 ShadowTracingSample shadow_map_trace_sample(ShadowMapTracingState state,
386 inout ShadowRayPunctual ray)
388 vec3 ray_pos = ray.origin + ray.direction * state.ray_time;
389 vec2 tilemap_uv = saturate(ray_pos.xy);
391 ShadowTracingSample samp;
392 samp.receiver_depth = ray_pos.z;
393 samp.occluder_depth = shadow_read_depth_at_tilemap_uv(ray.tilemap_index, tilemap_uv);
394 samp.skip_sample = (samp.occluder_depth == -1.0);
398 SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
402 /* ---------------------------------------------------------------------- */
403 /** \name Shadow Evaluation
406 /* Compute the world space offset of the shading position required for
407 * stochastic percentage closer filtering of shadow-maps. */
408 vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng, vec2 random)
410 if (light.pcf_radius <= 0.001) {
415 vec3 L = light_vector_get(light, is_directional, P).L;
416 if (dot(L, Ng) < 0.001) {
417 /* Don't apply PCF to almost perpendicular,
418 * since we can't project the offset to the surface. */
422 ShadowSampleParams params;
423 if (is_directional) {
424 params = shadow_directional_sample_params_get(shadow_tilemaps_tx, light, P);
427 params = shadow_punctual_sample_params_get(light, P);
429 ShadowSamplingTile tile = shadow_tile_data_get(shadow_tilemaps_tx, params);
430 if (!tile.is_valid) {
434 /* Compute the shadow-map tangent-bitangent matrix. */
436 float uv_offset = 1.0 / float(SHADOW_MAP_MAX_RES);
438 if (is_directional) {
439 TP = shadow_directional_reconstruct_position(
440 params, light, params.uv + vec3(uv_offset, 0.0, 0.0));
441 BP = shadow_directional_reconstruct_position(
442 params, light, params.uv + vec3(0.0, uv_offset, 0.0));
445 mat4 wininv = shadow_punctual_projection_perspective_inverse(light);
446 TP = shadow_punctual_reconstruct_position(
447 params, wininv, light, params.uv + vec3(uv_offset, 0.0, 0.0));
448 BP = shadow_punctual_reconstruct_position(
449 params, wininv, light, params.uv + vec3(0.0, uv_offset, 0.0));
452 /* TODO: Use a mat2x3 (Currently not supported by the Metal backend). */
453 mat3 TBN = mat3(TP - P, BP - P, Ng);
455 /* Compute the actual offset. */
457 vec2 pcf_offset = random * 2.0 - 1.0;
458 pcf_offset *= light.pcf_radius;
460 /* Scale the offset based on shadow LOD. */
461 if (is_directional) {
462 vec3 lP = light_world_to_local(light, P);
463 float level = shadow_directional_level_fractional(light, lP - light._position);
464 float pcf_scale = mix(0.5, 1.0, fract(level));
465 pcf_offset *= pcf_scale;
468 bool is_perspective = drw_view_is_perspective();
469 float dist_to_cam = distance(P, drw_view_position());
470 float footprint_ratio = shadow_punctual_footprint_ratio(
471 light, P, is_perspective, dist_to_cam, uniform_buf.shadow.tilemap_projection_ratio);
472 float lod = -log2(footprint_ratio) + light.lod_bias;
473 lod = clamp(lod, 0.0, float(SHADOW_TILEMAP_LOD));
474 float pcf_scale = exp2(lod);
475 pcf_offset *= pcf_scale;
478 vec3 ws_offset = TBN * vec3(pcf_offset, 0.0);
479 vec3 offset_P = P + ws_offset;
481 /* Project the offset position into the surface */
484 /* Workaround for a bug in the Nvidia shader compiler.
485 * If we don't compute L here again, it breaks shadows on reflection probes. */
486 L = light_vector_get(light, is_directional, P).L;
489 if (abs(dot(Ng, L)) > 0.999) {
493 offset_P = line_plane_intersect(offset_P, L, P, Ng);
494 ws_offset = offset_P - P;
496 if (dot(ws_offset, L) < 0.0) {
497 /* Project the offset position into the perpendicular plane, since it's closer to the light
498 * (avoids overshadowing at geometry angles). */
499 vec3 perpendicular_plane_normal = cross(Ng, normalize(cross(Ng, L)));
500 offset_P = line_plane_intersect(offset_P, L, P, perpendicular_plane_normal);
501 ws_offset = offset_P - P;
508 * Evaluate shadowing by casting rays toward the light direction.
510 ShadowEvalResult shadow_eval(LightData light,
511 const bool is_directional,
512 const bool is_transmission,
518 #ifdef EEVEE_SAMPLING_DATA
519 # ifdef GPU_FRAGMENT_SHADER
520 vec2 pixel = floor(gl_FragCoord.xy);
521 # elif defined(GPU_COMPUTE_SHADER)
522 vec2 pixel = vec2(gl_GlobalInvocationID.xy);
524 vec3 blue_noise_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
525 vec3 random_shadow_3d = blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U);
526 vec2 random_pcf_2d = fract(blue_noise_3d.xy + sampling_rng_2D_get(SAMPLING_SHADOW_X));
527 float normal_offset = uniform_buf.shadow.normal_bias;
529 /* Case of surfel light eval. */
530 vec3 random_shadow_3d = vec3(0.5);
531 vec2 random_pcf_2d = vec2(0.0);
532 /* TODO(fclem): Parameter on irradiance volumes? */
533 float normal_offset = 0.02;
536 /* We want to bias inside the object for transmission to go through the object itself. */
537 normal_offset = is_transmission ? -normal_offset : normal_offset;
539 P += shadow_pcf_offset(light, is_directional, P, Ng, random_pcf_2d);
541 /* Avoid self intersection. */
542 P = offset_ray(P, Ng);
543 /* The above offset isn't enough in most situation. Still add a bigger bias. */
544 /* TODO(fclem): Scale based on depth. */
545 P += Ng * normal_offset;
547 vec3 lP = is_directional ? light_world_to_local(light, P) :
548 light_world_to_local(light, P - light._position);
549 vec3 lNg = light_world_to_local(light, Ng);
551 float surface_hit = 0.0;
552 for (int ray_index = 0; ray_index < ray_count && ray_index < SHADOW_MAX_RAY; ray_index++) {
553 vec2 random_ray_2d = fract(hammersley_2d(ray_index, ray_count) + random_shadow_3d.xy);
555 ShadowMapTraceResult trace;
556 if (is_directional) {
557 ShadowRayDirectional clip_ray = shadow_ray_generate_directional(
558 light, random_ray_2d, lP, lNg);
559 trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
562 ShadowRayPunctual clip_ray = shadow_ray_generate_punctual(light, random_ray_2d, lP, lNg);
563 trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
566 surface_hit += float(trace.has_hit);
568 /* Average samples. */
569 ShadowEvalResult result;
570 result.light_visibilty = saturate(1.0 - surface_hit * float(ray_count));
571 result.occluder_distance = 0.0; /* Unused. Could reintroduced if needed. */