Workbench: Shadows: Fix cap being clipped by far plane.
authorClément Foucault <foucault.clem@gmail.com>
Mon, 4 Jun 2018 15:33:25 +0000 (17:33 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Mon, 4 Jun 2018 15:33:34 +0000 (17:33 +0200)
This was the last remaining problem with shadow volumes (that I know of).

Only extrude until we hit the far plane.

source/blender/draw/engines/workbench/shaders/workbench_shadow_vert.glsl
source/blender/draw/engines/workbench/workbench_deferred.c
source/blender/draw/engines/workbench/workbench_private.h
source/blender/draw/engines/workbench/workbench_studiolight.c
source/blender/draw/intern/DRW_render.h
source/blender/draw/intern/draw_manager_exec.c

index 3a61bf0a2860eeb628042587968c8dc7592aff68..50a721f948f6db06558f47fad1ddbac832b033e3 100644 (file)
@@ -3,6 +3,7 @@
 uniform mat4 ModelViewProjectionMatrix;
 
 uniform vec3 lightDirection = vec3(0.57, 0.57, -0.57);
+uniform float lightDistance = 1e4;
 
 in vec3 pos;
 
@@ -16,5 +17,5 @@ void main()
 {
        vData.pos = pos;
        vData.frontPosition = ModelViewProjectionMatrix * vec4(pos, 1.0);
-       vData.backPosition  = ModelViewProjectionMatrix * vec4(pos + lightDirection * INFINITE, 1.0);
+       vData.backPosition  = ModelViewProjectionMatrix * vec4(pos + lightDirection * lightDistance, 1.0);
 }
index f0052ba57fbfe8d6b573d10bf77e2d2c2744bc1a..ba5ab7f1cc35b1facd7fc2b036fa5035ac465ca5 100644 (file)
@@ -634,12 +634,15 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                                                grp = DRW_shgroup_create(e_data.shadow_pass_sh, psl->shadow_depth_pass_pass);
                                                        }
                                                        DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
+                                                       DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f);
                                                        DRW_shgroup_call_add(grp, geom_shadow, ob->obmat);
 #ifdef DEBUG_SHADOW_VOLUME
                                                        DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
 #endif
                                                }
                                                else {
+                                                       float extrude_distance = studiolight_object_shadow_distance(wpd, ob, engine_object_data);
+
                                                        /* TODO(fclem): only use caps if they are in the view frustum. */
                                                        const bool need_caps = true;
                                                        if (need_caps) {
@@ -650,6 +653,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                                                        grp = DRW_shgroup_create(e_data.shadow_caps_sh, psl->shadow_depth_fail_caps_pass);
                                                                }
                                                                DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
+                                                               DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
                                                                DRW_shgroup_call_add(grp, DRW_cache_object_surface_get(ob), ob->obmat);
                                                        }
 
@@ -660,6 +664,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                                                grp = DRW_shgroup_create(e_data.shadow_fail_sh, psl->shadow_depth_fail_pass);
                                                        }
                                                        DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1);
+                                                       DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance);
                                                        DRW_shgroup_call_add(grp, geom_shadow, ob->obmat);
 #ifdef DEBUG_SHADOW_VOLUME
                                                        DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
index ddcec29cd2197d55afb035c670a19adc74487cfc..00cb666643090f611da19a283872cf9723166eb5 100644 (file)
@@ -156,6 +156,7 @@ typedef struct WORKBENCH_PrivateData {
        float cached_shadow_direction[3];
        float shadow_mat[4][4];
        float shadow_inv[4][4];
+       float shadow_far_plane[4]; /* Far plane of the view frustum. */
        float shadow_near_corners[4][3]; /* Near plane corners in shadow space. */
        float shadow_near_min[3]; /* min and max of shadow_near_corners. allow fast test */
        float shadow_near_max[3];
@@ -186,7 +187,7 @@ typedef struct WORKBENCH_ObjectData {
        /* Accumulated recalc flags, which corresponds to ID->recalc flags. */
        int recalc;
        /* Shadow direction in local object space. */
-       float shadow_dir[3];
+       float shadow_dir[3], shadow_depth;
        float shadow_min[3], shadow_max[3]; /* Min, max in shadow space */
        BoundBox shadow_bbox;
        bool shadow_bbox_dirty;
@@ -232,6 +233,7 @@ void workbench_material_set_normal_world_matrix(
 void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd);
 void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]);
 bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed);
