Eevee: Shadows: Add cubemap filtering and adaptive sample count.
authorClément Foucault <foucault.clem@gmail.com>
Sat, 9 Sep 2017 19:11:22 +0000 (21:11 +0200)
committerClément Foucault <foucault.clem@gmail.com>
Sun, 10 Sep 2017 01:09:45 +0000 (03:09 +0200)
Filter size is constant in world space and not dependant of shadow resolution.
The filter size is limited to the number of precomputed samples.

source/blender/draw/engines/eevee/eevee_lights.c
source/blender/draw/engines/eevee/eevee_private.h
source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
source/blender/draw/engines/eevee/shaders/shadow_copy_frag.glsl
source/blender/draw/engines/eevee/shaders/shadow_geom.glsl
source/blender/draw/engines/eevee/shaders/shadow_store_frag.glsl

index ca25e6e13b0300db92e9a49e24c036ed97aa34c6..396318872aad8f29e940ba9adc18f97e25ab9014 100644 (file)
@@ -135,6 +135,8 @@ void EEVEE_lights_init(EEVEE_SceneLayerData *sldata)
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_pool);
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_target);
                DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
+               DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
+               DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_blur);
 
                linfo->shadow_high_bitdepth = sh_high_bitdepth;
                linfo->shadow_method = sh_method;
@@ -169,7 +171,7 @@ void EEVEE_lights_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                psl->shadow_cube_store_pass = DRW_pass_create("Shadow Storage Pass", DRW_STATE_WRITE_COLOR);
 
                DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_store_cube_sh[linfo->shadow_method], psl->shadow_cube_store_pass);
-               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_target);
+               DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cube_blur);
                DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
                DRW_shgroup_uniform_float(grp, "shadowFilterSize", &linfo->filter_size, 1);
                DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@@ -902,8 +904,6 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                if (led->need_update) {
                        EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
 
-                       srd->shadow_samples_ct = 32.0f;
-                       srd->shadow_inv_samples_ct = 1.0f / srd->shadow_samples_ct;
                        srd->clip_near = la->clipsta;
                        srd->clip_far = la->clipend;
                        copy_v3_v3(srd->position, ob->obmat[3]);
@@ -924,12 +924,17 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                        /* Render shadow cube */
                        DRW_draw_pass(psl->shadow_cube_pass);
 
+                       /* 0.001f is arbitrary, but it should be relatively small so that filter size is not too big. */
+                       float filter_texture_size = la->soft * 0.001f;
+                       float filter_pixel_size = ceil(filter_texture_size / linfo->shadow_render_data.cube_texel_size);
+                       linfo->filter_size = linfo->shadow_render_data.cube_texel_size * ((filter_pixel_size > 1.0f) ? 1.5f : 0.0f);
+
+                       /* TODO: OPTI: Filter all faces in one/two draw call */
                        for (linfo->current_shadow_face = 0;
                             linfo->current_shadow_face < 6;
                             linfo->current_shadow_face++)
                        {
                                /* Copy using a small 3x3 box filter */
-                               linfo->filter_size = (la->soft > 0.00001f) ? 1.0f : 0.0f;
                                DRW_framebuffer_cubeface_attach(sldata->shadow_store_fb, sldata->shadow_cube_blur, 0, linfo->current_shadow_face, 0);
                                DRW_framebuffer_bind(sldata->shadow_store_fb);
                                DRW_draw_pass(psl->shadow_cube_copy_pass);
@@ -937,7 +942,26 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                        }
 
                        /* Push it to shadowmap array */
-                       linfo->filter_size = la->soft * 0.0005f;
+
+                       /* Adjust constants if concentric samples change. */
+                       const float max_filter_size = 7.5f;
+                       const float previous_box_filter_size = 9.0f; /* Dunno why but that works. */
+                       const int max_sample = 256;
+
+                       if (filter_pixel_size > 2.0f) {
+                               linfo->filter_size = linfo->shadow_render_data.cube_texel_size * max_filter_size * previous_box_filter_size;
+                               filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
+                               /* Compute number of concentric samples. Depends directly on filter size. */
+                               float pix_size_sqr = filter_pixel_size * filter_pixel_size;
+                               srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
+                       }
+                       else {
+                               linfo->filter_size = 0.0f;
+                               srd->shadow_samples_ct = 4;
+                       }
+                       srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
+                       DRW_uniformbuffer_update(sldata->shadow_render_ubo, srd);
+
                        DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, i, 0);
                        DRW_framebuffer_bind(sldata->shadow_store_fb);
                        DRW_draw_pass(psl->shadow_cube_store_pass);
