Eevee: Add Variance Shadow Mapping

This is an alternative to ESM. It does not suffer the same bleeding artifacts.
This commit is contained in:
Clément Foucault 2017-09-01 18:39:39 +02:00
parent e2603a6e82
commit 32e96448b9
10 changed files with 122 additions and 40 deletions

View File

@ -384,6 +384,7 @@ class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
col = split.column(align=True)
col.prop(lamp, "shadow_buffer_bias", text="Bias")
col.prop(lamp, "shadow_buffer_exp", text="Exponent")
col.prop(lamp, "shadow_buffer_bleed_bias", text="Bleed Bias")
class DATA_PT_area(DataButtonsPanel, Panel):

View File

@ -321,6 +321,7 @@ classes = (
RENDERLAYER_PT_eevee_postprocess_settings,
RENDERLAYER_PT_eevee_screen_space_reflections,
RENDERLAYER_PT_eevee_volumetric,
RENDERLAYER_PT_eevee_shadows,
)
if __name__ == "__main__": # only for live edit.

View File

@ -804,7 +804,7 @@ void EEVEE_effects_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata)
struct GPUMaterial *mat = EEVEE_material_world_volume_get(
scene, wo, volumetrics->use_lights, volumetrics->use_volume_shadows,
false, volumetrics->use_colored_transmit);
false, volumetrics->use_colored_transmit, sldata->lamps->shadow_method);
psl->volumetric_integrate_ps = DRW_pass_create("Volumetric Integration", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_material_create(mat, psl->volumetric_integrate_ps);

View File

@ -55,8 +55,8 @@ typedef struct ShadowCaster {
static struct {
struct GPUShader *shadow_sh;
struct GPUShader *shadow_store_cube_sh;
struct GPUShader *shadow_store_cascade_sh;
struct GPUShader *shadow_store_cube_sh[SHADOW_METHOD_MAX];
struct GPUShader *shadow_store_cascade_sh[SHADOW_METHOD_MAX];
} e_data = {NULL}; /* Engine data */
extern char datatoc_shadow_vert_glsl[];
@ -79,8 +79,13 @@ void EEVEE_lights_init(EEVEE_SceneLayerData *sldata)
e_data.shadow_sh = DRW_shader_create(
datatoc_shadow_vert_glsl, datatoc_shadow_geom_glsl, datatoc_shadow_frag_glsl, NULL);
e_data.shadow_store_cube_sh = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, NULL);
e_data.shadow_store_cascade_sh = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define CSM");
e_data.shadow_store_cube_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define ESM\n");
e_data.shadow_store_cascade_sh[SHADOW_ESM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define ESM\n"
"#define CSM\n");
e_data.shadow_store_cube_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define VSM\n");
e_data.shadow_store_cascade_sh[SHADOW_VSM] = DRW_shader_create_fullscreen(datatoc_shadow_store_frag_glsl, "#define VSM\n"
"#define CSM\n");
}
if (!sldata->lamps) {
@ -92,14 +97,14 @@ void EEVEE_lights_init(EEVEE_SceneLayerData *sldata)
int sh_method = BKE_collection_engine_property_value_get_int(props, "shadow_method");
int sh_size = BKE_collection_engine_property_value_get_int(props, "shadow_size");
UNUSED_VARS(sh_method);
EEVEE_LampsInfo *linfo = sldata->lamps;
if (linfo->shadow_size != sh_size) {
if ((linfo->shadow_size != sh_size) || (linfo->shadow_method != sh_method)) {
BLI_assert((sh_size > 0) && (sh_size <= 8192));
DRW_TEXTURE_FREE_SAFE(sldata->shadow_pool);
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cascade_target);
linfo->shadow_method = sh_method;
linfo->shadow_size = sh_size;
linfo->shadow_render_data.stored_texel_size = 1.0 / (float)linfo->shadow_size;
@ -130,7 +135,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, psl->shadow_cube_store_pass);
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_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@ -139,7 +144,7 @@ void EEVEE_lights_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
{
psl->shadow_cascade_store_pass = DRW_pass_create("Shadow Cascade Storage Pass", DRW_STATE_WRITE_COLOR);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_store_cascade_sh, psl->shadow_cascade_store_pass);
DRWShadingGroup *grp = DRW_shgroup_create(e_data.shadow_store_cascade_sh[linfo->shadow_method], psl->shadow_cascade_store_pass);
DRW_shgroup_uniform_buffer(grp, "shadowTexture", &sldata->shadow_cascade_target);
DRW_shgroup_uniform_block(grp, "shadow_render_block", sldata->shadow_render_ubo);
DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL);
@ -273,8 +278,13 @@ void EEVEE_lights_cache_finish(EEVEE_SceneLayerData *sldata)
linfo->update_flag |= LIGHT_UPDATE_SHADOW_CUBE;
}
/* TODO Variance Shadow Map */
shadow_pool_format = DRW_TEX_R_32;
switch (linfo->shadow_method) {
case SHADOW_ESM: shadow_pool_format = DRW_TEX_R_32; break;
case SHADOW_VSM: shadow_pool_format = DRW_TEX_RG_32; break;
default:
BLI_assert(!"Incorrect Shadow Method");
break;
}
if (!sldata->shadow_cube_target) {
/* TODO render everything on the same 2d render target using clip planes and no Geom Shader. */
@ -412,7 +422,7 @@ static void eevee_shadow_cube_setup(Object *ob, EEVEE_LampsInfo *linfo, EEVEE_La
evsh->bias = 0.05f * la->bias;
evsh->near = la->clipsta;
evsh->far = la->clipend;
evsh->exp = la->bleedexp;
evsh->exp = (linfo->shadow_method == SHADOW_VSM) ? la->bleedbias : la->bleedexp;
evli->shadowid = (float)(evsmp->shadow_id);
}
@ -797,6 +807,8 @@ void EEVEE_draw_shadows(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl)
void EEVEE_lights_free(void)
{
DRW_SHADER_FREE_SAFE(e_data.shadow_sh);
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_sh);
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_sh);
for (int i = 0; i < SHADOW_METHOD_MAX; ++i) {
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cube_sh[i]);
DRW_SHADER_FREE_SAFE(e_data.shadow_store_cascade_sh[i]);
}
}

View File

@ -268,6 +268,19 @@ struct GPUTexture *EEVEE_materials_get_util_tex(void)
return e_data.util_tex;
}
static int eevee_material_shadow_option(int shadow_method)
{
switch (shadow_method) {
case SHADOW_ESM: return VAR_MAT_ESM;
case SHADOW_VSM: return VAR_MAT_VSM;
default:
BLI_assert(!"Incorrect Shadow Method");
break;
}
return 0;
}
static char *eevee_get_defines(int options)
{
char *str = NULL;
@ -305,6 +318,12 @@ static char *eevee_get_defines(int options)
if ((options & VAR_MAT_REFRACT) != 0) {
BLI_dynstr_appendf(ds, "#define USE_REFRACTION\n");
}
if ((options & VAR_MAT_VSM) != 0) {
BLI_dynstr_appendf(ds, "#define SHADOW_VSM\n");
}
if ((options & VAR_MAT_ESM) != 0) {
BLI_dynstr_appendf(ds, "#define SHADOW_ESM\n");
}
str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@ -601,7 +620,7 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, Wor
struct GPUMaterial *EEVEE_material_world_volume_get(
struct Scene *scene, World *wo,
bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit)
bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit, int shadow_method)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_WORLD_VOLUME;
@ -611,6 +630,8 @@ struct GPUMaterial *EEVEE_material_world_volume_get(
if (use_volume_shadows) options |= VAR_VOLUME_SHADOW;
if (use_color_transmit) options |= VAR_VOLUME_COLOR;
options |= eevee_material_shadow_option(shadow_method);
GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine, options);
if (mat != NULL) {
return mat;
@ -630,7 +651,7 @@ struct GPUMaterial *EEVEE_material_world_volume_get(
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma,
bool use_blend, bool use_multiply, bool use_refract)
bool use_blend, bool use_multiply, bool use_refract, int shadow_method)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_MESH;
@ -639,6 +660,8 @@ struct GPUMaterial *EEVEE_material_mesh_get(
if (use_multiply) options |= VAR_MAT_MULT;
if (use_refract) options |= VAR_MAT_REFRACT;
options |= eevee_material_shadow_option(shadow_method);
GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options);
if (mat) {
return mat;
@ -700,11 +723,13 @@ struct GPUMaterial *EEVEE_material_mesh_depth_get(
}
struct GPUMaterial *EEVEE_material_hair_get(
struct Scene *scene, Material *ma)
struct Scene *scene, Material *ma, int shadow_method)
{
const void *engine = &DRW_engine_viewport_eevee_type;
int options = VAR_MAT_MESH | VAR_MAT_HAIR;
options |= eevee_material_shadow_option(shadow_method);
GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options);
if (mat) {
return mat;
@ -727,7 +752,7 @@ struct GPUMaterial *EEVEE_material_hair_get(
**/
static struct DRWShadingGroup *EEVEE_default_shading_group_create(
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata, DRWPass *pass,
bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr)
bool is_hair, bool is_flat_normal, bool use_blend, bool use_ssr, int shadow_method)
{
static int ssr_id;
ssr_id = (use_ssr) ? 0 : -1;
@ -737,6 +762,8 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
if (is_flat_normal) options |= VAR_MAT_FLAT;
if (use_blend) options |= VAR_MAT_BLEND;
options |= eevee_material_shadow_option(shadow_method);
if (e_data.default_lit[options] == NULL) {
create_default_shader(options);
}
@ -752,7 +779,7 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_create(
**/
static struct DRWShadingGroup *EEVEE_default_shading_group_get(
EEVEE_SceneLayerData *sldata, EEVEE_Data *vedata,
bool is_hair, bool is_flat_normal, bool use_ssr)
bool is_hair, bool is_flat_normal, bool use_ssr, int shadow_method)
{
static int ssr_id;
ssr_id = (use_ssr) ? 0 : -1;
@ -761,6 +788,8 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(
if (is_hair) options |= VAR_MAT_HAIR;
if (is_flat_normal) options |= VAR_MAT_FLAT;
options |= eevee_material_shadow_option(shadow_method);
if (e_data.default_lit[options] == NULL) {
create_default_shader(options);
}
@ -920,6 +949,7 @@ static void material_opaque(
Scene *scene = draw_ctx->scene;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_LampsInfo *linfo = sldata->lamps;
float *color_p = &ma->r;
float *metal_p = &ma->ray_mirror;
@ -938,7 +968,7 @@ static void material_opaque(
/* This will have been created already, just perform a lookup. */
*gpumat = (use_gpumat) ? EEVEE_material_mesh_get(
scene, ma, false, false, use_refract) : NULL;
scene, ma, false, false, use_refract, linfo->shadow_method) : NULL;
*gpumat_depth = (use_gpumat) ? EEVEE_material_mesh_depth_get(
scene, ma, (ma->blend_method == MA_BM_HASHED), false) : NULL;
return;
@ -946,7 +976,7 @@ static void material_opaque(
if (use_gpumat) {
/* Shading */
*gpumat = EEVEE_material_mesh_get(scene, ma, false, false, use_refract);
*gpumat = EEVEE_material_mesh_get(scene, ma, false, false, use_refract, linfo->shadow_method);
*shgrp = DRW_shgroup_material_create(*gpumat, use_refract ? psl->refract_pass : psl->material_pass);
if (*shgrp) {
@ -990,7 +1020,7 @@ static void material_opaque(
/* Fallback to default shader */
if (*shgrp == NULL) {
*shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor, stl->effects->use_ssr);
*shgrp = EEVEE_default_shading_group_get(sldata, vedata, false, use_flat_nor, stl->effects->use_ssr, linfo->shadow_method);
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
@ -1024,6 +1054,7 @@ static void material_transparent(
Scene *scene = draw_ctx->scene;
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl;
EEVEE_LampsInfo *linfo = sldata->lamps;
const bool use_refract = ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((stl->effects->enabled_effects & EFFECT_REFRACT) != 0);
@ -1034,7 +1065,7 @@ static void material_transparent(
if (ma->use_nodes && ma->nodetree) {
/* Shading */
*gpumat = EEVEE_material_mesh_get(scene, ma, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract);
*gpumat = EEVEE_material_mesh_get(scene, ma, true, (ma->blend_method == MA_BM_MULTIPLY), use_refract, linfo->shadow_method);
*shgrp = DRW_shgroup_material_create(*gpumat, psl->transparent_pass);
if (*shgrp) {
@ -1055,7 +1086,7 @@ static void material_transparent(
if (*shgrp == NULL) {
*shgrp = EEVEE_default_shading_group_create(
sldata, vedata, psl->transparent_pass,
false, use_flat_nor, true, false);
false, use_flat_nor, true, false, linfo->shadow_method);
DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1);
@ -1268,7 +1299,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
}
else {
if (ma->use_nodes && ma->nodetree) {
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma);
struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma, sldata->lamps->shadow_method);
shgrp = DRW_shgroup_material_create(gpumat, psl->material_pass);
if (shgrp) {
@ -1290,7 +1321,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl
/* Fallback to default shader */
if (shgrp == NULL) {
shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, stl->effects->use_ssr);
shgrp = EEVEE_default_shading_group_get(sldata, vedata, true, false, stl->effects->use_ssr, sldata->lamps->shadow_method);
DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1);
DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1);
DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1);

View File

@ -64,10 +64,12 @@ enum {
VAR_MAT_HAIR = (1 << 2),
VAR_MAT_FLAT = (1 << 3),
VAR_MAT_BLEND = (1 << 4),
VAR_MAT_VSM = (1 << 5),
VAR_MAT_ESM = (1 << 6),
/* Max number of variation */
/* IMPORTANT : Leave it last and set
* it's value accordingly. */
VAR_MAT_MAX = (1 << 5),
VAR_MAT_MAX = (1 << 7),
/* These are options that are not counted in VAR_MAT_MAX
* because they are not cumulative with the others above. */
VAR_MAT_CLIP = (1 << 8),
@ -81,6 +83,7 @@ enum {
enum {
SHADOW_ESM = 1,
SHADOW_VSM = 2,
SHADOW_METHOD_MAX = 3,
};
typedef struct EEVEE_PassList {
@ -516,11 +519,12 @@ void EEVEE_materials_cache_finish(EEVEE_Data *vedata);
struct GPUMaterial *EEVEE_material_world_lightprobe_get(struct Scene *scene, struct World *wo);
struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, struct World *wo);
struct GPUMaterial *EEVEE_material_world_volume_get(
struct Scene *scene, struct World *wo, bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit);
struct Scene *scene, struct World *wo, bool use_lights, bool use_volume_shadows, bool is_homogeneous, bool use_color_transmit,
int shadow_method);
struct GPUMaterial *EEVEE_material_mesh_get(
struct Scene *scene, Material *ma, bool use_blend, bool use_multiply, bool use_refract);
struct Scene *scene, Material *ma, bool use_blend, bool use_multiply, bool use_refract, int shadow_method);
struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha, bool is_shadow);
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma);
struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, int shadow_method);
void EEVEE_materials_free(void);
void EEVEE_draw_default_passes(EEVEE_PassList *psl);
void EEVEE_update_util_texture(float offset);

View File

@ -92,6 +92,7 @@ struct ShadowCascadeData {
#define sh_cube_far near_far_bias_exp.y
#define sh_cube_bias near_far_bias_exp.z
#define sh_cube_exp near_far_bias_exp.w
#define sh_cube_bleed near_far_bias_exp.w
/* ------- Convenience functions --------- */

View File

@ -22,14 +22,37 @@ float shadow_cubemap(float shid, vec4 l_vector)
ShadowCubeData scd = shadows_cube_data[int(shid)];
vec3 cubevec = -l_vector.xyz / l_vector.w;
float dist = l_vector.w - scd.sh_cube_bias;
float dist = l_vector.w;
#if defined(SHADOW_VSM)
vec2 moments = texture_octahedron(shadowTexture, vec4(cubevec, shid)).rg;
float p = 0.0;
if (dist <= moments.x)
p = 1.0;
float variance = moments.y - (moments.x * moments.x);
variance = max(variance, scd.sh_cube_bias / 10.0);
float d = moments.x - dist;
float p_max = variance / (variance + d * d);
// Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]
p_max = clamp((p_max - scd.sh_cube_bleed) / (1.0 - scd.sh_cube_bleed), 0.0, 1.0);
float vsm_test = max(p, p_max);
return vsm_test;
#elif defined(SHADOW_ESM)
float z = texture_octahedron(shadowTexture, vec4(cubevec, shid)).r;
float esm_test = saturate(exp(scd.sh_cube_exp * (z - dist)));
float sh_test = step(0, z - dist);
float esm_test = saturate(exp(scd.sh_cube_exp * (z - (dist - scd.sh_cube_bias))));
return esm_test;
#else
float z = texture_octahedron(shadowTexture, vec4(cubevec, shid)).r;
float sh_test = step(0, z - (dist - scd.sh_cube_bias));
return sh_test;
#endif
}
float shadow_cascade(float shid, vec3 W)

View File

@ -117,8 +117,6 @@ float wang_hash_noise(uint s)
return fract(value);
}
#define ESM
void main() {
vec2 uvs = gl_FragCoord.xy * storedTexelSize;
@ -144,13 +142,14 @@ void main() {
/* TODO Can be optimized by groupping fetches
* and by converting to radial distance beforehand. */
#if defined(ESM)
#if defined(ESM) || defined(VSM)
vec3 T, B;
make_orthonormal_basis(cubevec, wang_hash_noise(0u), T, B);
T *= 0.01;
B *= 0.01;
#ifdef ESM
float accum = 0.0;
/* Poisson disc blur in log space. */
@ -164,7 +163,18 @@ void main() {
}
FragColor = vec4(accum);
#else /* VSM */
vec2 accum = vec2(0.0);
/* Poisson disc blur. */
for (int i = 0; i < SAMPLE_NUM; ++i) {
float dist = get_cube_radial_distance(cubevec + poisson[i].x * T + poisson[i].y * B);
float dist_sqr = dist * dist;
accum += vec2(dist, dist_sqr);
}
FragColor = accum.xyxy * shadowInvSampleCount;
#endif /* Prefilter */
#else /* PCF (no prefilter) */
FragColor = vec4(get_cube_radial_distance(cubevec));
#endif

View File

@ -6210,8 +6210,7 @@ static void rna_def_scene_layer_engine_settings_eevee(BlenderRNA *brna)
/* Keep in sync with eevee_private.h */
static EnumPropertyItem eevee_shadow_method_items[] = {
{1, "ESM", 0, "ESM", "Exponential Shadow Mapping"},
/* TODO */
// {2, "VSM", 0, "VSM", "Variance Shadow Mapping"},
{2, "VSM", 0, "VSM", "Variance Shadow Mapping"},
{0, NULL, 0, NULL, NULL}
};