EEVEE Next: Tag shadowmap usage for transparent object volumes #104580

Merged
Miguel Pozo merged 31 commits from pragma37/blender:pull-eevee-shadows-tag-usage-transparent into main 2023-03-08 13:51:35 +01:00
7 changed files with 364 additions and 87 deletions

View File

@ -722,7 +722,7 @@ void ShadowModule::begin_sync()
PassMain::Sub &sub = pass.sub("Transparent");
/* WORKAROUND: The DRW_STATE_WRITE_STENCIL is here only to avoid enabling the rasterizer
* discard inside draw manager. */
sub.state_set(DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WRITE_STENCIL);
sub.state_set(DRW_STATE_CULL_FRONT | DRW_STATE_WRITE_STENCIL);
sub.state_stencil(0, 0, 0);
sub.framebuffer_set(&usage_tag_fb);
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT));
@ -730,6 +730,10 @@ void ShadowModule::begin_sync()
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
sub.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
sub.push_constant("pixel_world_radius", &pixel_world_radius_);
sub.push_constant("fb_resolution", &usage_tag_fb_resolution_);
sub.push_constant("fb_lod", &usage_tag_fb_lod_);
inst_.hiz_buffer.bind_resources(&sub);
inst_.lights.bind_resources(&sub);
box_batch_ = DRW_cache_cube_get();
@ -1092,12 +1096,17 @@ void ShadowModule::set_view(View &view)
int3 target_size = inst_.render_buffers.depth_tx.size();
dispatch_depth_scan_size_ = math::divide_ceil(target_size, int3(SHADOW_DEPTH_SCAN_GROUP_SIZE));
tilemap_projection_ratio_ = tilemap_pixel_radius() /
screen_pixel_radius(view, int2(target_size));
pixel_world_radius_ = screen_pixel_radius(view, int2(target_size));
tilemap_projection_ratio_ = tilemap_pixel_radius() / pixel_world_radius_;
usage_tag_fb_resolution_ = math::divide_ceil(int2(target_size),
int2(std::exp2(usage_tag_fb_lod_)));
usage_tag_fb.ensure(usage_tag_fb_resolution_);
usage_tag_fb.ensure(int2(target_size));
render_fb_.ensure(int2(SHADOW_TILEMAP_RES * shadow_page_size_));
inst_.hiz_buffer.update();
bool tile_update_remains = true;
while (tile_update_remains) {
DRW_stats_group_start("Shadow");

View File

@ -216,6 +216,9 @@ class ShadowModule {
int3 dispatch_depth_scan_size_;
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
float tilemap_projection_ratio_;
float pixel_world_radius_;
int2 usage_tag_fb_resolution_;
int usage_tag_fb_lod_ = 5;
/* Statistics that are read back to CPU after a few frame (to avoid stall). */
SwapChain<ShadowStatisticsBuf, 5> statistics_buf_;

View File

@ -3,13 +3,120 @@
* Virtual shadowmapping: Usage tagging
*
* Shadow pages are only allocated if they are visible.
* This pass scan the depth buffer and tag all tiles that are needed for light shadowing as
* needed.
* This ray-marches the current fragment along the bounds depth and tags all the intersected shadow
* tiles.
*/
#pragma BLENDER_REQUIRE(eevee_shadow_tag_usage_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_debug_shape_lib.glsl)
float ray_aabb(vec3 ray_origin, vec3 ray_direction, vec3 aabb_min, vec3 aabb_max)
{
/* https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_aabb.html */
vec3 t_mins = (aabb_min - ray_origin) / ray_direction;
vec3 t_maxs = (aabb_max - ray_origin) / ray_direction;
float t_min = max_v3(min(t_mins, t_maxs));
float t_max = min_v3(max(t_mins, t_maxs));
/* AABB is in the opposite direction. */
if (t_max < 0.0) {
return -1.0;
}
/* No intersection. */
if (t_min > t_max) {
return -1.0;
}
/* The ray origin is inside the aabb. */
if (t_min < 0.0) {
/* For regular ray casting we would return t_max here,
* but we want to ray cast against the box volume, not just the surface. */
return 0.0;
}
return t_min;
}
float pixel_size_at(float linear_depth)
{
float pixel_size = pixel_world_radius;
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
pixel_size *= max(0.01, linear_depth);
}
return pixel_size * exp2(fb_lod);
}
void step_bounding_sphere(vec3 vs_near_plane,
vec3 vs_view_direction,
float near_t,
float far_t,
out vec3 sphere_center,
out float sphere_radius)
{
float near_pixel_size = pixel_size_at(near_t);
vec3 near_center = vs_near_plane + vs_view_direction * near_t;
float far_pixel_size = pixel_size_at(far_t);
pragma37 marked this conversation as resolved

Be conservative in Z too. Bias far_box_t by step_size to have one more iteration before and after.

This is for the same issue as the full 1px inflate.

Be conservative in Z too. Bias `far_box_t` by step_size to have one more iteration before and after. This is for the same issue as the full 1px inflate.
vec3 far_center = vs_near_plane + vs_view_direction * far_t;
sphere_center = mix(near_center, far_center, 0.5);
sphere_radius = 0;
for (int x = -1; x <= 1; x += 2) {
for (int y = -1; y <= 1; y += 2) {
vec3 near_corner = near_center + (near_pixel_size * 0.5 * vec3(x, y, 0));
sphere_radius = max(sphere_radius, len_squared(near_corner - sphere_center));
vec3 far_corner = far_center + (far_pixel_size * 0.5 * vec3(x, y, 0));
sphere_radius = max(sphere_radius, len_squared(far_corner - sphere_center));
}
}
sphere_center = point_view_to_world(sphere_center);
sphere_radius = sqrt(sphere_radius);
}
void main()
pragma37 marked this conversation as resolved

Use exp2 instead of pow(2, x). Try to apply this everywhere.

Use `exp2` instead of `pow(2, x)`. Try to apply this everywhere.
{
shadow_tag_usage(interp.vP, interp.P, gl_FragCoord.xy);
vec2 screen_uv = gl_FragCoord.xy / vec2(fb_resolution);
float opaque_depth = texelFetch(hiz_tx, int2(gl_FragCoord.xy), fb_lod).r;
vec3 ws_opaque = get_world_space_from_depth(screen_uv, opaque_depth);
vec3 ws_near_plane = get_world_space_from_depth(screen_uv, 0);
vec3 ws_view_direction = normalize(interp.P - ws_near_plane);
vec3 vs_near_plane = get_view_space_from_depth(screen_uv, 0);
vec3 vs_view_direction = normalize(interp.vP - vs_near_plane);
vec3 ls_near_plane = point_world_to_object(ws_near_plane);
vec3 ls_view_direction = normalize(point_world_to_object(interp.P) - ls_near_plane);
/* TODO (Miguel Pozo): We could try to ray-cast against the non-inflated bounds first,
* and fallback to the inflated ones if theres no hit.
* The inflated bounds can cause unnecesary extra steps. */
float ls_near_box_t = ray_aabb(
ls_near_plane, ls_view_direction, interp.ls_aabb_min, interp.ls_aabb_max);
vec3 ls_near_box = ls_near_plane + ls_view_direction * ls_near_box_t;
vec3 ws_near_box = point_object_to_world(ls_near_box);
float near_box_t = distance(ws_near_plane, ws_near_box);
float far_box_t = distance(ws_near_plane, interp.P);
/* Depth test. */
far_box_t = min(far_box_t, distance(ws_near_plane, ws_opaque));
/* Ray march from the front to the back of the bbox, and tag shadow usage along the way. */
float step_size;
for (float t = near_box_t; t <= far_box_t; t += step_size) {
/* Ensure we don't get past far_box_t. */
t = min(t, far_box_t);
step_size = pixel_size_at(t);
vec3 P = ws_near_plane + (ws_view_direction * t);
float step_radius;
step_bounding_sphere(vs_near_plane, vs_view_direction, t, t + step_size, P, step_radius);
vec3 vP = point_world_to_view(P);
shadow_tag_usage(vP, P, ws_view_direction, step_radius, t, gl_FragCoord.xy * exp2(fb_lod));
}
}

View File

@ -3,8 +3,7 @@
* Virtual shadowmapping: Usage tagging
*
* Shadow pages are only allocated if they are visible.
* This pass scan the depth buffer and tag all tiles that are needed for light shadowing as
* needed.
* This contains the common logic used for tagging shadows for opaque and transparent receivers.
*/
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
@ -14,7 +13,18 @@
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
void shadow_tag_usage_tilemap(uint l_idx, vec3 P, float dist_to_cam, const bool is_directional)
void shadow_tag_usage_tile(LightData light, ivec2 tile_co, int lod, int tilemap_index)
{
if (tilemap_index > light_tilemap_max_get(light)) {
return;
}
tile_co >>= lod;
int tile_index = shadow_tile_offset(tile_co, tilemaps_buf[tilemap_index].tiles_index, lod);
atomicOr(tiles_buf[tile_index], SHADOW_IS_USED);
}
void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radius)
{
pragma37 marked this conversation as resolved

dist_to_cam is not used.

`dist_to_cam` is not used.
LightData light = light_buf[l_idx];
@ -22,83 +32,141 @@ void shadow_tag_usage_tilemap(uint l_idx, vec3 P, float dist_to_cam, const bool
return;
}
int lod = 0;
ivec2 tile_co;
int tilemap_index = light.tilemap_index;
if (is_directional) {
vec3 lP = shadow_world_to_local(light, P);
vec3 lP = shadow_world_to_local(light, P);
if (radius == 0) {
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
tile_co = coord.tile_coord;
tilemap_index = coord.tilemap_index;
shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index);
}
else {
vec3 lP = light_world_to_local(light, P - light._position);
float dist_to_light = length(lP);
if (dist_to_light > light.influence_radius_max) {
return;
}
if (light.type == LIGHT_SPOT) {
/* Early out if out of cone. */
float angle_tan = length(lP.xy / dist_to_light);
if (angle_tan > light.spot_tan) {
return;
vec3 start_lP = shadow_world_to_local(light, P - V * radius);
vec3 end_lP = shadow_world_to_local(light, P + V * radius);
int min_level = shadow_directional_level(light, start_lP - light._position);
int max_level = shadow_directional_level(light, end_lP - light._position);
for (int level = min_level; level <= max_level; level++) {
ShadowCoordinates coord_min = shadow_directional_coordinates_at_level(
light, lP - vec3(radius, radius, 0), level);
ShadowCoordinates coord_max = shadow_directional_coordinates_at_level(
light, lP + vec3(radius, radius, 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);
}
}
}
else if (is_area_light(light.type)) {
/* Early out if on the wrong side. */
if (lP.z > 0.0) {
return;
}
}
/* How much a shadow map pixel covers a final image pixel.
* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
* resolution in screen space. */
float footprint_ratio = dist_to_light;
/* Project the radius to the screen. 1 unit away from the camera the same way
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
footprint_ratio /= dist_to_cam;
}
/* Apply resolution ratio. */
footprint_ratio *= tilemap_projection_ratio;
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);
tile_co = coord.tile_coord;
tilemap_index = coord.tilemap_index;
lod = int(ceil(-log2(footprint_ratio) + tilemaps_buf[tilemap_index].lod_bias));
lod = clamp(lod, 0, SHADOW_TILEMAP_LOD);
}
tile_co >>= lod;
}
if (tilemap_index > light_tilemap_max_get(light)) {
void shadow_tag_usage_tilemap_punctual(uint l_idx, vec3 P, vec3 V, float dist_to_cam, float radius)
{
LightData light = light_buf[l_idx];
if (light.tilemap_index == LIGHT_NO_SHADOW) {
return;
}
int tile_index = shadow_tile_offset(tile_co, tilemaps_buf[tilemap_index].tiles_index, lod);
atomicOr(tiles_buf[tile_index], SHADOW_IS_USED);
vec3 lP = light_world_to_local(light, P - light._position);
float dist_to_light = length(lP) - radius;
if (dist_to_light > light.influence_radius_max) {
return;
}
if (light.type == LIGHT_SPOT) {
/* Early out if out of cone. */
float angle_tan = length(lP.xy / dist_to_light);
if (angle_tan > light.spot_tan) {
return;
}
}
else if (is_area_light(light.type)) {
/* Early out if on the wrong side. */
if (lP.z - radius > 0.0) {
return;
}
}
/* How much a shadow map pixel covers a final image pixel.
* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
* resolution in screen space. */
float footprint_ratio = dist_to_light;
/* Project the radius to the screen. 1 unit away from the camera the same way
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
footprint_ratio /= dist_to_cam;
}
/* Apply resolution ratio. */
footprint_ratio *= tilemap_projection_ratio;
if (radius == 0) {
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);
int lod = int(ceil(-log2(footprint_ratio) + tilemaps_buf[coord.tilemap_index].lod_bias));
lod = clamp(lod, 0, SHADOW_TILEMAP_LOD);
shadow_tag_usage_tile(light, coord.tile_coord, lod, coord.tilemap_index);
}
else {
uint faces = 0u;
for (int x = -1; x <= 1; x += 2) {
for (int y = -1; y <= 1; y += 2) {
for (int z = -1; z <= 1; z += 2) {
vec3 _lP = lP + vec3(x, y, z) * radius;
faces |= 1u << shadow_punctual_face_index_get(_lP);
}
}
}
for (int face_id = 0; face_id < 6; face_id++) {
if ((faces & (1u << uint(face_id))) == 0u) {
continue;
}
int tilemap_index = light.tilemap_index + face_id;
int lod = int(ceil(-log2(footprint_ratio) + tilemaps_buf[tilemap_index].lod_bias));
lod = clamp(lod, 0, SHADOW_TILEMAP_LOD);
vec3 _lP = shadow_punctual_local_position_to_face_local(face_id, lP);
vec3 offset = vec3(radius, radius, 0);
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);
}
}
}
}
}
/**
* \a radius Radius of the tagging area in world space.
pragma37 marked this conversation as resolved

Document the parameters of this functions. This should be enough to explain the need for radius.

Document the parameters of this functions. This should be enough to explain the need for radius.
* Used for downsampled/ray-marched tagging, so all the shadowmap texels covered get correctly
* tagged.
*/
void shadow_tag_usage(vec3 vP, vec3 P, vec3 V, float radius, float dist_to_cam, vec2 pixel)
{
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
shadow_tag_usage_tilemap_directional(l_idx, P, V, radius);
}
LIGHT_FOREACH_END
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
shadow_tag_usage_tilemap_punctual(l_idx, P, V, dist_to_cam, radius);
}
LIGHT_FOREACH_END
}
void shadow_tag_usage(vec3 vP, vec3 P, vec2 pixel)
{
float dist_to_cam = length(vP);
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
shadow_tag_usage_tilemap(l_idx, P, dist_to_cam, true);
}
LIGHT_FOREACH_END
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
shadow_tag_usage_tilemap(l_idx, P, dist_to_cam, false);
}
LIGHT_FOREACH_END
shadow_tag_usage(vP, P, vec3(0), 0, dist_to_cam, pixel);
}

View File

@ -3,20 +3,89 @@
* Virtual shadowmapping: Usage tagging
*
* Shadow pages are only allocated if they are visible.
* This renders bounding boxes for transparent objects in order to tag the correct shadows.
* This renders the bounding boxes for transparent objects in order to tag the correct shadows.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
#pragma BLENDER_REQUIRE(common_debug_shape_lib.glsl)
/* Inflate bounds by half a pixel as a conservative rasterization alternative,
* to ensure the tiles needed by all LOD0 pixels get tagged */
void inflate_bounds(vec3 ls_center, inout vec3 P, inout vec3 lP)
{
vec3 vP = point_world_to_view(P);
float inflate_scale = pixel_world_radius * exp2(fb_lod);
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
pragma37 marked this conversation as resolved

I think we need a fullpixel here.

Imagine a really tiny box.

      tagged    |   not tagged    < Tiles tagging. Tilemap boundary at the middle
 --------------T|---------------  < the shadow tilemap at the correct LOD
            |----x----|           < the half inflated bounds (covers 1 pixel)
                 x                < the object (infinitesimal)
|----x----|----x----|----x----|   < Screen LOD where we raster the bboxes

A tile map boundary cannot be mitigated by tagging a lower LOD selection.

Inflating a whole pixel:

      tagged    |    tagged       < Tiles tagging. Tilemap boundary at the middle
 --------------T|--------T------  < the shadow tilemap at the correct LOD
       |---------x--------|       < the FULL inflated bounds (covers 2 pixel)
                 x                < the object (infinitesimal)
|----x----|----x----|----x----|   < Screen LOD where we raster the bboxes
I think we need a fullpixel here. Imagine a really tiny box. ``` tagged | not tagged < Tiles tagging. Tilemap boundary at the middle --------------T|--------------- < the shadow tilemap at the correct LOD |----x----| < the half inflated bounds (covers 1 pixel) x < the object (infinitesimal) |----x----|----x----|----x----| < Screen LOD where we raster the bboxes ``` A tile map boundary cannot be mitigated by tagging a lower LOD selection. Inflating a whole pixel: ``` tagged | tagged < Tiles tagging. Tilemap boundary at the middle --------------T|--------T------ < the shadow tilemap at the correct LOD |---------x--------| < the FULL inflated bounds (covers 2 pixel) x < the object (infinitesimal) |----x----|----x----|----x----| < Screen LOD where we raster the bboxes ```
inflate_scale *= -vP.z;
}
/* Half-pixel. */
inflate_scale *= 0.5;
pragma37 marked this conversation as resolved

This only works because max_v2 is a define. Use vs_inflate_vector.xy to avoid the confusion.
Also not entierly sure what's this about. Can you add a comment on this?

This only works because `max_v2` is a define. Use `vs_inflate_vector.xy` to avoid the confusion. Also not entierly sure what's this about. Can you add a comment on this?
vec3 vs_inflate_vector = normal_object_to_view(sign(lP - ls_center));
vs_inflate_vector.z = 0;
/* Scale the vector so the largest axis length is 1 */
vs_inflate_vector /= max_v2(abs(vs_inflate_vector.xy));
vs_inflate_vector *= inflate_scale;
vP += vs_inflate_vector;
P = point_view_to_world(vP);
lP = point_world_to_object(P);
}
void main()
{
ObjectBounds bounds = bounds_buf[drw_ResourceID];
PASS_RESOURCE_ID
interp.P = bounds.bounding_corners[0].xyz;
interp.P += bounds.bounding_corners[1].xyz * pos.x;
interp.P += bounds.bounding_corners[2].xyz * pos.y;
interp.P += bounds.bounding_corners[3].xyz * pos.z;
const ObjectBounds bounds = bounds_buf[resource_id];
Box box = shape_box(bounds.bounding_corners[0].xyz,
bounds.bounding_corners[0].xyz + bounds.bounding_corners[1].xyz,
bounds.bounding_corners[0].xyz + bounds.bounding_corners[2].xyz,
bounds.bounding_corners[0].xyz + bounds.bounding_corners[3].xyz);
vec3 ws_aabb_min = bounds.bounding_corners[0].xyz;
vec3 ws_aabb_max = bounds.bounding_corners[0].xyz + bounds.bounding_corners[1].xyz +
bounds.bounding_corners[2].xyz + bounds.bounding_corners[3].xyz;
vec3 ls_center = point_world_to_object((ws_aabb_min + ws_aabb_max) / 2.0);
vec3 ls_conservative_min = vec3(FLT_MAX);
vec3 ls_conservative_max = vec3(-FLT_MAX);
for (int i = 0; i < 8; i++) {
vec3 P = box.corners[i];
vec3 lP = point_world_to_object(P);
inflate_bounds(ls_center, P, lP);
ls_conservative_min = min(ls_conservative_min, lP);
ls_conservative_max = max(ls_conservative_max, lP);
}
pragma37 marked this conversation as resolved

Im' not sure why you are changing that for the debug visuals.

Im' not sure why you are changing that for the debug visuals.
Review

Because to debug the conservative rasterization I raycast against the original aabb in the fragment shader. If there's no hit, then it's a "conservatively rasterized" pixel.

If we keep the debugging visualizations that will have to be sent through its own varying.

Because to debug the conservative rasterization I raycast against the original aabb in the fragment shader. If there's no hit, then it's a "conservatively rasterized" pixel. If we keep the debugging visualizations that will have to be sent through its own varying.
interp.ls_aabb_min = ls_conservative_min;
interp.ls_aabb_max = ls_conservative_max;
vec3 lP = mix(ls_conservative_min, ls_conservative_max, max(vec3(0), pos));
interp.P = point_object_to_world(lP);
interp.vP = point_world_to_view(interp.P);
gl_Position = point_world_to_ndc(interp.P);
#if 0
if (gl_VertexID == 0) {
Box debug_box = shape_box(
ls_conservative_min,
ls_conservative_min + (ls_conservative_max - ls_conservative_min) * vec3(1, 0, 0),
ls_conservative_min + (ls_conservative_max - ls_conservative_min) * vec3(0, 1, 0),
ls_conservative_min + (ls_conservative_max - ls_conservative_min) * vec3(0, 0, 1));
for (int i = 0; i < 8; i++) {
debug_box.corners[i] = point_object_to_world(debug_box.corners[i]);
}
drw_debug(debug_box);
}
#endif
}