@@ -958,8 +982,6 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                EEVEE_ShadowCascadeData *evscd = (EEVEE_ShadowCascadeData *)led->storage;
                EEVEE_ShadowRender *srd = &linfo->shadow_render_data;
 
-               srd->shadow_samples_ct = 32.0f;
-               srd->shadow_inv_samples_ct = 1.0f / srd->shadow_samples_ct;
                srd->clip_near = la->clipsta;
                srd->clip_far = la->clipend;
                for (int j = 0; j < la->cascade_count; ++j) {
@@ -973,19 +995,43 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
                /* Render shadow cascades */
                DRW_draw_pass(psl->shadow_cascade_pass);
 
+               /* TODO: OPTI: Filter all cascade in one/two draw call */
                for (linfo->current_shadow_cascade = 0;
                     linfo->current_shadow_cascade < la->cascade_count;
                     ++linfo->current_shadow_cascade)
                {
+                       /* 0.01f factor to convert to percentage */
+                       float filter_texture_size = la->soft * 0.01f / evscd->radius[linfo->current_shadow_cascade];
+                       float filter_pixel_size = ceil(linfo->shadow_size * filter_texture_size);
+
                        /* Copy using a small 3x3 box filter */
-                       linfo->filter_size = (la->soft > 0.00001f) ? 1.0f : 0.0f;
+                       linfo->filter_size = linfo->shadow_render_data.stored_texel_size * ((filter_pixel_size > 1.0f) ? 1.0f : 0.0f);
                        DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_cascade_blur, 0, linfo->current_shadow_cascade, 0);
                        DRW_framebuffer_bind(sldata->shadow_store_fb);
                        DRW_draw_pass(psl->shadow_cascade_copy_pass);
                        DRW_framebuffer_texture_detach(sldata->shadow_cascade_blur);
 
                        /* Push it to shadowmap array and blur more */
-                       linfo->filter_size = la->soft * 0.0005f / (evscd->radius[linfo->current_shadow_cascade] * 0.05f);
+
+                       /* Adjust constants if concentric samples change. */
+                       const float max_filter_size = 7.5f;
+                       const float previous_box_filter_size = 3.2f; /* Arbitrary: less banding */
+                       const int max_sample = 256;
+
+                       if (filter_pixel_size > 2.0f) {
+                               linfo->filter_size = linfo->shadow_render_data.stored_texel_size * max_filter_size * previous_box_filter_size;
+                               filter_pixel_size = max_ff(0.0f, filter_pixel_size - 3.0f);
+                               /* Compute number of concentric samples. Depends directly on filter size. */
+                               float pix_size_sqr = filter_pixel_size * filter_pixel_size;
+                               srd->shadow_samples_ct = min_ii(max_sample, 4 + 8 * (int)filter_pixel_size + 4 * (int)(pix_size_sqr));
+                       }
+                       else {
+                               linfo->filter_size = 0.0f;
+                               srd->shadow_samples_ct = 4;
+                       }
+                       srd->shadow_inv_samples_ct = 1.0f / (float)srd->shadow_samples_ct;
+                       DRW_uniformbuffer_update(sldata->shadow_render_ubo, &linfo->shadow_render_data);
+
                        int layer = evscd->layer_id + linfo->current_shadow_cascade;
                        DRW_framebuffer_texture_layer_attach(sldata->shadow_store_fb, sldata->shadow_pool, 0, layer, 0);
                        DRW_framebuffer_bind(sldata->shadow_store_fb);
index 72bd04a59589b168d9ce2035ce4bb8b46b90be5f..e5994a0c14711b2d37a8db51c9bce2270a8004a1 100644 (file)
@@ -237,7 +237,7 @@ typedef struct EEVEE_ShadowRender {
        float stored_texel_size;
        float clip_near;
        float clip_far;
-       float shadow_samples_ct;
+       int shadow_samples_ct;
        float shadow_inv_samples_ct;
 } EEVEE_ShadowRender;
 
index 6ad5b46f6839327f2eb44256ffc589096aa4d776..92d4b5aaed036f7764a6df9a8438d3b3b1aafe88 100644 (file)
@@ -23,7 +23,7 @@ layout(std140) uniform shadow_render_block {
        float storedTexelSize;
        float nearClip;
        float farClip;
-       float shadowSampleCount;
+       int shadowSampleCount;
        float shadowInvSampleCount;
 };
 
index b70d6faa73dc4c901e0d89f2be16a6d39382b069..8742bd99defd93a2fdc15b4832b2ab7460e0c6bf 100644 (file)
@@ -9,7 +9,7 @@ layout(std140) uniform shadow_render_block {
        float storedTexelSize;
        float nearClip;
        float farClip;
-       float shadowSampleCount;
+       int shadowSampleCount;
        float shadowInvSampleCount;
 };
 
@@ -18,7 +18,7 @@ uniform sampler2DArray shadowTexture;
 uniform int cascadeId;
 #else
 uniform samplerCube shadowTexture;
-uniform vec3 cubeFaceVec[3];
+uniform int faceId;
 #endif
 uniform float shadowFilterSize;
 
@@ -57,7 +57,7 @@ vec4 get_world_distance(vec4 depths, vec3 cos[4])
 {
        vec4 is_background = step(vec4(1.0), depths);
        depths = linear_depth(depths);
-       depths += vec4(1e16) * is_background;
+       depths += vec4(1e1) * is_background;
        cos[0] = normalize(abs(cos[0]));
        cos[1] = normalize(abs(cos[1]));
        cos[2] = normalize(abs(cos[2]));
@@ -74,7 +74,7 @@ float get_world_distance(float depth, vec3 cos)
 {
        float is_background = step(1.0, depth);
        depth = linear_depth(depth);
-       depth += 1e16 * is_background;
+       depth += 1e1 * is_background;
        cos = normalize(abs(cos));
        float cos_vec = max(cos.x, max(cos.y, cos.z));
        return depth / cos_vec;
@@ -123,25 +123,25 @@ const vec3 minorAxisX[6] = vec3[6](
 const vec3 minorAxisY[6] = vec3[6](
        vec3(0.0f, -1.0f, 0.0f),
        vec3(0.0f, -1.0f, 0.0f),
-       vec3(0.0f, 0.0f, -1.0f),
        vec3(0.0f, 0.0f, 1.0f),
+       vec3(0.0f, 0.0f, -1.0f),
        vec3(0.0f, -1.0f, 0.0f),
        vec3(0.0f, -1.0f, 0.0f)
 );
 
 const vec3 majorAxis[6] = vec3[6](
-       vec3(-1.0f, 0.0f, 0.0f),
        vec3(1.0f, 0.0f, 0.0f),
+       vec3(-1.0f, 0.0f, 0.0f),
        vec3(0.0f, 1.0f, 0.0f),
        vec3(0.0f, -1.0f, 0.0f),
-       vec3(0.0f, 0.0f, -1.0f),
-       vec3(0.0f, 0.0f, 1.0f)
+       vec3(0.0f, 0.0f, 1.0f),
+       vec3(0.0f, 0.0f, -1.0f)
 );
 
 vec3 get_texco(vec2 uvs, vec2 ofs)
 {
        uvs += ofs;
-       return majorAxis[0] + uvs.x * minorAxisX[1] + uvs.y * minorAxisY[2];
+       return majorAxis[faceId] + uvs.x * minorAxisX[faceId] + uvs.y * minorAxisY[faceId];
 }
 #endif
 
@@ -152,13 +152,23 @@ void main() {
 #ifdef CSM
        vec2 uvs = gl_FragCoord.xy * storedTexelSize;
 #else /* CUBEMAP */
-       vec2 uvs = gl_FragCoord.xy * cubeTexelSize;
+       vec2 uvs = gl_FragCoord.xy * cubeTexelSize * 2.0 - 1.0;
 #endif
 
        /* Center texel */
        vec3 co = get_texco(uvs, vec2(0.0));
        float depth = texture(shadowTexture, co).r;
        depth = get_world_distance(depth, co);
+
+       if (shadowFilterSize == 0.0) {
+#ifdef ESM
+               FragColor = vec4(depth);
+#else /* VSM */
+               FragColor = vec2(depth, depth * depth).xyxy;
+#endif
+               return;
+       }
+
 #ifdef ESM
        float accum = ln_space_prefilter(0.0, 0.0, SAMPLE_WEIGHT, depth);
 #else /* VSM */
@@ -166,9 +176,9 @@ void main() {
 #endif
 
 #ifdef CSM
-       vec3 ofs = storedTexelSize * vec3(1.0, 0.0, -1.0) * shadowFilterSize;
+       vec3 ofs = vec3(1.0, 0.0, -1.0) * shadowFilterSize;
 #else /* CUBEMAP */
-       vec3 ofs = cubeTexelSize * vec3(1.0, 0.0, -1.0) * shadowFilterSize;
+       vec3 ofs = vec3(1.0, 0.0, -1.0) * shadowFilterSize;
 #endif
 
        vec3 cos[4];
index 508df7a6b3793f379729fa370b6886de4201f0f5..ea51fa73a65cd6501d45c45e04dcefbf3d87e9ce 100644 (file)
@@ -7,7 +7,7 @@ layout(std140) uniform shadow_render_block {
        float storedTexelSize;
        float nearClip;
        float farClip;
-       float shadowSampleCount;
+       int shadowSampleCount;
        float shadowInvSampleCount;
 };
 
index c14107198aa7fc4046689bcf86180b9a14faf4cd..ebb98024a51faba8f56d8b1cca324542261634b5 100644 (file)
@@ -7,7 +7,7 @@ layout(std140) uniform shadow_render_block {
        float storedTexelSize;
        float nearClip;
        float farClip;
-       float shadowSampleCount;
+       int shadowSampleCount;
        float shadowInvSampleCount;
 };
 
@@ -38,8 +38,8 @@ vec3 octahedral_to_cubemap_proj(vec2 co)
 void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
 {
        vec3 UpVector = (abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
-       vec3 nT = normalize(cross(UpVector, N));
-       vec3 nB = cross(N, nT);
+       T = normalize(cross(UpVector, N));
+       B = cross(N, T);
 }
 
 /* Marco Salvi's GDC 2008 presentation about shadow maps pre-filtering techniques slide 24 */
@@ -53,9 +53,6 @@ vec4 ln_space_prefilter(float w0, vec4 x, float w1, vec4 y)
     return x + log(w0 + w1 * exp(y - x));
 }
 
-/* globals */
-vec3 T, B;
-
 #ifdef CSM
 vec3 get_texco(vec3 cos, vec2 ofs)
 {
@@ -63,14 +60,13 @@ vec3 get_texco(vec3 cos, vec2 ofs)
        return cos;
 }
 #else /* CUBEMAP */
+vec3 T, B; /* global vars */
 vec3 get_texco(vec3 cos, vec2 ofs)
 {
        return cos + ofs.x * T + ofs.y * B;
 }
 #endif
 
-const float INV_SAMPLE_NUM = 1.0 / float(CONCENTRIC_SAMPLE_NUM);
-
 void main() {
        vec3 cos;
 
@@ -113,14 +109,14 @@ void main() {
        depths.y = texture(shadowTexture, get_texco(cos, concentric[1])).r;
        depths.z = texture(shadowTexture, get_texco(cos, concentric[2])).r;
        depths.w = texture(shadowTexture, get_texco(cos, concentric[3])).r;
-       accum = ln_space_prefilter(0.0, accum, INV_SAMPLE_NUM, depths);
+       accum = ln_space_prefilter(0.0, accum, shadowInvSampleCount, depths);
 
-       for (int i = 4; i < CONCENTRIC_SAMPLE_NUM; i += 4) {
+       for (int i = 4; i < shadowSampleCount && i < CONCENTRIC_SAMPLE_NUM; i += 4) {
                depths.x = texture(shadowTexture, get_texco(cos, concentric[i+0])).r;
                depths.y = texture(shadowTexture, get_texco(cos, concentric[i+1])).r;
                depths.z = texture(shadowTexture, get_texco(cos, concentric[i+2])).r;
                depths.w = texture(shadowTexture, get_texco(cos, concentric[i+3])).r;
-               accum = ln_space_prefilter(1.0, accum, INV_SAMPLE_NUM, depths);
+               accum = ln_space_prefilter(1.0, accum, shadowInvSampleCount, depths);
        }
 
        accum.x = ln_space_prefilter(1.0, accum.x, 1.0, accum.y);
@@ -133,7 +129,7 @@ void main() {
 
        /* disc blur. */
        vec4 depths1, depths2;
-       for (int i = 0; i < CONCENTRIC_SAMPLE_NUM; i += 4) {
+       for (int i = 0; i < shadowSampleCount && i < CONCENTRIC_SAMPLE_NUM; i += 4) {
                depths1.xy = texture(shadowTexture, get_texco(cos, concentric[i+0])).rg;
                depths1.zw = texture(shadowTexture, get_texco(cos, concentric[i+1])).rg;
                depths2.xy = texture(shadowTexture, get_texco(cos, concentric[i+2])).rg;
@@ -141,6 +137,6 @@ void main() {
                accum += depths1.xy + depths1.zw + depths2.xy + depths2.zw;
        }
 
-       FragColor = accum.xyxy * INV_SAMPLE_NUM;
+       FragColor = accum.xyxy * shadowInvSampleCount;
 #endif
 }
\ No newline at end of file