EEVEE-Next: Improve shadow tracing
The goal of this PR is to remove the per face, NDC space tracing for shadow maps. This requires custom occluder extrapolation but in return removes quite a lot of complexity in other areas, namely: - No more per face transform before the tracing - No more per face jittering (fix #119565) - No more frustum padding (increased maximum precision) - Better use of 32bit precision shadow map - Fix #121343 This improve softness at relatively low step count (default 6 is better) and reduces light leaking at very low sample count (sharper). It makes it more intuitive now that higher sample count is smoother. Pull Request: https://projects.blender.org/blender/blender/pulls/121317
This commit is contained in:
committed by
Clément Foucault
parent
18cccafeaf
commit
87d164c56b
@@ -1237,11 +1237,6 @@ struct ShadowTileMapData {
|
||||
float half_size;
|
||||
/** Offset in local space to the tilemap center in world units. Used for directional winmat. */
|
||||
float2 center_offset;
|
||||
/** Filter radius of the light in pixels. */
|
||||
float filter_radius;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16)
|
||||
|
||||
@@ -1259,9 +1254,10 @@ struct ShadowRenderView {
|
||||
float clip_distance_inv;
|
||||
/* Viewport to submit the geometry of this tilemap view to. */
|
||||
uint viewport_index;
|
||||
/* Filter radius for this view. */
|
||||
float filter_radius;
|
||||
uint _pad1;
|
||||
/* True if comming from a sun light shadow. */
|
||||
bool32_t is_directionnal;
|
||||
/* If directionnal, distance along the negative Z axis of the near clip in view space. */
|
||||
float clip_near;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowRenderView, 16)
|
||||
|
||||
|
||||
@@ -30,19 +30,15 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
float lod_bias_,
|
||||
float filter_radius_,
|
||||
eShadowProjectionType projection_type_)
|
||||
{
|
||||
if ((projection_type != projection_type_) || (level != clipmap_level) ||
|
||||
(filter_radius != filter_radius_))
|
||||
{
|
||||
if ((projection_type != projection_type_) || (level != clipmap_level)) {
|
||||
set_dirty();
|
||||
}
|
||||
projection_type = projection_type_;
|
||||
level = clipmap_level;
|
||||
light_type = eLightType::LIGHT_SUN;
|
||||
is_area_side = false;
|
||||
filter_radius = filter_radius_;
|
||||
|
||||
if (grid_shift == int2(0)) {
|
||||
/* Only replace shift if it is not already dirty. */
|
||||
@@ -82,7 +78,6 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
float side_,
|
||||
float shift,
|
||||
eCubeFace face,
|
||||
float filter_radius_,
|
||||
float lod_bias_)
|
||||
{
|
||||
if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) {
|
||||
@@ -95,13 +90,10 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
light_type = light_type_;
|
||||
is_area_side = is_area_light(light_type) && (face != eCubeFace::Z_NEG);
|
||||
|
||||
if ((clip_near != near_) || (filter_radius != filter_radius_) || (clip_far != far_) ||
|
||||
(half_size != side_))
|
||||
{
|
||||
if ((clip_near != near_) || (clip_far != far_) || (half_size != side_)) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
filter_radius = filter_radius_;
|
||||
clip_near = near_;
|
||||
clip_far = far_;
|
||||
area_shift = shift;
|
||||
@@ -274,100 +266,15 @@ void ShadowPunctual::release_excess_tilemaps()
|
||||
}
|
||||
|
||||
void ShadowPunctual::compute_projection_boundaries(eLightType light_type,
|
||||
float light_radius,
|
||||
float shadow_radius,
|
||||
float max_lit_distance,
|
||||
float &near,
|
||||
float &far,
|
||||
float &side,
|
||||
float &back_shift)
|
||||
{
|
||||
/*
|
||||
* In order to make sure we can trace any ray in its entirety using a single tile-map, we have
|
||||
* to make sure that the tile-map cover all potential occluder that can intersect any ray shot
|
||||
* in this particular shadow quadrant.
|
||||
*
|
||||
* To this end, we inflate the tile-map perspective sides to make sure the
|
||||
* tile-map frustum starts where the rays cannot go.
|
||||
*
|
||||
* We are interesting in finding `I` the new origin and `n` the new near plane distances.
|
||||
*
|
||||
* I .... Intersection between tangent and
|
||||
* /| projection center axis
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / |
|
||||
* / ....|
|
||||
* / .... |
|
||||
* / ... |
|
||||
* /. |
|
||||
* / |
|
||||
* Tangent to light shape .... T\--------------N
|
||||
* / --\ Beta |
|
||||
* / -\ |
|
||||
* / --\ |
|
||||
* /. --\ |
|
||||
* / . -\ |
|
||||
* / . Alpha -O .... Light center
|
||||
* / . /-/ |
|
||||
* Inflated side / . /--- -/ |
|
||||
* . / . /---- --/ |
|
||||
* . / /---- . --/ |
|
||||
* /-------------/------------X .... Desired near plane (inscribed cube)
|
||||
* /---- --/ .. |
|
||||
* /---- / --/ ... |
|
||||
* /---- / --/ .... |
|
||||
* / -/ ....| .... Shadow radius
|
||||
* / --/ |
|
||||
* /--/ |
|
||||
* F .... Most distant shadow receiver possible.
|
||||
*
|
||||
* F: The most distant shadowed point at the edge of the 45° cube-face pyramid.
|
||||
* O: The light origin.
|
||||
* T: The tangent to the circle of radius `radius` centered at the origin and passing through F.
|
||||
* I: Intersection between tangent and the projection center axis.
|
||||
* N: The shifted near plane center.
|
||||
* X: Intersection between the near plane and the projection center axis.
|
||||
* Alpha: FOT angle.
|
||||
* Beta: OTN angle.
|
||||
*
|
||||
* NOTE: FTO, ONT and TNI are right angles.
|
||||
*/
|
||||
float cos_alpha = shadow_radius / max_lit_distance;
|
||||
float sin_alpha = sqrt(1.0f - math::square(cos_alpha));
|
||||
float near_shift = M_SQRT2 * shadow_radius * 0.5f * (sin_alpha - cos_alpha);
|
||||
float side_shift = M_SQRT2 * shadow_radius * 0.5f * (sin_alpha + cos_alpha);
|
||||
float origin_shift = M_SQRT2 * shadow_radius / (sin_alpha - cos_alpha);
|
||||
|
||||
float min_near = (max_lit_distance / 4000.0f) / M_SQRT3;
|
||||
|
||||
if (is_area_light(light_type)) {
|
||||
/* Make near plane be inside the inscribed cube of the shadow sphere. */
|
||||
near = max_ff(shadow_radius / M_SQRT3, min_near);
|
||||
/* Subtract min_near to make the shadow center match the light center if there is no shadow
|
||||
* tracing required. This avoid light leaking issues near the light plane caused by the
|
||||
* shadow discard clipping. */
|
||||
back_shift = (near - min_near);
|
||||
}
|
||||
else {
|
||||
/* Make near plane be inside the inscribed cube of the light sphere. */
|
||||
near = max_ff(light_radius / M_SQRT3, min_near);
|
||||
back_shift = 0.0f;
|
||||
}
|
||||
|
||||
far = max_lit_distance;
|
||||
if (shadow_radius > 1e-5f) {
|
||||
side = ((side_shift / (origin_shift - near_shift)) * (origin_shift + near));
|
||||
}
|
||||
else {
|
||||
side = near;
|
||||
}
|
||||
near = side = (max_lit_distance / 4000.0f) / M_SQRT3;
|
||||
back_shift = 0.0f;
|
||||
}
|
||||
|
||||
void ShadowPunctual::end_sync(Light &light, float lod_bias)
|
||||
@@ -375,8 +282,7 @@ void ShadowPunctual::end_sync(Light &light, float lod_bias)
|
||||
ShadowTileMapPool &tilemap_pool = shadows_.tilemap_pool;
|
||||
|
||||
float side, near, far, shift;
|
||||
compute_projection_boundaries(
|
||||
light.type, light_radius_, shadow_radius_, max_distance_, near, far, side, shift);
|
||||
compute_projection_boundaries(light.type, max_distance_, near, far, side, shift);
|
||||
|
||||
float4x4 obmat_tmp = light.object_to_world;
|
||||
|
||||
@@ -385,21 +291,20 @@ void ShadowPunctual::end_sync(Light &light, float lod_bias)
|
||||
tilemaps_.append(tilemap_pool.acquire());
|
||||
}
|
||||
|
||||
tilemaps_[Z_NEG]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, Z_NEG, light.pcf_radius, lod_bias);
|
||||
tilemaps_[Z_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_NEG, lod_bias);
|
||||
if (tilemaps_needed_ >= 5) {
|
||||
tilemaps_[X_POS]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, X_POS, light.pcf_radius, lod_bias);
|
||||
light.type, obmat_tmp, near, far, side, shift, X_POS, lod_bias);
|
||||
tilemaps_[X_NEG]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, X_NEG, light.pcf_radius, lod_bias);
|
||||
light.type, obmat_tmp, near, far, side, shift, X_NEG, lod_bias);
|
||||
tilemaps_[Y_POS]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, Y_POS, light.pcf_radius, lod_bias);
|
||||
light.type, obmat_tmp, near, far, side, shift, Y_POS, lod_bias);
|
||||
tilemaps_[Y_NEG]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, Y_NEG, light.pcf_radius, lod_bias);
|
||||
light.type, obmat_tmp, near, far, side, shift, Y_NEG, lod_bias);
|
||||
}
|
||||
if (tilemaps_needed_ == 6) {
|
||||
tilemaps_[Z_POS]->sync_cubeface(
|
||||
light.type, obmat_tmp, near, far, side, shift, Z_POS, light.pcf_radius, lod_bias);
|
||||
light.type, obmat_tmp, near, far, side, shift, Z_POS, lod_bias);
|
||||
}
|
||||
|
||||
light.tilemap_index = tilemap_pool.tilemaps_data.size();
|
||||
@@ -544,8 +449,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
||||
/* Equal spacing between cascades layers since we want uniform shadow density. */
|
||||
int2 level_offset = origin_offset +
|
||||
shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i);
|
||||
tilemap->sync_orthographic(
|
||||
object_mat_, level_offset, level, 0.0f, light.pcf_radius, SHADOW_PROJECTION_CASCADE);
|
||||
tilemap->sync_orthographic(object_mat_, level_offset, level, 0.0f, SHADOW_PROJECTION_CASCADE);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -612,7 +516,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
|
||||
int2 level_offset = int2(math::round(light_space_camera_position / tile_size));
|
||||
|
||||
tilemap->sync_orthographic(
|
||||
object_mat_, level_offset, level, lod_bias, light.pcf_radius, SHADOW_PROJECTION_CLIPMAP);
|
||||
object_mat_, level_offset, level, lod_bias, SHADOW_PROJECTION_CLIPMAP);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -1460,15 +1364,18 @@ void ShadowModule::set_view(View &view, int2 extent)
|
||||
{
|
||||
/* Depth is cleared to 0 for TBDR optimization. */
|
||||
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_DONT_CARE, {0.0f, 0.0f, 0.0f, 0.0f}},
|
||||
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_DONT_CARE, {1.0f, 1.0f, 1.0f, 1.0f}},
|
||||
{GPU_LOADACTION_CLEAR,
|
||||
GPU_STOREACTION_DONT_CARE,
|
||||
{FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX}},
|
||||
});
|
||||
}
|
||||
else if (shadow_depth_fb_tx_.is_valid()) {
|
||||
GPU_framebuffer_bind_ex(
|
||||
render_fb_,
|
||||
{
|
||||
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_DONT_CARE, {1.0f, 1.0f, 1.0f, 1.0f}},
|
||||
});
|
||||
GPU_framebuffer_bind_ex(render_fb_,
|
||||
{
|
||||
{GPU_LOADACTION_CLEAR,
|
||||
GPU_STOREACTION_DONT_CARE,
|
||||
{FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX}},
|
||||
});
|
||||
}
|
||||
else {
|
||||
GPU_framebuffer_bind(render_fb_);
|
||||
|
||||
@@ -101,7 +101,6 @@ struct ShadowTileMap : public ShadowTileMapData {
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
float lod_bias_,
|
||||
float filter_radius,
|
||||
eShadowProjectionType projection_type_);
|
||||
|
||||
void sync_cubeface(eLightType light_type_,
|
||||
@@ -111,7 +110,6 @@ struct ShadowTileMap : public ShadowTileMapData {
|
||||
float side,
|
||||
float shift,
|
||||
eCubeFace face,
|
||||
float filter_radius,
|
||||
float lod_bias_);
|
||||
|
||||
void debug_draw() const;
|
||||
@@ -456,8 +454,6 @@ class ShadowPunctual : public NonCopyable, NonMovable {
|
||||
* quadrant.
|
||||
*/
|
||||
void compute_projection_boundaries(eLightType light_type,
|
||||
float light_radius,
|
||||
float shadow_radius,
|
||||
float max_lit_distance,
|
||||
float &near,
|
||||
float &far,
|
||||
|
||||
@@ -50,11 +50,10 @@ void thickness_from_shadow_single(uint l_idx,
|
||||
/* Inverting this bias means we will over estimate the distance. Which removes some artifacts. */
|
||||
P_offset -= texel_radius * shadow_pcf_offset(lv.L, Ng, pcf_random);
|
||||
|
||||
ShadowEvalResult result = shadow_sample(
|
||||
float occluder_delta = shadow_sample(
|
||||
is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P_offset);
|
||||
|
||||
if (result.light_visibilty == 0.0) {
|
||||
float hit_distance = result.occluder_distance;
|
||||
if (occluder_delta > 0.0) {
|
||||
float hit_distance = abs(occluder_delta);
|
||||
/* Add back the amount of offset we added to the original position.
|
||||
* This avoids self shadowing issue. */
|
||||
hit_distance += (normal_offset + 1.0) * texel_radius;
|
||||
|
||||
@@ -14,7 +14,6 @@ void main()
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index));
|
||||
shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
@@ -59,8 +58,10 @@ void main()
|
||||
#endif
|
||||
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_clip.vector = shadow_clip_vector_get(drw_point_world_to_view(interp.P),
|
||||
render_view_buf[drw_view_id].clip_distance_inv);
|
||||
vec3 vs_P = drw_point_world_to_view(interp.P);
|
||||
ShadowRenderView view = render_view_buf[drw_view_id];
|
||||
shadow_clip.position = shadow_position_vector_get(vs_P, view);
|
||||
shadow_clip.vector = shadow_clip_vector_get(vs_P, view.clip_distance_inv);
|
||||
#endif
|
||||
|
||||
gl_Position = drw_point_world_to_homogenous(interp.P);
|
||||
|
||||
@@ -14,7 +14,6 @@ void main()
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index));
|
||||
shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
@@ -59,7 +58,9 @@ void main()
|
||||
#endif
|
||||
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_clip.vector = shadow_clip_vector_get(drw_point_world_to_view(interp.P),
|
||||
render_view_buf[drw_view_id].clip_distance_inv);
|
||||
vec3 vs_P = drw_point_world_to_view(interp.P);
|
||||
ShadowRenderView view = render_view_buf[drw_view_id];
|
||||
shadow_clip.position = shadow_position_vector_get(vs_P, view);
|
||||
shadow_clip.vector = shadow_clip_vector_get(vs_P, view.clip_distance_inv);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ void main()
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index));
|
||||
shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
@@ -41,8 +40,10 @@ void main()
|
||||
#endif
|
||||
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_clip.vector = shadow_clip_vector_get(drw_point_world_to_view(interp.P),
|
||||
render_view_buf[drw_view_id].clip_distance_inv);
|
||||
vec3 vs_P = drw_point_world_to_view(interp.P);
|
||||
ShadowRenderView view = render_view_buf[drw_view_id];
|
||||
shadow_clip.position = shadow_position_vector_get(vs_P, view);
|
||||
shadow_clip.vector = shadow_clip_vector_get(vs_P, view.clip_distance_inv);
|
||||
#endif
|
||||
|
||||
gl_Position = drw_point_world_to_homogenous(interp.P);
|
||||
|
||||
@@ -15,7 +15,6 @@ void main()
|
||||
DRW_VIEW_FROM_RESOURCE_ID;
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index));
|
||||
shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius;
|
||||
#endif
|
||||
|
||||
init_interface();
|
||||
@@ -53,8 +52,10 @@ void main()
|
||||
#endif
|
||||
|
||||
#ifdef MAT_SHADOW
|
||||
shadow_clip.vector = shadow_clip_vector_get(drw_point_world_to_view(interp.P),
|
||||
render_view_buf[drw_view_id].clip_distance_inv);
|
||||
vec3 vs_P = drw_point_world_to_view(interp.P);
|
||||
ShadowRenderView view = render_view_buf[drw_view_id];
|
||||
shadow_clip.position = shadow_position_vector_get(vs_P, view);
|
||||
shadow_clip.vector = shadow_clip_vector_get(vs_P, view.clip_distance_inv);
|
||||
#endif
|
||||
|
||||
gl_Position = drw_point_world_to_homogenous(interp.P);
|
||||
|
||||
@@ -181,17 +181,16 @@ void light_eval_single(uint l_idx,
|
||||
|
||||
float shadow = 1.0;
|
||||
if (light.tilemap_index != LIGHT_NO_SHADOW) {
|
||||
ShadowEvalResult result = shadow_eval(light,
|
||||
is_directional,
|
||||
is_transmission,
|
||||
is_translucent_with_thickness,
|
||||
thickness,
|
||||
P,
|
||||
Ng,
|
||||
lv.L,
|
||||
ray_count,
|
||||
ray_step_count);
|
||||
shadow = result.light_visibilty;
|
||||
shadow = shadow_eval(light,
|
||||
is_directional,
|
||||
is_transmission,
|
||||
is_translucent_with_thickness,
|
||||
thickness,
|
||||
P,
|
||||
Ng,
|
||||
lv.L,
|
||||
ray_count,
|
||||
ray_step_count);
|
||||
}
|
||||
|
||||
if (is_translucent_with_thickness) {
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
/** Control the scaling of the tile-map splat. */
|
||||
const float pixel_scale = 4.0;
|
||||
|
||||
ShadowSamplingTile shadow_tile_data_get(usampler2D tilemaps_tx, ShadowCoordinates coord)
|
||||
{
|
||||
return shadow_tile_load(tilemaps_tx, coord.tilemap_tile, coord.tilemap_index);
|
||||
}
|
||||
|
||||
vec3 debug_random_color(ivec2 v)
|
||||
{
|
||||
float r = interlieved_gradient_noise(vec2(v), 0.0, 0.0);
|
||||
@@ -69,19 +74,24 @@ vec3 debug_tile_state_color(eLightType type, ShadowSamplingTile tile)
|
||||
SHADOW_TILEMAP_LOD));
|
||||
}
|
||||
|
||||
ShadowSampleParams debug_shadow_sample_get(vec3 P, LightData light)
|
||||
ShadowCoordinates debug_coord_get(vec3 P, LightData light)
|
||||
{
|
||||
if (is_sun_light(light.type)) {
|
||||
return shadow_directional_sample_params_get(shadow_tilemaps_tx, light, P);
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
return shadow_directional_coordinates(light, lP);
|
||||
}
|
||||
else {
|
||||
return shadow_punctual_sample_params_get(light, P);
|
||||
vec3 lP = light_world_to_local_point(light, P);
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
return shadow_punctual_coordinates(light, lP, face_id);
|
||||
}
|
||||
}
|
||||
|
||||
ShadowSamplingTile debug_tile_get(vec3 P, LightData light)
|
||||
{
|
||||
return shadow_tile_data_get(shadow_tilemaps_tx, debug_shadow_sample_get(P, light));
|
||||
ShadowCoordinates coord = debug_coord_get(P, light);
|
||||
return shadow_tile_data_get(shadow_tilemaps_tx, coord);
|
||||
}
|
||||
|
||||
LightData debug_light_get()
|
||||
@@ -114,9 +124,9 @@ bool debug_tilemaps(vec3 P, LightData light)
|
||||
if ((px.y < SHADOW_TILEMAP_RES) && (tilemap_index <= light_tilemap_max_get(light))) {
|
||||
#if 1
|
||||
/* Debug values in the tilemap_tx. */
|
||||
ivec2 tilemap_texel = shadow_tile_coord_in_atlas(px, tilemap_index);
|
||||
uvec2 tilemap_texel = shadow_tile_coord_in_atlas(uvec2(px), tilemap_index);
|
||||
ShadowSamplingTile tile = shadow_sampling_tile_unpack(
|
||||
texelFetch(shadow_tilemaps_tx, tilemap_texel, 0).x);
|
||||
texelFetch(shadow_tilemaps_tx, ivec2(tilemap_texel), 0).x);
|
||||
/* Leave 1 px border between tile-maps. */
|
||||
if (!any(equal(ivec2(gl_FragCoord.xy) % (SHADOW_TILEMAP_RES * debug_tile_size_px), ivec2(0))))
|
||||
{
|
||||
@@ -155,9 +165,9 @@ void debug_tile_state(vec3 P, LightData light)
|
||||
|
||||
void debug_atlas_values(vec3 P, LightData light)
|
||||
{
|
||||
ShadowSampleParams samp = debug_shadow_sample_get(P, light);
|
||||
float depth = shadow_read_depth(shadow_atlas_tx, shadow_tilemaps_tx, samp);
|
||||
out_color_add = vec4(float3(depth), 0.0);
|
||||
ShadowCoordinates coord = debug_coord_get(P, light);
|
||||
float depth = shadow_read_depth(shadow_atlas_tx, shadow_tilemaps_tx, coord);
|
||||
out_color_add = vec4((depth == -1) ? vec3(1.0, 0.0, 0.0) : float3(1.0 / depth), 0.0);
|
||||
out_color_mul = vec4(0.5);
|
||||
}
|
||||
|
||||
@@ -170,18 +180,7 @@ void debug_random_tile_color(vec3 P, LightData light)
|
||||
|
||||
void debug_random_tilemap_color(vec3 P, LightData light)
|
||||
{
|
||||
ShadowCoordinates coord;
|
||||
if (is_sun_light(light.type)) {
|
||||
vec3 lP = light_world_to_local(light, P);
|
||||
coord = shadow_directional_coordinates(light, lP);
|
||||
}
|
||||
else {
|
||||
vec3 lP = light_world_to_local(light, P - light_position_get(light));
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
}
|
||||
|
||||
ShadowCoordinates coord = debug_coord_get(P, light);
|
||||
out_color_add = vec4(debug_random_color(ivec2(coord.tilemap_index)), 0) * 0.5;
|
||||
out_color_mul = vec4(0.5);
|
||||
}
|
||||
|
||||
@@ -14,278 +14,74 @@
|
||||
# define SHADOW_ATLAS_TYPE usampler2DArray
|
||||
#endif
|
||||
|
||||
struct ShadowSampleParams {
|
||||
vec3 lP;
|
||||
vec3 uv;
|
||||
int tilemap_index;
|
||||
float z_range;
|
||||
};
|
||||
|
||||
ShadowSamplingTile shadow_tile_data_get(usampler2D tilemaps_tx, ShadowSampleParams params)
|
||||
{
|
||||
/* Prevent out of bound access. Assumes the input is already non negative. */
|
||||
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
|
||||
|
||||
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
|
||||
/* Using bitwise ops is way faster than integer ops. */
|
||||
const int page_shift = SHADOW_PAGE_LOD;
|
||||
|
||||
ivec2 tile_coord = texel_coord >> page_shift;
|
||||
return shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
|
||||
}
|
||||
|
||||
float shadow_read_depth(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
ShadowSampleParams params)
|
||||
ShadowCoordinates coord)
|
||||
{
|
||||
/* Prevent out of bound access. Assumes the input is already non negative. */
|
||||
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
|
||||
|
||||
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
|
||||
/* Using bitwise ops is way faster than integer ops. */
|
||||
const int page_shift = SHADOW_PAGE_LOD;
|
||||
|
||||
ivec2 tile_coord = texel_coord >> page_shift;
|
||||
ShadowSamplingTile tile = shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
|
||||
|
||||
ShadowSamplingTile tile = shadow_tile_load(tilemaps_tx, coord.tilemap_tile, coord.tilemap_index);
|
||||
if (!tile.is_valid) {
|
||||
return -1.0;
|
||||
}
|
||||
/* Using bitwise ops is way faster than integer ops. */
|
||||
const uint page_shift = uint(SHADOW_PAGE_LOD);
|
||||
const uint page_mask = ~(0xFFFFFFFFu << uint(SHADOW_PAGE_LOD));
|
||||
|
||||
int page_mask = ~(0xFFFFFFFF << (SHADOW_PAGE_LOD + int(tile.lod)));
|
||||
ivec2 texel_page = (texel_coord & page_mask) >> int(tile.lod);
|
||||
ivec3 texel = ivec3((ivec2(tile.page.xy) << page_shift) | texel_page, tile.page.z);
|
||||
uvec2 texel = coord.tilemap_texel;
|
||||
/* Shift LOD0 pixels so that they get wrapped at the right position for the given LOD. */
|
||||
texel += uvec2(tile.lod_offset << SHADOW_PAGE_LOD);
|
||||
/* Scale to LOD pixels (merge LOD0 pixels together) then mask to get pixel in page. */
|
||||
uvec2 texel_page = (texel >> tile.lod) & page_mask;
|
||||
texel = (uvec2(tile.page.xy) << page_shift) | texel_page;
|
||||
|
||||
return uintBitsToFloat(texelFetch(atlas_tx, texel, 0).r);
|
||||
return uintBitsToFloat(texelFetch(atlas_tx, ivec3(ivec2(texel), tile.page.z), 0).r);
|
||||
}
|
||||
|
||||
struct ShadowEvalResult {
|
||||
/* Visibility of the light. */
|
||||
float light_visibilty;
|
||||
/* Average occluder distance. In world space linear distance. */
|
||||
float occluder_distance;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Shadow Sampling Functions
|
||||
* \{ */
|
||||
|
||||
mat4x4 shadow_projection_perspective(float side, float near_clip, float far_clip)
|
||||
{
|
||||
float z_delta = far_clip - near_clip;
|
||||
|
||||
mat4x4 mat = mat4x4(1.0);
|
||||
mat[0][0] = near_clip / side;
|
||||
mat[1][1] = near_clip / side;
|
||||
mat[2][0] = 0.0;
|
||||
mat[2][1] = 0.0;
|
||||
mat[2][2] = -(far_clip + near_clip) / z_delta;
|
||||
mat[2][3] = -1.0;
|
||||
mat[3][2] = (-2.0 * near_clip * far_clip) / z_delta;
|
||||
mat[3][3] = 0.0;
|
||||
return mat;
|
||||
}
|
||||
|
||||
mat4x4 shadow_projection_perspective_inverse(float side, float near_clip, float far_clip)
|
||||
{
|
||||
float z_delta = far_clip - near_clip;
|
||||
float d = 2.0 * near_clip * far_clip;
|
||||
|
||||
mat4x4 mat = mat4x4(1.0);
|
||||
mat[0][0] = side / near_clip;
|
||||
mat[1][1] = side / near_clip;
|
||||
mat[2][0] = 0.0;
|
||||
mat[2][1] = 0.0;
|
||||
mat[2][2] = 0.0;
|
||||
mat[2][3] = (near_clip - far_clip) / d;
|
||||
mat[3][2] = -1.0;
|
||||
mat[3][3] = (near_clip + far_clip) / d;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert occluder distance in shadow space to world space distance.
|
||||
* Assuming the occluder is above the shading point in direction to the shadow projection center.
|
||||
*/
|
||||
float shadow_linear_occluder_distance(LightData light,
|
||||
const bool is_directional,
|
||||
vec3 lP,
|
||||
float occluder)
|
||||
{
|
||||
float near = orderedIntBitsToFloat(light.clip_near);
|
||||
float far = orderedIntBitsToFloat(light.clip_far);
|
||||
|
||||
float occluder_z = (is_directional) ? (occluder * (far - near) + near) :
|
||||
((near * far) / (occluder * (near - far) + far));
|
||||
float receiver_z = (is_directional) ? -lP.z : reduce_max(abs(lP));
|
||||
if (!is_directional) {
|
||||
float lP_len = length(lP);
|
||||
return lP_len - lP_len * (occluder_z / receiver_z);
|
||||
}
|
||||
return receiver_z - occluder_z;
|
||||
}
|
||||
|
||||
mat4 shadow_punctual_projection_perspective(LightData light)
|
||||
{
|
||||
/* Face Local (View) Space > Clip Space. */
|
||||
float clip_far = intBitsToFloat(light.clip_far);
|
||||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
return shadow_projection_perspective(clip_side, clip_near, clip_far);
|
||||
}
|
||||
|
||||
mat4 shadow_punctual_projection_perspective_inverse(LightData light)
|
||||
{
|
||||
/* Face Local (View) Space > Clip Space. */
|
||||
float clip_far = intBitsToFloat(light.clip_far);
|
||||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
return shadow_projection_perspective_inverse(clip_side, clip_near, clip_far);
|
||||
}
|
||||
|
||||
vec3 shadow_punctual_reconstruct_position(ShadowSampleParams params,
|
||||
mat4 wininv,
|
||||
LightData light,
|
||||
vec3 uvw)
|
||||
{
|
||||
vec3 clip_P = uvw * 2.0 - 1.0;
|
||||
vec3 lP = project_point(wininv, clip_P);
|
||||
int face_id = params.tilemap_index - light.tilemap_index;
|
||||
lP = shadow_punctual_face_local_to_local_position(face_id, lP);
|
||||
return transform_point(light.object_to_world, lP);
|
||||
}
|
||||
|
||||
ShadowSampleParams shadow_punctual_sample_params_get(LightData light, vec3 P)
|
||||
float shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
vec3 lP = transform_point_inversed(light.object_to_world, P);
|
||||
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
/* Local Light Space > Face Local (View) Space. */
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
mat4 winmat = shadow_punctual_projection_perspective(light);
|
||||
vec3 clip_P = project_point(winmat, lP);
|
||||
/* Clip Space > UV Space. */
|
||||
vec3 uv_P = saturate(clip_P * 0.5 + 0.5);
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
|
||||
ShadowSampleParams result;
|
||||
result.lP = lP;
|
||||
result.uv = uv_P;
|
||||
result.tilemap_index = light.tilemap_index + face_id;
|
||||
result.z_range = 1.0;
|
||||
return result;
|
||||
float radial_dist = shadow_read_depth(atlas_tx, tilemaps_tx, coord);
|
||||
if (radial_dist == -1.0) {
|
||||
return 1e10;
|
||||
}
|
||||
float receiver_dist = length(lP);
|
||||
float occluder_dist = radial_dist;
|
||||
return receiver_dist - occluder_dist;
|
||||
}
|
||||
|
||||
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
float shadow_directional_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
ShadowSampleParams params = shadow_punctual_sample_params_get(light, P);
|
||||
|
||||
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
|
||||
|
||||
ShadowEvalResult result;
|
||||
result.light_visibilty = float(params.uv.z < depth);
|
||||
result.occluder_distance = shadow_linear_occluder_distance(light, false, params.lP, depth);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ShadowDirectionalSampleInfo {
|
||||
float clip_near;
|
||||
float clip_far;
|
||||
int level_relative;
|
||||
int lod_relative;
|
||||
ivec2 clipmap_offset;
|
||||
vec2 clipmap_origin;
|
||||
};
|
||||
|
||||
ShadowDirectionalSampleInfo shadow_directional_sample_info_get(LightData light, vec3 lP)
|
||||
{
|
||||
ShadowDirectionalSampleInfo info;
|
||||
info.clip_near = orderedIntBitsToFloat(light.clip_near);
|
||||
info.clip_far = orderedIntBitsToFloat(light.clip_far);
|
||||
|
||||
int level = shadow_directional_level(light, lP - light_position_get(light));
|
||||
/* This difference needs to be less than 32 for the later shift to be valid.
|
||||
* This is ensured by ShadowDirectional::clipmap_level_range(). */
|
||||
info.level_relative = level - light_sun_data_get(light).clipmap_lod_min;
|
||||
info.lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light_sun_data_get(light).clipmap_lod_min :
|
||||
level;
|
||||
|
||||
info.clipmap_offset = shadow_decompress_grid_offset(
|
||||
light.type,
|
||||
light_sun_data_get(light).clipmap_base_offset_neg,
|
||||
light_sun_data_get(light).clipmap_base_offset_pos,
|
||||
info.level_relative);
|
||||
info.clipmap_origin = light_sun_data_get(light).clipmap_origin;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
vec3 shadow_directional_reconstruct_position(ShadowSampleParams params, LightData light, vec3 uvw)
|
||||
{
|
||||
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, params.lP);
|
||||
|
||||
vec2 tilemap_uv = uvw.xy;
|
||||
tilemap_uv += vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
|
||||
vec2 clipmap_pos = (tilemap_uv - 0.5) / exp2(-float(info.lod_relative));
|
||||
|
||||
vec3 lP;
|
||||
lP.xy = clipmap_pos + info.clipmap_origin;
|
||||
lP.z = (params.uv.z + info.clip_near) * -1.0;
|
||||
|
||||
return transform_direction_transposed(light.object_to_world, lP);
|
||||
}
|
||||
|
||||
ShadowSampleParams shadow_directional_sample_params_get(usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
vec3 lP = transform_direction(light.object_to_world, P);
|
||||
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, lP);
|
||||
|
||||
vec3 lP = transform_direction_transposed(light.object_to_world, P);
|
||||
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
|
||||
|
||||
/* Assumed to be non-null. */
|
||||
float z_range = info.clip_far - info.clip_near;
|
||||
float dist_to_near_plane = -lP.z - info.clip_near;
|
||||
|
||||
vec2 clipmap_pos = lP.xy - info.clipmap_origin;
|
||||
vec2 tilemap_uv = clipmap_pos * exp2(-float(info.lod_relative)) + 0.5;
|
||||
|
||||
/* Translate tilemap UVs to its origin. */
|
||||
tilemap_uv -= vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
|
||||
/* Clamp to avoid out of tilemap access. */
|
||||
tilemap_uv = saturate(tilemap_uv);
|
||||
|
||||
ShadowSampleParams result;
|
||||
result.lP = lP;
|
||||
result.uv = vec3(tilemap_uv, dist_to_near_plane);
|
||||
result.tilemap_index = light.tilemap_index + info.level_relative;
|
||||
result.z_range = z_range;
|
||||
return result;
|
||||
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, coord);
|
||||
if (depth == -1.0) {
|
||||
return 1e10;
|
||||
}
|
||||
/* Use increasing distance from the light. */
|
||||
float receiver_dist = -lP.z - orderedIntBitsToFloat(light.clip_near);
|
||||
float occluder_dist = depth;
|
||||
return receiver_dist - occluder_dist;
|
||||
}
|
||||
|
||||
ShadowEvalResult shadow_directional_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
ShadowSampleParams params = shadow_directional_sample_params_get(tilemaps_tx, light, P);
|
||||
|
||||
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
|
||||
|
||||
ShadowEvalResult result;
|
||||
result.light_visibilty = float(params.uv.z < depth * params.z_range);
|
||||
result.occluder_distance = shadow_linear_occluder_distance(light, true, params.lP, depth);
|
||||
return result;
|
||||
}
|
||||
|
||||
ShadowEvalResult shadow_sample(const bool is_directional,
|
||||
SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
float shadow_sample(const bool is_directional,
|
||||
SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
{
|
||||
if (is_directional) {
|
||||
return shadow_directional_sample_get(atlas_tx, tilemaps_tx, light, P);
|
||||
|
||||
@@ -17,6 +17,5 @@ void main()
|
||||
uvec3 page_co = shadow_page_unpack(page_packed);
|
||||
page_co.xy = page_co.xy * SHADOW_PAGE_RES + gl_GlobalInvocationID.xy;
|
||||
|
||||
/* Clear to FLT_MAX instead of 1 so the far plane doesn't cast shadows onto farther objects. */
|
||||
imageStoreFast(shadow_atlas_img, ivec3(page_co), uvec4(floatBitsToUint(FLT_MAX)));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ shared uint levels_rendered;
|
||||
|
||||
int shadow_tile_offset_lds(ivec2 tile, int lod)
|
||||
{
|
||||
return shadow_tile_offset(tile, 0, lod);
|
||||
return shadow_tile_offset(uvec2(tile), 0, lod);
|
||||
}
|
||||
|
||||
/* Deactivate threads that are not part of this LOD. Will only let pass threads which tile
|
||||
@@ -46,7 +46,7 @@ void main()
|
||||
* main memory the usage bit. */
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++) {
|
||||
if (thread_mask(tile_co, lod)) {
|
||||
int tile_offset = shadow_tile_offset(tile_co, tilemap.tiles_index, lod);
|
||||
int tile_offset = shadow_tile_offset(uvec2(tile_co), tilemap.tiles_index, lod);
|
||||
ShadowTileDataPacked tile_data = tiles_buf[tile_offset];
|
||||
|
||||
if ((tile_data & SHADOW_IS_USED) == 0) {
|
||||
@@ -158,7 +158,7 @@ void main()
|
||||
if (thread_mask(tile_co, lod)) {
|
||||
int tile_lds = shadow_tile_offset_lds(tile_co, lod);
|
||||
if ((tiles_local[tile_lds] & SHADOW_TILE_AMENDED) != 0) {
|
||||
int tile_offset = shadow_tile_offset(tile_co, tilemap.tiles_index, lod);
|
||||
int tile_offset = shadow_tile_offset(uvec2(tile_co), tilemap.tiles_index, lod);
|
||||
/* Note that we only flush the visibility so that cached pages can be reused. */
|
||||
if ((tiles_local[tile_lds] & SHADOW_TILE_MASKED) != 0) {
|
||||
tiles_buf[tile_offset] &= ~SHADOW_IS_USED;
|
||||
|
||||
@@ -95,7 +95,7 @@ void main()
|
||||
for (int lod = 0; lod <= SHADOW_TILEMAP_LOD; lod++, box_min >>= 1, box_max >>= 1) {
|
||||
for (int y = box_min.y; y <= box_max.y; y++) {
|
||||
for (int x = box_min.x; x <= box_max.x; x++) {
|
||||
int tile_index = shadow_tile_offset(ivec2(x, y), tilemap.tiles_index, lod);
|
||||
int tile_index = shadow_tile_offset(uvec2(x, y), tilemap.tiles_index, lod);
|
||||
atomicOr(tiles_buf[tile_index], uint(SHADOW_DO_UPDATE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
|
||||
|
||||
void shadow_tag_usage_tile(LightData light, ivec2 tile_co, int lod, int tilemap_index)
|
||||
void shadow_tag_usage_tile(LightData light, uvec2 tile_co, int lod, int tilemap_index)
|
||||
{
|
||||
if (tilemap_index > light_tilemap_max_get(light)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tile_co >>= lod;
|
||||
tile_co >>= uint(lod);
|
||||
int tile_index = shadow_tile_offset(tile_co, tilemaps_buf[tilemap_index].tiles_index, lod);
|
||||
atomicOr(tiles_buf[tile_index], uint(SHADOW_IS_USED));
|
||||
}
|
||||
@@ -40,7 +40,7 @@ void shadow_tag_usage_tilemap_directional_at_level(uint l_idx, vec3 P, int level
|
||||
level, light_sun_data_get(light).clipmap_lod_min, light_sun_data_get(light).clipmap_lod_max);
|
||||
|
||||
ShadowCoordinates coord = shadow_directional_coordinates_at_level(light, lP, level);
|
||||
shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index);
|
||||
shadow_tag_usage_tile(light, coord.tilemap_tile, 0, coord.tilemap_index);
|
||||
}
|
||||
|
||||
void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radius, int lod_bias)
|
||||
@@ -57,7 +57,7 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
||||
if (radius == 0.0) {
|
||||
int level = shadow_directional_level(light, lP - light_position_get(light));
|
||||
ShadowCoordinates coord = shadow_directional_coordinates_at_level(light, lP, level);
|
||||
shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index);
|
||||
shadow_tag_usage_tile(light, coord.tilemap_tile, 0, coord.tilemap_index);
|
||||
}
|
||||
else {
|
||||
vec3 start_lP = light_world_to_local(light, P - V * radius);
|
||||
@@ -71,9 +71,9 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
|
||||
ShadowCoordinates coord_max = shadow_directional_coordinates_at_level(
|
||||
light, lP + vec3(radius, radius, 0.0), level);
|
||||
|
||||
for (int x = coord_min.tile_coord.x; x <= coord_max.tile_coord.x; x++) {
|
||||
for (int y = coord_min.tile_coord.y; y <= coord_max.tile_coord.y; y++) {
|
||||
shadow_tag_usage_tile(light, ivec2(x, y), 0, coord_min.tilemap_index);
|
||||
for (uint x = coord_min.tilemap_tile.x; x <= coord_max.tilemap_tile.x; x++) {
|
||||
for (uint y = coord_min.tilemap_tile.y; y <= coord_max.tilemap_tile.y; y++) {
|
||||
shadow_tag_usage_tile(light, uvec2(x, y), 0, coord_min.tilemap_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, float radius, int lod
|
||||
int face_id = shadow_punctual_face_index_get(lP);
|
||||
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(light, lP, face_id);
|
||||
shadow_tag_usage_tile(light, coord.tile_coord, lod, coord.tilemap_index);
|
||||
shadow_tag_usage_tile(light, coord.tilemap_tile, lod, coord.tilemap_index);
|
||||
}
|
||||
else {
|
||||
uint faces = 0u;
|
||||
@@ -146,9 +146,9 @@ void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, float radius, int lod
|
||||
ShadowCoordinates coord_min = shadow_punctual_coordinates(light, _lP - offset, face_id);
|
||||
ShadowCoordinates coord_max = shadow_punctual_coordinates(light, _lP + offset, face_id);
|
||||
|
||||
for (int x = coord_min.tile_coord.x; x <= coord_max.tile_coord.x; x++) {
|
||||
for (int y = coord_min.tile_coord.y; y <= coord_max.tile_coord.y; y++) {
|
||||
shadow_tag_usage_tile(light, ivec2(x, y), lod, tilemap_index);
|
||||
for (uint x = coord_min.tilemap_tile.x; x <= coord_max.tilemap_tile.x; x++) {
|
||||
for (uint y = coord_min.tilemap_tile.y; y <= coord_max.tilemap_tile.y; y++) {
|
||||
shadow_tag_usage_tile(light, uvec2(x, y), lod, tilemap_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,26 +130,26 @@ void main()
|
||||
lP = vec3(1e-5, 1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(-1e-5, -1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2((SHADOW_TILEMAP_RES / 2) - 1));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2((SHADOW_TILEMAP_RES / 2) - 1));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(-0.5, -0.5, 0.0); /* Min of first LOD. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
lP = vec3(0.5, 0.5, 0.0); /* Max of first LOD. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES - 1));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES), 1e-3);
|
||||
|
||||
/* Test clip-map level selection. */
|
||||
|
||||
@@ -161,26 +161,26 @@ void main()
|
||||
lP = vec3(2.00001, 2.00001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(1.50001, 1.50001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
lP = vec3(1.00001, 1.00001, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
lP = vec3(-0.0001, -0.0001, 0.0); /* Out of bounds. */
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
/* Test clip-map offset. */
|
||||
|
||||
@@ -188,16 +188,16 @@ void main()
|
||||
lP = vec3(2.0001, 0.0001, 0.0);
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, -1));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, -1));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
/* Test clip-map negative offsets. */
|
||||
|
||||
@@ -205,16 +205,16 @@ void main()
|
||||
lP = vec3(-2.0001, -0.0001, 0.0);
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 1));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 1));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
}
|
||||
|
||||
TEST(eevee_shadow, DirectionalCascadeCoordinates)
|
||||
@@ -249,34 +249,31 @@ void main()
|
||||
lP = vec3(1e-8, 1e-8, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(lod_half_size * narrowing - 1e-5, 1e-8, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 1);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(float(SHADOW_TILEMAP_RES) - 0.5, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(float(SHADOW_TILEMAP_RES) - 0.5, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
lP = vec3(lod_half_size + 1e-5, 1e-5, 0.0);
|
||||
coords = shadow_directional_coordinates(light, lP);
|
||||
EXPECT_EQ(coords.tilemap_index, 2);
|
||||
EXPECT_EQ(coords.lod_relative, 0);
|
||||
EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES - 1, SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
// lP = vec3(-0.5, -0.5, 0.0); /* Min of first LOD. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
// lP = vec3(0.5, 0.5, 0.0); /* Max of first LOD. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES - 1));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES), 1e-3);
|
||||
|
||||
/* Test clip-map level selection. */
|
||||
@@ -289,25 +286,25 @@ void main()
|
||||
// lP = vec3(2.00001, 2.00001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 0);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 2), 1e-3);
|
||||
|
||||
// lP = vec3(1.50001, 1.50001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 1);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
// lP = vec3(1.00001, 1.00001, 0.0);
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 2);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 4));
|
||||
// EXPECT_NEAR(coords.uv, vec2(SHADOW_TILEMAP_RES / 4), 1e-3);
|
||||
|
||||
// lP = vec3(-0.0001, -0.0001, 0.0); /* Out of bounds. */
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tilemap_index, 2);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(0));
|
||||
// EXPECT_NEAR(coords.uv, vec2(0), 1e-3);
|
||||
|
||||
/* Test clip-map offset. */
|
||||
@@ -316,16 +313,16 @@ void main()
|
||||
// lP = vec3(2.0001, 0.0001, 0.0);
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, -1));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, -1));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2) + ivec2(1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2) + uvec2(1, 0));
|
||||
|
||||
/* Test clip-map negative offsets. */
|
||||
|
||||
@@ -333,15 +330,15 @@ void main()
|
||||
// lP = vec3(-2.0001, -0.0001, 0.0);
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 1));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 1));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
|
||||
// coords = shadow_directional_coordinates(light, lP);
|
||||
// EXPECT_EQ(coords.tile_coord, ivec2(SHADOW_TILEMAP_RES / 2 - 1) + ivec2(-1, 0));
|
||||
// EXPECT_EQ(coords.tilemap_tile, uvec2(SHADOW_TILEMAP_RES / 2 - 1) + uvec2(-1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ void main()
|
||||
/* Iterate in reverse. */
|
||||
for (int lod = lod_max; lod >= 0; lod--) {
|
||||
int tilemap_index = light.tilemap_index + lod;
|
||||
ivec2 atlas_texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
|
||||
uvec2 atlas_texel = shadow_tile_coord_in_atlas(uvec2(tile_co), tilemap_index);
|
||||
|
||||
ShadowSamplingTilePacked tile_packed = imageLoad(tilemaps_img, atlas_texel).x;
|
||||
ShadowSamplingTilePacked tile_packed = imageLoad(tilemaps_img, ivec2(atlas_texel)).x;
|
||||
ShadowSamplingTile tile = shadow_sampling_tile_unpack(tile_packed);
|
||||
|
||||
if (lod != lod_max && !tile.is_valid) {
|
||||
@@ -81,7 +81,7 @@ void main()
|
||||
|
||||
tile_prev_packed = shadow_sampling_tile_pack(tile_prev);
|
||||
/* Replace the missing page with the one from the lower LOD. */
|
||||
imageStore(tilemaps_img, atlas_texel, uvec4(tile_prev_packed));
|
||||
imageStore(tilemaps_img, ivec2(atlas_texel), uvec4(tile_prev_packed));
|
||||
/* Push this amended tile to the local tiles. */
|
||||
tile_packed = tile_prev_packed;
|
||||
tile.is_valid = true;
|
||||
|
||||
@@ -51,7 +51,7 @@ void main()
|
||||
int tilemap_index = int(gl_GlobalInvocationID.z);
|
||||
ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
ivec2 atlas_texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
|
||||
uvec2 atlas_texel = shadow_tile_coord_in_atlas(uvec2(tile_co), tilemap_index);
|
||||
|
||||
ShadowTileMapData tilemap_data = tilemaps_buf[tilemap_index];
|
||||
bool is_cubemap = (tilemap_data.projection_type == SHADOW_PROJECTION_CUBEFACE);
|
||||
@@ -64,7 +64,7 @@ void main()
|
||||
* Add one render view per LOD that has tiles to be rendered. */
|
||||
for (int lod = lod_max; lod >= 0; lod--) {
|
||||
ivec2 tile_co_lod = tile_co >> lod;
|
||||
int tile_index = shadow_tile_offset(tile_co_lod, tilemap_data.tiles_index, lod);
|
||||
int tile_index = shadow_tile_offset(uvec2(tile_co_lod), tilemap_data.tiles_index, lod);
|
||||
|
||||
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
|
||||
|
||||
@@ -103,26 +103,6 @@ void main()
|
||||
view_index = atomicAdd(statistics_buf.view_needed_count, 1);
|
||||
if (view_index < SHADOW_VIEW_MAX) {
|
||||
/* Setup the view. */
|
||||
|
||||
render_view_buf[view_index].viewport_index = viewport_index;
|
||||
/* Scale by actual radius size (overestimate since scaled by bounding circle). */
|
||||
float filter_radius = tilemap_data.filter_radius * M_SQRT2;
|
||||
/* We need a minimum slope bias even if filter is 0 to avoid some invalid shadowing. */
|
||||
render_view_buf[view_index].filter_radius = max(1.0, filter_radius);
|
||||
/* Clipping setup. */
|
||||
if (tilemap_data.is_area_side) {
|
||||
/* Negative for tagging this case. See shadow_clip_vector_get for explanation. */
|
||||
render_view_buf[view_index].clip_distance_inv = -M_SQRT1_3 / tilemap_data.area_shift;
|
||||
}
|
||||
else if (is_point_light(tilemap_data.light_type)) {
|
||||
/* Clip as a sphere around the clip_near cube. */
|
||||
render_view_buf[view_index].clip_distance_inv = M_SQRT1_3 / tilemap_data.clip_near;
|
||||
}
|
||||
else {
|
||||
/* Disable local clipping. */
|
||||
render_view_buf[view_index].clip_distance_inv = 0.0;
|
||||
}
|
||||
|
||||
view_infos_buf[view_index].viewmat = tilemap_data.viewmat;
|
||||
view_infos_buf[view_index].viewinv = inverse(tilemap_data.viewmat);
|
||||
|
||||
@@ -153,6 +133,23 @@ void main()
|
||||
|
||||
view_infos_buf[view_index].winmat = winmat;
|
||||
view_infos_buf[view_index].wininv = inverse(winmat);
|
||||
|
||||
render_view_buf[view_index].viewport_index = viewport_index;
|
||||
render_view_buf[view_index].is_directionnal = !is_cubemap;
|
||||
render_view_buf[view_index].clip_near = clip_near;
|
||||
/* Clipping setup. */
|
||||
if (tilemap_data.is_area_side) {
|
||||
/* Negative for tagging this case. See shadow_clip_vector_get for explanation. */
|
||||
render_view_buf[view_index].clip_distance_inv = -M_SQRT1_3 / tilemap_data.area_shift;
|
||||
}
|
||||
else if (is_point_light(tilemap_data.light_type)) {
|
||||
/* Clip as a sphere around the clip_near cube. */
|
||||
render_view_buf[view_index].clip_distance_inv = M_SQRT1_3 / tilemap_data.clip_near;
|
||||
}
|
||||
else {
|
||||
/* Disable local clipping. */
|
||||
render_view_buf[view_index].clip_distance_inv = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +202,7 @@ void main()
|
||||
ShadowTileData tile_data = shadow_tile_unpack(tile_packed);
|
||||
ShadowSamplingTile tile_sampling = shadow_sampling_tile_create(tile_data, valid_lod);
|
||||
ShadowSamplingTilePacked tile_sampling_packed = shadow_sampling_tile_pack(tile_sampling);
|
||||
imageStore(tilemaps_img, atlas_texel, uvec4(tile_sampling_packed));
|
||||
imageStore(tilemaps_img, ivec2(atlas_texel), uvec4(tile_sampling_packed));
|
||||
|
||||
if (all(equal(gl_GlobalInvocationID, uvec3(0)))) {
|
||||
/* Clamp it as it can underflow if there is too much tile present on screen. */
|
||||
|
||||
@@ -87,7 +87,7 @@ void main()
|
||||
for (int lod = 0; lod <= lod_max; lod++, lod_size >>= 1u) {
|
||||
bool thread_active = all(lessThan(tile_co, ivec2(lod_size)));
|
||||
ShadowTileDataPacked tile = 0;
|
||||
int tile_load = shadow_tile_offset(tile_wrapped, tilemap.tiles_index, lod);
|
||||
int tile_load = shadow_tile_offset(uvec2(tile_wrapped), tilemap.tiles_index, lod);
|
||||
if (thread_active) {
|
||||
tile = init_tile_data(tiles_buf[tile_load], do_update);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void main()
|
||||
barrier();
|
||||
|
||||
if (thread_active) {
|
||||
int tile_store = shadow_tile_offset(tile_co, tilemap.tiles_index, lod);
|
||||
int tile_store = shadow_tile_offset(uvec2(tile_co), tilemap.tiles_index, lod);
|
||||
if ((tile_load != tile_store) && flag_test(tile, SHADOW_IS_CACHED)) {
|
||||
/* Inlining of shadow_page_cache_update_tile_ref to avoid buffer dependencies. */
|
||||
pages_cached_buf[shadow_tile_unpack(tile).cache_index].y = tile_store;
|
||||
|
||||
@@ -15,19 +15,19 @@ int shadow_tile_index(ivec2 tile)
|
||||
return tile.x + tile.y * SHADOW_TILEMAP_RES;
|
||||
}
|
||||
|
||||
ivec2 shadow_tile_coord(int tile_index)
|
||||
uvec2 shadow_tile_coord(int tile_index)
|
||||
{
|
||||
return ivec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES);
|
||||
return uvec2(tile_index % SHADOW_TILEMAP_RES, tile_index / SHADOW_TILEMAP_RES);
|
||||
}
|
||||
|
||||
/* Return bottom left pixel position of the tile-map inside the tile-map atlas. */
|
||||
ivec2 shadow_tilemap_start(int tilemap_index)
|
||||
uvec2 shadow_tilemap_start(int tilemap_index)
|
||||
{
|
||||
return SHADOW_TILEMAP_RES *
|
||||
ivec2(tilemap_index % SHADOW_TILEMAP_PER_ROW, tilemap_index / SHADOW_TILEMAP_PER_ROW);
|
||||
uvec2(tilemap_index % SHADOW_TILEMAP_PER_ROW, tilemap_index / SHADOW_TILEMAP_PER_ROW);
|
||||
}
|
||||
|
||||
ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index)
|
||||
uvec2 shadow_tile_coord_in_atlas(uvec2 tile, int tilemap_index)
|
||||
{
|
||||
return shadow_tilemap_start(tilemap_index) + tile;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ ivec2 shadow_tile_coord_in_atlas(ivec2 tile, int tilemap_index)
|
||||
* Return tile index inside `tiles_buf` for a given tile coordinate inside a specific LOD.
|
||||
* `tiles_index` should be `ShadowTileMapData.tiles_index`.
|
||||
*/
|
||||
int shadow_tile_offset(ivec2 tile, int tiles_index, int lod)
|
||||
int shadow_tile_offset(uvec2 tile, int tiles_index, int lod)
|
||||
{
|
||||
#if SHADOW_TILEMAP_LOD > 5
|
||||
# error This needs to be adjusted
|
||||
@@ -54,34 +54,35 @@ int shadow_tile_offset(ivec2 tile, int tiles_index, int lod)
|
||||
const int lod4_size = lod4_width * lod4_width;
|
||||
const int lod5_size = lod5_width * lod5_width;
|
||||
|
||||
/* TODO(fclem): Convert everything to uint. */
|
||||
int offset = tiles_index;
|
||||
switch (lod) {
|
||||
case 5:
|
||||
offset += lod0_size + lod1_size + lod2_size + lod3_size + lod4_size;
|
||||
offset += tile.y * lod5_width;
|
||||
offset += int(tile.y) * lod5_width;
|
||||
break;
|
||||
case 4:
|
||||
offset += lod0_size + lod1_size + lod2_size + lod3_size;
|
||||
offset += tile.y * lod4_width;
|
||||
offset += int(tile.y) * lod4_width;
|
||||
break;
|
||||
case 3:
|
||||
offset += lod0_size + lod1_size + lod2_size;
|
||||
offset += tile.y * lod3_width;
|
||||
offset += int(tile.y) * lod3_width;
|
||||
break;
|
||||
case 2:
|
||||
offset += lod0_size + lod1_size;
|
||||
offset += tile.y * lod2_width;
|
||||
offset += int(tile.y) * lod2_width;
|
||||
break;
|
||||
case 1:
|
||||
offset += lod0_size;
|
||||
offset += tile.y * lod1_width;
|
||||
offset += int(tile.y) * lod1_width;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
offset += tile.y * lod0_width;
|
||||
offset += int(tile.y) * lod0_width;
|
||||
break;
|
||||
}
|
||||
offset += tile.x;
|
||||
offset += int(tile.x);
|
||||
return offset;
|
||||
}
|
||||
|
||||
@@ -92,13 +93,13 @@ int shadow_tile_offset(ivec2 tile, int tiles_index, int lod)
|
||||
* \{ */
|
||||
|
||||
/** \note Will clamp if out of bounds. */
|
||||
ShadowSamplingTile shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilemap_index)
|
||||
ShadowSamplingTile shadow_tile_load(usampler2D tilemaps_tx, uvec2 tile_co, int tilemap_index)
|
||||
{
|
||||
/* NOTE(@fclem): This clamp can hide some small imprecision at clip-map transition.
|
||||
* Can be disabled to check if the clip-map is well centered. */
|
||||
tile_co = clamp(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
|
||||
uint tile_data = texelFetch(tilemaps_tx, texel, 0).x;
|
||||
tile_co = clamp(tile_co, uvec2(0), uvec2(SHADOW_TILEMAP_RES - 1));
|
||||
uvec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
|
||||
uint tile_data = texelFetch(tilemaps_tx, ivec2(texel), 0).x;
|
||||
return shadow_sampling_tile_unpack(tile_data);
|
||||
}
|
||||
|
||||
@@ -202,14 +203,22 @@ int shadow_punctual_level(LightData light,
|
||||
struct ShadowCoordinates {
|
||||
/* Index of the tile-map to containing the tile. */
|
||||
int tilemap_index;
|
||||
/* LOD of the tile to load relative to the min level. Always positive. */
|
||||
int lod_relative;
|
||||
/* Tile coordinate inside the tile-map. */
|
||||
ivec2 tile_coord;
|
||||
/* UV coordinates in [0..SHADOW_TILEMAP_RES) range. */
|
||||
vec2 uv;
|
||||
/* Texel coordinates in [0..SHADOW_MAP_MAX_RES) range. */
|
||||
uvec2 tilemap_texel;
|
||||
/* Tile coordinate in [0..SHADOW_TILEMAP_RES) range. */
|
||||
uvec2 tilemap_tile;
|
||||
};
|
||||
|
||||
/* Assumes tilemap_uv is already saturated. */
|
||||
ShadowCoordinates shadow_coordinate_from_uvs(int tilemap_index, vec2 tilemap_uv)
|
||||
{
|
||||
ShadowCoordinates ret;
|
||||
ret.tilemap_index = tilemap_index;
|
||||
ret.tilemap_texel = uvec2(tilemap_uv * (float(SHADOW_MAP_MAX_RES) - 1e-2));
|
||||
ret.tilemap_tile = ret.tilemap_texel >> uint(SHADOW_PAGE_LOD);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Retain sign bit and avoid costly int division. */
|
||||
ivec2 shadow_decompress_grid_offset(eLightType light_type,
|
||||
ivec2 offset_neg,
|
||||
@@ -229,30 +238,24 @@ ivec2 shadow_decompress_grid_offset(eLightType light_type,
|
||||
*/
|
||||
ShadowCoordinates shadow_directional_coordinates_at_level(LightData light, vec3 lP, int level)
|
||||
{
|
||||
ShadowCoordinates ret;
|
||||
/* This difference needs to be less than 32 for the later shift to be valid.
|
||||
* This is ensured by `ShadowDirectional::clipmap_level_range()`. */
|
||||
int level_relative = level - light_sun_data_get(light).clipmap_lod_min;
|
||||
|
||||
ret.tilemap_index = light.tilemap_index + level_relative;
|
||||
|
||||
ret.lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light_sun_data_get(light).clipmap_lod_min :
|
||||
int lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light_sun_data_get(light).clipmap_lod_min :
|
||||
level;
|
||||
|
||||
/* Compute offset in tile. */
|
||||
ivec2 clipmap_offset = shadow_decompress_grid_offset(
|
||||
light.type,
|
||||
light_sun_data_get(light).clipmap_base_offset_neg,
|
||||
light_sun_data_get(light).clipmap_base_offset_pos,
|
||||
level_relative);
|
||||
/* UV in [0..1] range over the tilemap. */
|
||||
vec2 tilemap_uv = lP.xy - light_sun_data_get(light).clipmap_origin;
|
||||
tilemap_uv *= exp2(float(-lod_relative));
|
||||
tilemap_uv -= vec2(clipmap_offset) * (1.0 / float(SHADOW_TILEMAP_RES));
|
||||
tilemap_uv = saturate(tilemap_uv + 0.5);
|
||||
|
||||
ret.uv = lP.xy - light_sun_data_get(light).clipmap_origin;
|
||||
ret.uv /= exp2(float(ret.lod_relative));
|
||||
ret.uv = ret.uv * float(SHADOW_TILEMAP_RES) + float(SHADOW_TILEMAP_RES / 2);
|
||||
ret.uv -= vec2(clipmap_offset);
|
||||
/* Clamp to avoid out of tile-map access. */
|
||||
ret.tile_coord = clamp(ivec2(ret.uv), ivec2(0.0), ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
return ret;
|
||||
return shadow_coordinate_from_uvs(light.tilemap_index + level_relative, tilemap_uv);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,16 +328,12 @@ ShadowCoordinates shadow_punctual_coordinates(LightData light, vec3 lP, int face
|
||||
{
|
||||
float clip_near = intBitsToFloat(light.clip_near);
|
||||
float clip_side = light_local_data_get(light).clip_side;
|
||||
|
||||
ShadowCoordinates ret;
|
||||
ret.tilemap_index = light.tilemap_index + face_id;
|
||||
/* UVs in [-1..+1] range. */
|
||||
ret.uv = (lP.xy * clip_near) / abs(lP.z * clip_side);
|
||||
/* UVs in [0..SHADOW_TILEMAP_RES] range. */
|
||||
ret.uv = ret.uv * float(SHADOW_TILEMAP_RES / 2) + float(SHADOW_TILEMAP_RES / 2);
|
||||
/* Clamp to avoid out of tile-map access. */
|
||||
ret.tile_coord = clamp(ivec2(ret.uv), ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1));
|
||||
return ret;
|
||||
vec2 tilemap_uv = (lP.xy * clip_near) / abs(lP.z * clip_side);
|
||||
/* UVs in [0..1] range. */
|
||||
tilemap_uv = saturate(tilemap_uv * 0.5 + 0.5);
|
||||
|
||||
return shadow_coordinate_from_uvs(light.tilemap_index + face_id, tilemap_uv);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -15,47 +15,17 @@
|
||||
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
|
||||
|
||||
float shadow_read_depth_at_tilemap_uv(int tilemap_index, vec2 tilemap_uv)
|
||||
{
|
||||
/* Prevent out of bound access. Assumes the input is already non negative. */
|
||||
tilemap_uv = min(tilemap_uv, vec2(0.99999));
|
||||
|
||||
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
|
||||
/* Using bitwise ops is way faster than integer ops. */
|
||||
const int page_shift = SHADOW_PAGE_LOD;
|
||||
const int page_mask = ~(0xFFFFFFFF << SHADOW_PAGE_LOD);
|
||||
|
||||
ivec2 tile_coord = texel_coord >> page_shift;
|
||||
ShadowSamplingTile tile = shadow_tile_load(shadow_tilemaps_tx, tile_coord, tilemap_index);
|
||||
|
||||
if (!tile.is_valid) {
|
||||
return -1.0;
|
||||
}
|
||||
/* Shift LOD0 pixels so that they get wrapped at the right position for the given LOD. */
|
||||
/* TODO convert everything to uint to avoid signed int operations. */
|
||||
texel_coord += ivec2(tile.lod_offset << SHADOW_PAGE_LOD);
|
||||
/* Scale to LOD pixels (merge LOD0 pixels together) then mask to get pixel in page. */
|
||||
ivec2 texel_page = (texel_coord >> int(tile.lod)) & page_mask;
|
||||
ivec3 texel = ivec3((ivec2(tile.page.xy) << page_shift) | texel_page, tile.page.z);
|
||||
|
||||
return uintBitsToFloat(texelFetch(shadow_atlas_tx, texel, 0).r);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Shadow Map Tracing loop
|
||||
* \{ */
|
||||
|
||||
#define SHADOW_TRACING_INVALID_HISTORY -999.0
|
||||
#define SHADOW_TRACING_INVALID_HISTORY FLT_MAX
|
||||
|
||||
struct ShadowMapTracingState {
|
||||
/* Receiver Z value at previous valid depth sample. */
|
||||
float receiver_depth_history;
|
||||
/* Occluder Z value at previous valid depth sample. */
|
||||
float occluder_depth_history;
|
||||
/* Ray time at previous valid depth sample. */
|
||||
float ray_time_history;
|
||||
/* Z slope (delta/time) between previous valid sample (N-1) and the one before that (N-2). */
|
||||
float occluder_depth_slope;
|
||||
/* Occluder ray coordinate at previous valid depth sample. */
|
||||
vec2 occluder_history;
|
||||
/* Time slope between previous valid sample (N-1) and the one before that (N-2). */
|
||||
float occluder_slope;
|
||||
/* Multiplier and bias to the ray step quickly compute ray time. */
|
||||
float ray_step_mul;
|
||||
float ray_step_bias;
|
||||
@@ -67,10 +37,8 @@ struct ShadowMapTracingState {
|
||||
ShadowMapTracingState shadow_map_trace_init(int sample_count, float step_offset)
|
||||
{
|
||||
ShadowMapTracingState state;
|
||||
state.receiver_depth_history = -1.0;
|
||||
state.occluder_depth_history = SHADOW_TRACING_INVALID_HISTORY;
|
||||
state.ray_time_history = -1.0;
|
||||
state.occluder_depth_slope = 0.0;
|
||||
state.occluder_history = vec2(SHADOW_TRACING_INVALID_HISTORY);
|
||||
state.occluder_slope = SHADOW_TRACING_INVALID_HISTORY;
|
||||
/* We trace the ray in reverse. From 1.0 (light) to 0.0 (shading point). */
|
||||
state.ray_step_mul = -1.0 / float(sample_count);
|
||||
state.ray_step_bias = 1.0 + step_offset * state.ray_step_mul;
|
||||
@@ -79,8 +47,12 @@ ShadowMapTracingState shadow_map_trace_init(int sample_count, float step_offset)
|
||||
}
|
||||
|
||||
struct ShadowTracingSample {
|
||||
float receiver_depth;
|
||||
float occluder_depth;
|
||||
/**
|
||||
* Occluder position in ray space.
|
||||
* `x` component is just the normalized distance from the ray start to the ray end.
|
||||
* `y` component is signed distance to the ray, positive if on the light side of the ray.
|
||||
*/
|
||||
vec2 occluder;
|
||||
bool skip_sample;
|
||||
};
|
||||
|
||||
@@ -91,7 +63,7 @@ struct ShadowTracingSample {
|
||||
* Most of the code is wrapped into functions to avoid to debug issues inside macro code.
|
||||
*/
|
||||
#define SHADOW_MAP_TRACE_FN(ShadowRayType) \
|
||||
ShadowMapTraceResult shadow_map_trace(ShadowRayType ray, int sample_count, float step_offset) \
|
||||
bool shadow_map_trace(ShadowRayType ray, int sample_count, float step_offset) \
|
||||
{ \
|
||||
ShadowMapTracingState state = shadow_map_trace_init(sample_count, step_offset); \
|
||||
for (int i = 0; (i <= sample_count) && (i <= SHADOW_MAX_STEP) && (state.hit == false); i++) { \
|
||||
@@ -102,7 +74,7 @@ struct ShadowTracingSample {
|
||||
\
|
||||
shadow_map_trace_hit_check(state, samp); \
|
||||
} \
|
||||
return shadow_map_trace_finish(state); \
|
||||
return state.hit; \
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,68 +91,38 @@ void shadow_map_trace_hit_check(inout ShadowMapTracingState state, ShadowTracing
|
||||
return;
|
||||
}
|
||||
/* For the first sample, regular depth compare since we do not have history values. */
|
||||
if (state.occluder_depth_history == SHADOW_TRACING_INVALID_HISTORY) {
|
||||
if (samp.occluder_depth < samp.receiver_depth) {
|
||||
if (state.occluder_history.x == SHADOW_TRACING_INVALID_HISTORY) {
|
||||
if (samp.occluder.x > state.ray_time) {
|
||||
state.hit = true;
|
||||
return;
|
||||
}
|
||||
state.occluder_depth_history = samp.occluder_depth;
|
||||
state.receiver_depth_history = samp.receiver_depth;
|
||||
state.ray_time_history = state.ray_time;
|
||||
state.occluder_history = samp.occluder;
|
||||
return;
|
||||
}
|
||||
/* Delta between previous valid sample. */
|
||||
float ray_depth_delta = samp.receiver_depth - state.receiver_depth_history;
|
||||
/* Delta between previous valid sample not occluding the ray. */
|
||||
float time_delta = state.ray_time - state.ray_time_history;
|
||||
/* Arbitrary increase the threshold to avoid missing occluders because of precision issues.
|
||||
* Increasing the threshold inflates the occluders. */
|
||||
float compare_threshold = abs(ray_depth_delta) * 1.05;
|
||||
/* Find out if the ray step is behind an occluder.
|
||||
* To be consider behind (and ignore the occluder), the occluder must not be cross the ray.
|
||||
* Use the full delta ray depth as threshold to make sure to not miss any occluder. */
|
||||
bool is_behind = samp.occluder_depth < (samp.receiver_depth - compare_threshold);
|
||||
|
||||
if (is_behind) {
|
||||
/* Use last known valid occluder Z value and extrapolate to the sample position. */
|
||||
samp.occluder_depth = state.occluder_depth_history + state.occluder_depth_slope * time_delta;
|
||||
/* Intersection test will be against the extrapolated last known occluder. */
|
||||
bool is_behind_occluder = samp.occluder.y > 0.0;
|
||||
if (is_behind_occluder && (state.occluder_slope != SHADOW_TRACING_INVALID_HISTORY)) {
|
||||
/* Extrapolate last known valid occluder and check if it crossed the ray.
|
||||
* Note that we only want to check if the extrapolated occluder is above the ray at a certain
|
||||
* time value, we don't actually care about the correct value. So we replace the complex
|
||||
* problem of trying to get the extrapolation in shadow map space into the extrapolation at
|
||||
* ray_time in ray space. This is equivalent as both functions have the same roots. */
|
||||
float delta_time = state.ray_time - state.occluder_history.x;
|
||||
float extrapolated_occluder_y = abs(state.occluder_history.y) +
|
||||
state.occluder_slope * delta_time;
|
||||
state.hit = extrapolated_occluder_y < 0.0;
|
||||
}
|
||||
else {
|
||||
/* Compute current occluder slope and record history for when the ray goes behind a surface. */
|
||||
state.occluder_depth_slope = (samp.occluder_depth - state.occluder_depth_history) / time_delta;
|
||||
state.occluder_depth_slope = clamp(state.occluder_depth_slope, -100.0, 100.0);
|
||||
state.occluder_depth_history = samp.occluder_depth;
|
||||
state.ray_time_history = state.ray_time;
|
||||
/* Intersection test will be against the current sample's occluder. */
|
||||
vec2 delta = samp.occluder - state.occluder_history;
|
||||
/* Clamping the slope to a mininim avoid light leaking. */
|
||||
/* TODO(fclem): Expose as parameter? */
|
||||
const float min_slope = tan(M_PI * 0.25);
|
||||
state.occluder_slope = max(min_slope, abs(delta.y / delta.x));
|
||||
state.occluder_history = samp.occluder;
|
||||
/* Intersection test. Intersect if above the ray time. */
|
||||
state.hit = samp.occluder.x > state.ray_time;
|
||||
}
|
||||
|
||||
if (samp.occluder_depth < samp.receiver_depth) {
|
||||
state.occluder_depth_history = samp.occluder_depth;
|
||||
state.hit = true;
|
||||
return;
|
||||
}
|
||||
/* No intersection. */
|
||||
state.receiver_depth_history = samp.receiver_depth;
|
||||
}
|
||||
|
||||
struct ShadowMapTraceResult {
|
||||
bool has_hit;
|
||||
float occluder_depth;
|
||||
};
|
||||
|
||||
ShadowMapTraceResult shadow_map_trace_finish(ShadowMapTracingState state)
|
||||
{
|
||||
ShadowMapTraceResult result;
|
||||
if (state.hit) {
|
||||
result.occluder_depth = state.occluder_depth_history;
|
||||
result.has_hit = true;
|
||||
}
|
||||
else {
|
||||
result.occluder_depth = 0.0;
|
||||
result.has_hit = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -202,12 +144,15 @@ vec3 shadow_ray_above_horizon_ensure(vec3 L, vec3 N)
|
||||
* \{ */
|
||||
|
||||
struct ShadowRayDirectional {
|
||||
/* Ray in local translated coordinate, with depth in [0..1] range in W component. */
|
||||
vec4 origin;
|
||||
vec4 direction;
|
||||
/* Ray in light rotated space. But not translated. */
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
/* Convert form local light position to ray oriented position where X axis is the ray. */
|
||||
vec3 local_ray_up;
|
||||
LightData light;
|
||||
};
|
||||
|
||||
/* `lP` is supposed to be in light rotated space. But not translated. */
|
||||
ShadowRayDirectional shadow_ray_generate_directional(LightData light,
|
||||
vec2 random_2d,
|
||||
vec3 lP,
|
||||
@@ -216,28 +161,25 @@ ShadowRayDirectional shadow_ray_generate_directional(LightData light,
|
||||
float clip_near = orderedIntBitsToFloat(light.clip_near);
|
||||
float clip_far = orderedIntBitsToFloat(light.clip_far);
|
||||
/* Assumed to be non-null. */
|
||||
float z_range = clip_far - clip_near;
|
||||
float dist_to_near_plane = -lP.z - clip_near;
|
||||
|
||||
/* `lP` is supposed to be in light rotated space. But not translated. */
|
||||
vec4 origin = vec4(lP, dist_to_near_plane / z_range);
|
||||
|
||||
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);
|
||||
vec3 direction = sample_uniform_cone(sample_cylinder(random_2d),
|
||||
light_sun_data_get(light).shadow_angle);
|
||||
|
||||
direction = shadow_ray_above_horizon_ensure(direction, lNg);
|
||||
|
||||
/* 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);
|
||||
dist_to_near_plane / direction.z);
|
||||
|
||||
ShadowRayDirectional ray;
|
||||
ray.origin = origin;
|
||||
ray.origin = lP;
|
||||
ray.direction = direction;
|
||||
ray.light = light;
|
||||
/* TODO(fclem): We can simplify this using the ray direction construction. */
|
||||
ray.local_ray_up = safe_normalize(
|
||||
cross(cross(vec3(0.0, 0.0, -1.0), ray.direction), ray.direction));
|
||||
return ray;
|
||||
}
|
||||
|
||||
@@ -245,37 +187,21 @@ ShadowTracingSample shadow_map_trace_sample(ShadowMapTracingState state,
|
||||
inout ShadowRayDirectional ray)
|
||||
{
|
||||
/* Ray position is ray local position with origin at light origin. */
|
||||
vec4 ray_pos = ray.origin + ray.direction * state.ray_time;
|
||||
vec3 ray_pos = ray.origin + ray.direction * state.ray_time;
|
||||
|
||||
int level = shadow_directional_level(ray.light, ray_pos.xyz - light_position_get(ray.light));
|
||||
/* This difference needs to be less than 32 for the later shift to be valid.
|
||||
* This is ensured by ShadowDirectional::clipmap_level_range(). */
|
||||
int level_relative = level - light_sun_data_get(ray.light).clipmap_lod_min;
|
||||
ShadowCoordinates coord = shadow_directional_coordinates(ray.light, ray_pos);
|
||||
|
||||
int lod_relative = (ray.light.type == LIGHT_SUN_ORTHO) ?
|
||||
light_sun_data_get(ray.light).clipmap_lod_min :
|
||||
level;
|
||||
|
||||
vec2 clipmap_origin = light_sun_data_get(ray.light).clipmap_origin;
|
||||
vec2 clipmap_pos = ray_pos.xy - clipmap_origin;
|
||||
vec2 tilemap_uv = clipmap_pos * exp2(-float(lod_relative)) + 0.5;
|
||||
|
||||
/* Compute offset in tile. */
|
||||
ivec2 clipmap_offset = shadow_decompress_grid_offset(
|
||||
ray.light.type,
|
||||
light_sun_data_get(ray.light).clipmap_base_offset_neg,
|
||||
light_sun_data_get(ray.light).clipmap_base_offset_pos,
|
||||
level_relative);
|
||||
/* Translate tilemap UVs to its origin. */
|
||||
tilemap_uv -= vec2(clipmap_offset) / float(SHADOW_TILEMAP_RES);
|
||||
/* Clamp to avoid out of tilemap access. */
|
||||
tilemap_uv = saturate(tilemap_uv);
|
||||
float depth = shadow_read_depth(shadow_atlas_tx, shadow_tilemaps_tx, coord);
|
||||
/* Distance from near plane. */
|
||||
float clip_near = orderedIntBitsToFloat(ray.light.clip_near);
|
||||
vec3 occluder_pos = vec3(ray_pos.xy, -depth - clip_near);
|
||||
/* Transform to ray local space. */
|
||||
vec3 ray_local_occluder = occluder_pos - ray.origin;
|
||||
|
||||
ShadowTracingSample samp;
|
||||
samp.receiver_depth = ray_pos.w;
|
||||
samp.occluder_depth = shadow_read_depth_at_tilemap_uv(ray.light.tilemap_index + level_relative,
|
||||
tilemap_uv);
|
||||
samp.skip_sample = (samp.occluder_depth == -1.0);
|
||||
samp.occluder.x = dot(ray_local_occluder, ray.direction) / length_squared(ray.direction);
|
||||
samp.occluder.y = dot(ray_local_occluder, ray.local_ray_up);
|
||||
samp.skip_sample = (depth == -1.0);
|
||||
return samp;
|
||||
}
|
||||
|
||||
@@ -288,11 +214,14 @@ SHADOW_MAP_TRACE_FN(ShadowRayDirectional)
|
||||
* \{ */
|
||||
|
||||
struct ShadowRayPunctual {
|
||||
/* Ray in tile-map normalized coordinates [0..1]. */
|
||||
/* Light space shadow ray origin and direction. */
|
||||
vec3 origin;
|
||||
vec3 direction;
|
||||
/* Convert form local light position to ray oriented position where X axis is the ray. */
|
||||
vec3 local_ray_up;
|
||||
/* Tile-map to sample. */
|
||||
int tilemap_index;
|
||||
int light_tilemap_index;
|
||||
LightData light;
|
||||
};
|
||||
|
||||
/* Return ray in UV clip space [0..1]. */
|
||||
@@ -354,44 +283,34 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, vec2 random_2d,
|
||||
direction *= saturate((dist - clip_distance) / dist);
|
||||
}
|
||||
|
||||
/* Apply shadow origin shift. */
|
||||
vec3 local_ray_start = lP + projection_origin;
|
||||
vec3 local_ray_end = local_ray_start + direction;
|
||||
|
||||
/* Use an offset in the ray direction to jitter which face is traced.
|
||||
* This helps hiding some harsh discontinuity. */
|
||||
int face_id = shadow_punctual_face_index_get(local_ray_start + direction * 0.5);
|
||||
/* Local Light Space > Face Local (View) Space. */
|
||||
vec3 view_ray_start = shadow_punctual_local_position_to_face_local(face_id, local_ray_start);
|
||||
vec3 view_ray_end = shadow_punctual_local_position_to_face_local(face_id, local_ray_end);
|
||||
|
||||
/* Face Local (View) Space > Clip Space. */
|
||||
/* TODO: Could be simplified since frustum is completely symmetrical. */
|
||||
mat4 winmat = projection_perspective(
|
||||
-clip_side, clip_side, -clip_side, clip_side, clip_near, clip_far);
|
||||
vec3 clip_ray_start = project_point(winmat, view_ray_start);
|
||||
vec3 clip_ray_end = project_point(winmat, view_ray_end);
|
||||
/* Clip Space > UV Space. */
|
||||
vec3 uv_ray_start = clip_ray_start * 0.5 + 0.5;
|
||||
vec3 uv_ray_end = clip_ray_end * 0.5 + 0.5;
|
||||
/* Compute the ray again. */
|
||||
ShadowRayPunctual ray;
|
||||
ray.origin = uv_ray_start;
|
||||
ray.direction = uv_ray_end - uv_ray_start;
|
||||
ray.tilemap_index = light.tilemap_index + face_id;
|
||||
ray.origin = lP;
|
||||
ray.direction = direction;
|
||||
ray.light_tilemap_index = light.tilemap_index;
|
||||
ray.local_ray_up = safe_normalize(cross(cross(ray.origin, ray.direction), ray.direction));
|
||||
ray.light = light;
|
||||
return ray;
|
||||
}
|
||||
|
||||
ShadowTracingSample shadow_map_trace_sample(ShadowMapTracingState state,
|
||||
inout ShadowRayPunctual ray)
|
||||
{
|
||||
vec3 ray_pos = ray.origin + ray.direction * state.ray_time;
|
||||
vec2 tilemap_uv = saturate(ray_pos.xy);
|
||||
vec3 receiver_pos = ray.origin + ray.direction * state.ray_time;
|
||||
int face_id = shadow_punctual_face_index_get(receiver_pos);
|
||||
vec3 face_pos = shadow_punctual_local_position_to_face_local(face_id, receiver_pos);
|
||||
ShadowCoordinates coord = shadow_punctual_coordinates(ray.light, face_pos, face_id);
|
||||
|
||||
float radial_occluder_depth = shadow_read_depth(shadow_atlas_tx, shadow_tilemaps_tx, coord);
|
||||
vec3 occluder_pos = receiver_pos * (radial_occluder_depth / length(receiver_pos));
|
||||
|
||||
/* Transform to ray local space. */
|
||||
vec3 ray_local_occluder = occluder_pos - ray.origin;
|
||||
|
||||
ShadowTracingSample samp;
|
||||
samp.receiver_depth = ray_pos.z;
|
||||
samp.occluder_depth = shadow_read_depth_at_tilemap_uv(ray.tilemap_index, tilemap_uv);
|
||||
samp.skip_sample = (samp.occluder_depth == -1.0);
|
||||
samp.occluder.x = dot(ray_local_occluder, ray.direction) / length_squared(ray.direction);
|
||||
samp.occluder.y = dot(ray_local_occluder, ray.local_ray_up);
|
||||
samp.skip_sample = (radial_occluder_depth == -1.0);
|
||||
return samp;
|
||||
}
|
||||
|
||||
@@ -487,17 +406,18 @@ float shadow_normal_offset(vec3 Ng, vec3 L)
|
||||
|
||||
/**
|
||||
* Evaluate shadowing by casting rays toward the light direction.
|
||||
* Returns light visibility.
|
||||
*/
|
||||
ShadowEvalResult shadow_eval(LightData light,
|
||||
const bool is_directional,
|
||||
const bool is_transmission,
|
||||
bool is_translucent_with_thickness,
|
||||
float thickness, /* Only used if is_transmission is true. */
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 L,
|
||||
int ray_count,
|
||||
int ray_step_count)
|
||||
float shadow_eval(LightData light,
|
||||
const bool is_directional,
|
||||
const bool is_transmission,
|
||||
bool is_translucent_with_thickness,
|
||||
float thickness, /* Only used if is_transmission is true. */
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 L,
|
||||
int ray_count,
|
||||
int ray_step_count)
|
||||
{
|
||||
#if defined(EEVEE_SAMPLING_DATA) && defined(EEVEE_UTILITY_TX)
|
||||
# ifdef GPU_FRAGMENT_SHADER
|
||||
@@ -506,7 +426,7 @@ ShadowEvalResult shadow_eval(LightData light,
|
||||
vec2 pixel = vec2(gl_GlobalInvocationID.xy);
|
||||
# endif
|
||||
vec3 blue_noise_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
|
||||
vec3 random_shadow_3d = blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U);
|
||||
vec3 random_shadow_3d = fract(blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U));
|
||||
vec2 random_pcf_2d = fract(blue_noise_3d.xy + sampling_rng_2D_get(SAMPLING_SHADOW_X));
|
||||
#else
|
||||
/* Case of surfel light eval. */
|
||||
@@ -546,24 +466,21 @@ ShadowEvalResult shadow_eval(LightData light,
|
||||
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);
|
||||
|
||||
ShadowMapTraceResult trace;
|
||||
bool has_hit;
|
||||
if (is_directional) {
|
||||
ShadowRayDirectional clip_ray = shadow_ray_generate_directional(
|
||||
light, random_ray_2d, lP, lNg);
|
||||
trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
|
||||
has_hit = 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);
|
||||
trace = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
|
||||
has_hit = shadow_map_trace(clip_ray, ray_step_count, random_shadow_3d.z);
|
||||
}
|
||||
|
||||
surface_hit += float(trace.has_hit);
|
||||
surface_hit += float(has_hit);
|
||||
}
|
||||
/* Average samples. */
|
||||
ShadowEvalResult result;
|
||||
result.light_visibilty = saturate(1.0 - surface_hit / float(ray_count));
|
||||
result.occluder_distance = 0.0; /* Unused. Could reintroduced if needed. */
|
||||
return result;
|
||||
return saturate(1.0 - surface_hit / float(ray_count));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -162,6 +162,14 @@ void shadow_viewport_layer_set(int view_id, int lod)
|
||||
gpu_ViewportIndex = lod;
|
||||
}
|
||||
|
||||
vec3 shadow_position_vector_get(vec3 view_position, ShadowRenderView view)
|
||||
{
|
||||
if (view.is_directionnal) {
|
||||
return vec3(0.0, 0.0, -view_position.z - view.clip_near);
|
||||
}
|
||||
return view_position;
|
||||
}
|
||||
|
||||
/* In order to support physical clipping, we pass a vector to the fragment shader that then clips
|
||||
* each fragment using a unit sphere test. This allows to support both point light and area light
|
||||
* clipping at the same time. */
|
||||
|
||||
@@ -24,21 +24,19 @@ vec4 closure_to_rgba(Closure cl)
|
||||
|
||||
void main()
|
||||
{
|
||||
float f_depth = gl_FragCoord.z;
|
||||
/* Slope bias.
|
||||
* Note that we always need a minimum slope bias of 1 pixel to avoid slanted surfaces aliasing
|
||||
* onto facing surfaces.
|
||||
* IMPORTANT: `fwidth` needs to be inside uniform control flow. */
|
||||
float ndc_depth = gl_FragCoord.z;
|
||||
float linear_depth = length(shadow_clip.position);
|
||||
|
||||
#ifdef SHADOW_UPDATE_TBDR
|
||||
/* We need to write to `gl_FragDepth` un-conditionally. So we cannot early exit or use discard. */
|
||||
# define discard_result f_depth = 1.0;
|
||||
# define discard_result \
|
||||
linear_depth = FLT_MAX; \
|
||||
ndc_depth = 1.0;
|
||||
#else
|
||||
# define discard_result \
|
||||
discard; \
|
||||
return;
|
||||
#endif
|
||||
/* Avoid values greater than 1. */
|
||||
f_depth = saturate(f_depth);
|
||||
|
||||
/* Clip to light shape. */
|
||||
if (length_squared(shadow_clip.vector) < 1.0) {
|
||||
@@ -83,12 +81,12 @@ void main()
|
||||
|
||||
ivec3 out_texel = ivec3((page.xy << page_shift) | texel_page, page.z);
|
||||
|
||||
uint u_depth = floatBitsToUint(f_depth);
|
||||
uint u_depth = floatBitsToUint(linear_depth);
|
||||
imageAtomicMin(shadow_atlas_img, out_texel, u_depth);
|
||||
#endif
|
||||
|
||||
#ifdef SHADOW_UPDATE_TBDR
|
||||
gl_FragDepth = f_depth;
|
||||
out_depth = f_depth;
|
||||
gl_FragDepth = ndc_depth;
|
||||
out_depth = linear_depth;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ vec3 volume_light_eval(const bool is_directional, vec3 P, vec3 V, uint l_idx, fl
|
||||
|
||||
float visibility = attenuation;
|
||||
if (light.tilemap_index != LIGHT_NO_SHADOW) {
|
||||
visibility *= shadow_sample(is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P)
|
||||
.light_visibilty;
|
||||
float delta = shadow_sample(is_directional, shadow_atlas_tx, shadow_tilemaps_tx, light, P);
|
||||
if (delta > 0.0) {
|
||||
return vec3(0);
|
||||
}
|
||||
}
|
||||
visibility *= volume_phase_function(-V, lv.L, s_anisotropy);
|
||||
if (visibility < LIGHT_ATTENUATION_THRESHOLD) {
|
||||
|
||||
@@ -232,17 +232,14 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_atomic_iface, "shadow_iface")
|
||||
.flat(Type::INT, "shadow_view_id");
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_clipping_iface, "shadow_clip")
|
||||
.smooth(Type::VEC3, "position")
|
||||
.smooth(Type::VEC3, "vector");
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_flat_iface, "shadow_flat")
|
||||
.flat(Type::FLOAT, "filter_radius");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_shadow)
|
||||
.define("DRW_VIEW_LEN", STRINGIFY(SHADOW_VIEW_MAX))
|
||||
.define("MAT_SHADOW")
|
||||
.builtins(BuiltinBits::VIEWPORT_INDEX)
|
||||
.vertex_out(eevee_surf_shadow_clipping_iface)
|
||||
.vertex_out(eevee_surf_shadow_flat_iface)
|
||||
.storage_buf(SHADOW_RENDER_VIEW_BUF_SLOT,
|
||||
Qualifier::READ,
|
||||
"ShadowRenderView",
|
||||
|
||||
@@ -234,13 +234,12 @@ static void test_eevee_shadow_tag_update()
|
||||
{
|
||||
ShadowTileMap tilemap(0 * SHADOW_TILEDATA_PER_TILEMAP);
|
||||
tilemap.sync_cubeface(
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f, 0.0f);
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
{
|
||||
ShadowTileMap tilemap(1 * SHADOW_TILEDATA_PER_TILEMAP);
|
||||
tilemap.sync_orthographic(
|
||||
float4x4::identity(), int2(0), 1, 0.0f, 0.0f, SHADOW_PROJECTION_CLIPMAP);
|
||||
tilemap.sync_orthographic(float4x4::identity(), int2(0), 1, 0.0f, SHADOW_PROJECTION_CLIPMAP);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
|
||||
@@ -1544,7 +1543,7 @@ static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap)
|
||||
{
|
||||
ShadowTileMap tilemap(0);
|
||||
tilemap.sync_cubeface(
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f, 0.0f);
|
||||
LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f);
|
||||
tilemaps_data.append(tilemap);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user