Commit: 8ac1f03f411c98efe4ca4d568a2b231ff59b2505
Author: Clément Foucault
Date:   Fri Apr 21 16:43:14 2017 +0200
Branches: blender2.8
https://developer.blender.org/rB8ac1f03f411c98efe4ca4d568a2b231ff59b2505

Eevee: Cascaded Shadow Maps, follow up.

- Compute coarse bounding box of split frustum. Can be improved
- Make use of 4 cascade.
- View dependant glitches are fixed.
- Optimized shader code.

===================================================================

M       source/blender/draw/engines/eevee/eevee_lights.c
M       source/blender/draw/engines/eevee/eevee_private.h
M       source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
M       source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl

===================================================================

diff --git a/source/blender/draw/engines/eevee/eevee_lights.c 
b/source/blender/draw/engines/eevee/eevee_lights.c
index 7c4a5523ecd..cb4032f198c 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -295,14 +295,65 @@ static void eevee_shadow_map_setup(Object *ob, 
EEVEE_LampsInfo *linfo, EEVEE_Lam
 
 #define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
 
+static void frustum_min_bounding_sphere(const float corners[8][4], float 
r_center[3], float *r_radius)
+{
+#if 0 /* Simple solution but waist too much space. */
+       float minvec[3], maxvec[3];
+
+       /* compute the bounding box */
+       INIT_MINMAX(minvec, maxvec);
+       for (int i = 0; i < 8; ++i)     {
+               minmax_v3v3_v3(minvec, maxvec, corners[i]);
+       }
+
+       /* compute the bounding sphere of this box */
+       r_radius = len_v3v3(minvec, maxvec) * 0.5f;
+       add_v3_v3v3(r_center, minvec, maxvec);
+       mul_v3_fl(r_center, 0.5f);
+#else
+       /* Make the bouding sphere always centered on the front diagonal */
+       add_v3_v3v3(r_center, corners[4], corners[7]);
+       mul_v3_fl(r_center, 0.5f);
+       *r_radius = len_v3v3(corners[0], r_center);
+
+       /* Search the largest distance between the sphere center
+        * and the front plane corners. */
+       for (int i = 0; i < 4; ++i) {
+               float rad = len_v3v3(corners[4+i], r_center);
+               if (rad > *r_radius) {
+                       *r_radius = rad;
+               }
+       }
+#endif
+}
+
 static void eevee_shadow_cascade_setup(Object *ob, EEVEE_LampsInfo *linfo, 
EEVEE_LampEngineData *led)
 {
        /* Camera Matrices */
        float persmat[4][4], persinv[4][4];
+       float viewprojmat[4][4], projinv[4][4];
+       float near, far;
+       float near_v[4] = {0.0f, 0.0f, -1.0f, 1.0f};
+       float far_v[4] = {0.0f, 0.0f,  1.0f, 1.0f};
+       bool is_persp = DRW_viewport_is_persp_get();
+       DRW_viewport_matrix_get(persmat, DRW_MAT_PERS);
+       invert_m4_m4(persinv, persmat);
+       /* FIXME : Get near / far from Draw manager? */
+       DRW_viewport_matrix_get(viewprojmat, DRW_MAT_WIN);
+       invert_m4_m4(projinv, viewprojmat);
+       mul_m4_v4(projinv, near_v);
+       mul_m4_v4(projinv, far_v);
+       near = near_v[2];
+       far = far_v[2]; /* TODO: Should be a shadow parameter */
+       if (is_persp) {
+               near /= near_v[3];
+               far /= far_v[3];
+       }
+
        /* Lamps Matrices */
        float viewmat[4][4], projmat[4][4];
-       float minvec[3], maxvec[3];
        int cascade_ct = MAX_CASCADE_NUM;
+       float shadow_res = 512.0f; /* TODO parameter */
 
        EEVEE_ShadowCascadeData *evscp = (EEVEE_ShadowCascadeData *)led->sto;
        EEVEE_Light *evli = linfo->light_data + evscp->light_id;
@@ -313,28 +364,56 @@ static void eevee_shadow_cascade_setup(Object *ob, 
EEVEE_LampsInfo *linfo, EEVEE
         * the view frustum into several sub-frustum
         * that are individually receiving one shadow map */
 
+       /* init near/far */
+       for (int c = 0; c < MAX_CASCADE_NUM; ++c) {
+               evsh->split[c] = far;
+       }
+
+       /* Compute split planes */
+       float splits_ndc[MAX_CASCADE_NUM + 1];
+       splits_ndc[0] = -1.0f;
+       splits_ndc[cascade_ct] = 1.0f;
+       for (int c = 1; c < cascade_ct; ++c) {
+               const float lambda = 0.8f; /* TODO : Parameter */
+
+               /* View Space */
+               float linear_split = LERP(((float)(c) / (float)cascade_ct), 
near, far);
+               float exp_split = near * powf(far / near, (float)(c) / 
(float)cascade_ct);
+
+               if (is_persp) {
+                       evsh->split[c-1] = LERP(lambda, linear_split, 
exp_split);
+               }
+               else {
+                       evsh->split[c-1] = linear_split;
+               }
+
+               /* NDC Space */
+               float p[4] = {1.0f, 1.0f, evsh->split[c-1], 1.0f};
+               mul_m4_v4(viewprojmat, p);
+               splits_ndc[c] = p[2];
+
+               if (is_persp) {
+                       splits_ndc[c] /= p[3];
+               }
+       }
+
        /* For each cascade */
        for (int c = 0; c < cascade_ct; ++c) {
-               float splitnear = LERP(((float)(c) / cascade_ct), -1.0f, 1.0f);
-               float splitfar = LERP(((float)(c + 1) / cascade_ct), -1.0f, 
1.0f);
-
                /* Given 8 frustrum corners */
                float corners[8][4] = {
-                       /* Far Cap */
-                       {-1.0f, -1.0f, splitfar, 1.0f},
-                       { 1.0f, -1.0f, splitfar, 1.0f},
-                       {-1.0f,  1.0f, splitfar, 1.0f},
-                       { 1.0f,  1.0f, splitfar, 1.0f},
                        /* Near Cap */
-                       {-1.0f, -1.0f, splitnear, 1.0f},
-                       { 1.0f, -1.0f, splitnear, 1.0f},
-                       {-1.0f,  1.0f, splitnear, 1.0f},
-                       { 1.0f,  1.0f, splitnear, 1.0f}
+                       {-1.0f, -1.0f, splits_ndc[c], 1.0f},
+                       { 1.0f, -1.0f, splits_ndc[c], 1.0f},
+                       {-1.0f,  1.0f, splits_ndc[c], 1.0f},
+                       { 1.0f,  1.0f, splits_ndc[c], 1.0f},
+                       /* Far Cap */
+                       {-1.0f, -1.0f, splits_ndc[c+1], 1.0f},
+                       { 1.0f, -1.0f, splits_ndc[c+1], 1.0f},
+                       {-1.0f,  1.0f, splits_ndc[c+1], 1.0f},
+                       { 1.0f,  1.0f, splits_ndc[c+1], 1.0f}
                };
 
                /* Transform them into world space */
-               DRW_viewport_matrix_get(persmat, DRW_MAT_PERS);
-               invert_m4_m4(persinv, persmat);
                for (int i = 0; i < 8; ++i)     {
                        mul_m4_v4(persinv, corners[i]);
                        mul_v3_fl(corners[i], 1.0f / corners[i][3]);
@@ -351,21 +430,38 @@ static void eevee_shadow_cascade_setup(Object *ob, 
EEVEE_LampsInfo *linfo, EEVEE
                        mul_m4_v4(viewmat, corners[i]);
                }
 
-               /* compute the minimum bounding box */
-               INIT_MINMAX(minvec, maxvec);
-               for (int i = 0; i < 8; ++i)     {
-                       minmax_v3v3_v3(minvec, maxvec, corners[i]);
-               }
+               float center[3], radius;
+               frustum_min_bounding_sphere(corners, center, &radius);
+
+               /* Snap projection center to nearest texel to cancel shimering. 
*/
+               float shadow_origin[2], shadow_texco[2];
+               mul_v2_v2fl(shadow_origin, center, shadow_res / (2.0f * 
radius)); /* Light to texture space. */
+
+               /* Find the nearest texel. */
+               shadow_texco[0] = round(shadow_origin[0]);
+               shadow_texco[1] = round(shadow_origin[1]);
+
+               /* Compute offset. */
+               sub_v2_v2(shadow_texco, shadow_origin);
+               mul_v2_fl(shadow_texco, (2.0f * radius) / shadow_res); /* 
Texture to light space. */
 
-               /* expand the bounding box to cover light range */
-               orthographic_m4(projmat, minvec[0], maxvec[0], minvec[1], 
maxvec[1], la->clipsta, la->clipend);
+               /* Apply offset. */
+               add_v2_v2(center, shadow_texco);
+
+               /* Expand the projection to cover frustum range */
+               orthographic_m4(projmat,
+                               center[0] - radius,
+                               center[0] + radius,
+                               center[1] - radius,
+                               center[1] + radius,
+                               la->clipsta, la->clipend);
 
                mul_m4_m4m4(evscp->viewprojmat[c], projmat, viewmat);
                mul_m4_m4m4(evsh->shadowmat[c], texcomat, 
evscp->viewprojmat[c]);
-       }
 
-       evsh->bias = 0.005f * la->bias;
-       evsh->count = (float)cascade_ct;
+               /* TODO modify bias depending on the cascade radius */
+               evsh->bias[c] = 0.005f * la->bias;
+       }
 
        evli->shadowid = (float)(MAX_SHADOW_CUBE + MAX_SHADOW_MAP + 
evscp->shadow_id);
 }
diff --git a/source/blender/draw/engines/eevee/eevee_private.h 
b/source/blender/draw/engines/eevee/eevee_private.h
index 6f249ea888d..48c991aecf9 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -112,9 +112,8 @@ typedef struct EEVEE_ShadowMap {
 
 typedef struct EEVEE_ShadowCascade {
        float shadowmat[MAX_CASCADE_NUM][4][4]; /* World->Lamp->NDC->Tex : used 
for sampling the shadow map. */
-       float bias, count, pad[2];
-       float near[MAX_CASCADE_NUM];
-       float far[MAX_CASCADE_NUM];
+       float split[4];
+       float bias[4];
 } EEVEE_ShadowCascade;
 
 typedef struct EEVEE_ShadowRender {
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl 
b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index b907adc2323..5762ca45b71 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -59,16 +59,11 @@ struct ShadowMapData {
 
 struct ShadowCascadeData {
        mat4 shadowmat[MAX_CASCADE_NUM];
-       vec4 bias_count;
-       float near[MAX_CASCADE_NUM];
-       float far[MAX_CASCADE_NUM];
+       /* arrays of float are not aligned so use vec4 */
+       vec4 split_distances;
+       vec4 bias;
 };
 
-/* convenience aliases */
-#define sh_cascade_bias   bias_count.x
-#define sh_cascade_count  bias_count.y
-
-
 struct AreaData {
        vec3 corner[4];
        float solid_angle;
diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl 
b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
index 02111d6fcba..7e0c0462b30 100644
--- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl
@@ -91,17 +91,38 @@ float light_visibility(LightData ld, ShadingData sd)
                float shid = ld.l_shadowid - (MAX_SHADOW_CUBE + MAX_SHADOW_MAP);
                ShadowCascadeData smd = shadows_cascade_data[int(shid)];
 
-               for (int i = 0; i < int(smd.sh_cascade_count); i++) {
-                       vec4 shpos = smd.shadowmat[i] * vec4(sd.W, 1.0);
-                       shpos.z -= smd.sh_cascade_bias * shpos.w;
-                       shpos.xyz /= shpos.w;
-
-                       if (shpos.w > 0.0 && min(shpos.x, shpos.y) > 0.0 && 
max(shpos.x, shpos.y) < 1.0) {
-                               vis *= texture(shadowCascades, vec4(shpos.xy, 
shid * float(MAX_CASCADE_NUM) + float(i), shpos.z));
-                               // vis = float(i) / float(MAX_CASCADE_NUM);
-                               break;
-                       }
+               /* Finding Cascade index */
+               vec4 z = vec4(-dot(cameraPos - worldPosition, normalize(eye)));
+               vec4 comp = step(z, smd.split_distances);
+               float cascade = dot(comp, comp);
+               mat4 shadowmat;
+               float bias;
+
+               /* Manual Unrolling of a loop for better performance.
+                * Doing fetch directly with cascade index leads to
+                * major performance impact. (0.27ms -> 10.0ms for 1 light) */
+               if (cascade == 0.0) {
+                       shadowmat = smd.shadowmat[0];
+                       bias = smd.bias[0];
+               }
+               else if (cascade == 1.0) {
+                       shadowmat = smd.shadowmat[1];
+                       bias = smd.bias[1];
+               }
+               else if (cascade == 2.0) {
+                       shadowmat = smd.shadowmat[2];
+                       bias = smd.bias[2];
+               }
+               else {
+                       shadowmat = smd.shadowmat[3];
+                       bias = smd.bias[3];
                }
+
+               vec4 shpos = shadowmat * vec4(sd.W, 1.0);
+               shpos.z -= bias * shpos.w;
+               shpos.xyz /= shpos.w;
+
+               vis *= texture(shadowCascades, vec4(shpos.xy, shid * 
float(MAX_CASCADE_NUM) + cascade, shpos.z));
        }
        else if (ld.l_shadowid >= MAX_SHADOW_CUBE) {
                /* Shadow Map */

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to