Eevee: Shadows: Add cubemap filtering and adaptive sample count.

Filter size is constant in world space and not dependant of shadow resolution.
The filter size is limited to the number of precomputed samples.
This commit is contained in:
Clément Foucault 2017-09-09 21:11:22 +02:00
parent 314739bced
commit 90efcd6df7
6 changed files with 89 additions and 37 deletions

View 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);

View 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;

View File

@ -23,7 +23,7 @@ layout(std140) uniform shadow_render_block {
float storedTexelSize;
float nearClip;
float farClip;
float shadowSampleCount;
int shadowSampleCount;
float shadowInvSampleCount;
};

View 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];

View File

@ -7,7 +7,7 @@ layout(std140) uniform shadow_render_block {
float storedTexelSize;
float nearClip;
float farClip;
float shadowSampleCount;
int shadowSampleCount;
float shadowInvSampleCount;
};

View 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
}