Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2017-04-21 17:43:14 +0300
committerClément Foucault <foucault.clem@gmail.com>2017-04-21 17:43:35 +0300
commit8ac1f03f411c98efe4ca4d568a2b231ff59b2505 (patch)
treec8d04a38b5dd83a90100e8819db90186c84830f5 /source/blender/draw/engines/eevee/eevee_lights.c
parentcc2d501642555616f3e8808bdbaa72f9c752e1a1 (diff)
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.
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_lights.c')
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c146
1 files changed, 121 insertions, 25 deletions
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);
}