View File

@ -98,7 +98,12 @@ ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilem
return shadow_tile_unpack(tile_data);
}
/* This function should be the inverse of ShadowDirectional::coverage_get(). */
/**
* This function should be the inverse of ShadowDirectional::coverage_get().
*
* \a lP shading point position in light space, relative to the to camera position snapped to
* the smallest clipmap level (`shadow_world_to_local(light, P) - light._position`).
pragma37 marked this conversation as resolved

This is confusing since shadow_world_to_local is a function. Also put backticks ` around code inside comments.

This is confusing since `shadow_world_to_local` is a function. Also put backticks ` around code inside comments.
*/
int shadow_directional_level(LightData light, vec3 lP)
{
float lod;
@ -144,14 +149,11 @@ ivec2 shadow_decompress_grid_offset(eLightType light_type, ivec2 offset, int lev
}
/**
* \a lP shading point position in light space (world unit) and translated to camera position
* snapped to smallest clipmap level.
* \a lP shading point position in light space (`shadow_world_to_local(light, P)`).
*/
ShadowCoordinates shadow_directional_coordinates(LightData light, vec3 lP)
ShadowCoordinates shadow_directional_coordinates_at_level(LightData light, vec3 lP, int level)
{
ShadowCoordinates ret;
int level = shadow_directional_level(light, lP - light._position);
/* 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.clipmap_lod_min;
@ -173,6 +175,15 @@ ShadowCoordinates shadow_directional_coordinates(LightData light, vec3 lP)
return ret;
}
pragma37 marked this conversation as resolved

Well, light oriented world space is the light local space for directional. This is because they are considered always at the origin. Maybe this should be added somewhere.

Well, `light oriented world space` is the light local space for directional. This is because they are considered always at the origin. Maybe this should be added somewhere.
Review

Could you confirm if this is correct? 29d79d2210

Could you confirm if this is correct? https://projects.blender.org/blender/blender/commit/29d79d221017ae1fa6f9eb59d586ddc540c16ec6

Not quite.
lP here is shading point relative to the shadow origin in light space.
light._position isn't the actual position of the light in world space but the offset in light space to the shadow center, which is chosen by using the view near plane center point projected to the tile space of the shadow.

So lP is still a position and it is still in light local space but it hasn't the same origin.

Not quite. `lP` here is shading point relative to the shadow origin in light space. `light._position` isn't the actual position of the light in world space but the offset in light space to the shadow center, which is chosen by using the view near plane center point projected to the tile space of the shadow. So `lP` is still a position and it is still in light local space but it hasn't the same origin.
Review

Ok, so the original lP shading point position in light space (world unit) and translated to camera position snapped to smallest clipmap level. comment was correct, but it should be in shadow_directional_level instead of shadow_directional_coordinates.

Ok, so the original `lP shading point position in light space (world unit) and translated to camera position snapped to smallest clipmap level.` comment was correct, but it should be in `shadow_directional_level` instead of `shadow_directional_coordinates`.
/**
* \a lP shading point position in light space (`shadow_world_to_local(light, P)`).
*/
ShadowCoordinates shadow_directional_coordinates(LightData light, vec3 lP)
{
int level = shadow_directional_level(light, lP - light._position);
return shadow_directional_coordinates_at_level(light, lP, level);
}
/* Transform vector to face local coordinate. */
vec3 shadow_punctual_local_position_to_face_local(int face_id, vec3 lL)
{

View File

@ -54,7 +54,9 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque)
GPU_SHADER_INTERFACE_INFO(eevee_shadow_tag_transparent_iface, "interp")
.smooth(Type::VEC3, "P")
.smooth(Type::VEC3, "vP");
.smooth(Type::VEC3, "vP")
.flat(Type::VEC3, "ls_aabb_min")
.flat(Type::VEC3, "ls_aabb_max");
GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
.do_static_compilation(true)
@ -63,9 +65,17 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "pixel_world_radius")
.push_constant(Type::IVEC2, "fb_resolution")
.push_constant(Type::INT, "fb_lod")
.vertex_out(eevee_shadow_tag_transparent_iface)
.additional_info(
"eevee_shared", "draw_view", "draw_view_culling", "draw_modelmat_new", "eevee_light_data")
.additional_info("eevee_shared",
"draw_resource_id_varying",
"draw_view",
"draw_view_culling",
"draw_modelmat_new",
"eevee_hiz_data",
"eevee_light_data")
.vertex_source("eevee_shadow_tag_usage_vert.glsl")
.fragment_source("eevee_shadow_tag_usage_frag.glsl");