+float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed);
 bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed);
 
 /* workbench_data.c */
index 4db89717b2aba44f7b3cc99f4617b7685ddf4262..6451d1f57c8cb678b1a130b2c9ce14134e3cfa53 100644 (file)
@@ -76,9 +76,13 @@ void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_dire
                invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat);
 
                copy_v3_v3(wpd->cached_shadow_direction, light_direction);
-
        }
 
+       float planes[6][4];
+       DRW_culling_frustum_planes_get(planes);
+       /* we only need the far plane. */
+       copy_v4_v4(wpd->shadow_far_plane, planes[2]);
+
        BoundBox frustum_corners;
        DRW_culling_frustum_corners_get(&frustum_corners);
 
@@ -112,8 +116,9 @@ static BoundBox *studiolight_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd,
                        mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]);
                        minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner);
                }
+               oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2];
                /* Extend towards infinity. */
-               oed->shadow_max[2] += 1e4;
+               oed->shadow_max[2] += 1e4f;
 
                /* Get extended AABB in world space. */
                BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max);
@@ -131,6 +136,30 @@ bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, Object *
        return DRW_culling_box_test(shadow_bbox);
 }
 
+float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
+{
+       BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed);
+
+       int corners[4] = {0, 3, 4, 7};
+       float dist = 1e4f, dist_isect;
+       for (int i = 0; i < 4; ++i) {
+               if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]],
+                                      wpd->cached_shadow_direction,
+                                      wpd->shadow_far_plane,
+                                      &dist_isect, true))
+               {
+                       if (dist_isect < dist) {
+                               dist = dist_isect;
+                       }
+               }
+               else {
+                       /* All rays are parallels. If one fails, the other will too. */
+                       break;
+               }
+       }
+       return max_ii(dist - oed->shadow_depth, 0);
+}
+
 bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed)
 {
        /* Just to be sure the min, max are updated. */
index 081bae944d8ac4de08984be6007409d032ea7041..935a6d362f1ac4c80d5b1ec5b024cffd4b803f37 100644 (file)
@@ -506,6 +506,7 @@ bool DRW_culling_box_test(BoundBox *bbox);
 bool DRW_culling_plane_test(float plane[4]);
 
 void DRW_culling_frustum_corners_get(BoundBox *corners);
+void DRW_culling_frustum_planes_get(float planes[6][4]);
 
 /* Selection */
 void DRW_select_load_id(uint id);
index cb2cad8a36e493d75869ebcbc0ef6fa4c78642bd..f6b6438395de59bf10cf896d2f81600b156a2a9c 100644 (file)
@@ -516,12 +516,12 @@ static void draw_clipping_setup_from_view(void)
        for (int p = 0; p < 6; p++) {
                int q, r;
                switch (p) {
-                       case 0:  q = 1; r = 2; break;
-                       case 1:  q = 0; r = 5; break;
-                       case 2:  q = 1; r = 5; break;
-                       case 3:  q = 2; r = 6; break;
-                       case 4:  q = 0; r = 3; break;
-                       default: q = 4; r = 7; break;
+                       case 0:  q = 1; r = 2; break; /* -X */
+                       case 1:  q = 0; r = 5; break; /* -Y */
+                       case 2:  q = 1; r = 5; break; /* +Z (far) */
+                       case 3:  q = 2; r = 6; break; /* +Y */
+                       case 4:  q = 0; r = 3; break; /* -Z (near) */
+                       default: q = 4; r = 7; break; /* +X */
                }
                if (DST.frontface == GL_CW) {
                        SWAP(int, q, r);
@@ -713,6 +713,13 @@ void DRW_culling_frustum_corners_get(BoundBox *corners)
        memcpy(corners, &DST.clipping.frustum_corners, sizeof(BoundBox));
 }
 
+/* See draw_clipping_setup_from_view() for the plane order. */
+void DRW_culling_frustum_planes_get(float planes[6][4])
+{
+       draw_clipping_setup_from_view();
+       memcpy(planes, &DST.clipping.frustum_planes, sizeof(DST.clipping.frustum_planes));
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */