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:
parent
314739bced
commit
90efcd6df7
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ layout(std140) uniform shadow_render_block {
|
|||
float storedTexelSize;
|
||||
float nearClip;
|
||||
float farClip;
|
||||
float shadowSampleCount;
|
||||
int shadowSampleCount;
|
||||
float shadowInvSampleCount;
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -7,7 +7,7 @@ layout(std140) uniform shadow_render_block {
|
|||
float storedTexelSize;
|
||||
float nearClip;
|
||||
float farClip;
|
||||
float shadowSampleCount;
|
||||
int shadowSampleCount;
|
||||
float shadowInvSampleCount;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue