diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-07-09 13:01:29 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-07-11 13:39:35 +0300 |
commit | 05bef13b53e93978716aff8e2efba7ddf72264ed (patch) | |
tree | f1ff4fbf5a7d4c5e1a8f934b17fa47256d40177a /source/blender/draw/engines | |
parent | e2c0197a96dbac2aee519fbfb142441c6aed0963 (diff) |
Eevee: Add support for Alpha clip and Hashed Alpha transparency.
Hashed Alpha transparency offers a noisy output but has the benefit of being correctly ordered. Noise can be attenuated with Multisampling / AntiAliasing.
Diffstat (limited to 'source/blender/draw/engines')
6 files changed, 199 insertions, 21 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 9453cdb0b80..3475bbb7fad 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -92,14 +92,12 @@ static void EEVEE_cache_populate(void *vedata, Object *ob) } } - struct Gwn_Batch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - EEVEE_materials_cache_populate(vedata, sldata, ob, geom); + if (ELEM(ob->type, OB_MESH)) { + EEVEE_materials_cache_populate(vedata, sldata, ob); const bool cast_shadow = true; if (cast_shadow) { - EEVEE_lights_cache_shcaster_add(sldata, psl, geom, ob->obmat); BLI_addtail(&sldata->shadow_casters, BLI_genericNodeN(ob)); EEVEE_ObjectEngineData *oedata = EEVEE_object_data_get(ob); oedata->need_update = ((ob->deg_update_flag & DEG_RUNTIME_DATA_UPDATE) != 0); diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 4cc8b5ed93c..151bd05358d 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -179,8 +179,6 @@ static char *eevee_get_defines(int options) { char *str = NULL; - BLI_assert(options < VAR_MAT_MAX); - DynStr *ds = BLI_dynstr_new(); BLI_dynstr_appendf(ds, SHADER_DEFINES); @@ -202,6 +200,12 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_BENT) != 0) { BLI_dynstr_appendf(ds, "#define USE_BENT_NORMAL\n"); } + if ((options & VAR_MAT_CLIP) != 0) { + BLI_dynstr_appendf(ds, "#define USE_ALPHA_CLIP\n"); + } + if ((options & VAR_MAT_HASH) != 0) { + BLI_dynstr_appendf(ds, "#define USE_ALPHA_HASH\n"); + } str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -491,6 +495,42 @@ struct GPUMaterial *EEVEE_material_mesh_get( return mat; } +struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha) +{ + const void *engine = &DRW_engine_viewport_eevee_type; + int options = VAR_MAT_MESH; + + if (use_hashed_alpha) { + options |= VAR_MAT_HASH; + } + else { + options |= VAR_MAT_CLIP; + } + + GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine, options); + if (mat) { + return mat; + } + + char *defines = eevee_get_defines(options); + + DynStr *ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, e_data.frag_shader_lib); + BLI_dynstr_append(ds_frag, datatoc_prepass_frag_glsl); + char *frag_str = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + mat = GPU_material_from_nodetree( + scene, ma->nodetree, &ma->gpumaterial, engine, options, + datatoc_lit_surface_vert_glsl, NULL, frag_str, + defines); + + MEM_freeN(frag_str); + MEM_freeN(defines); + + return mat; +} + struct GPUMaterial *EEVEE_material_hair_get( struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals) @@ -637,7 +677,7 @@ void EEVEE_materials_cache_init(EEVEE_Data *vedata) } \ } while (0) -void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob, struct Gwn_Batch *geom) +void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; @@ -650,12 +690,6 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl const bool is_sculpt_mode = is_active && (ob->mode & OB_MODE_SCULPT) != 0; const bool is_default_mode_shader = is_sculpt_mode; - /* Depth Prepass */ - DRWShadingGroup *depth_shgrp = do_cull ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp; - DRWShadingGroup *depth_clip_shgrp = do_cull ? stl->g_data->depth_shgrp_clip_cull : stl->g_data->depth_shgrp_clip; - ADD_SHGROUP_CALL(depth_shgrp, ob, geom); - ADD_SHGROUP_CALL(depth_clip_shgrp, ob, geom); - /* First get materials for this mesh. */ if (ELEM(ob->type, OB_MESH)) { const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol)); @@ -740,6 +774,47 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sl if (mat_geom) { for (int i = 0; i < materials_len; ++i) { ADD_SHGROUP_CALL(shgrp_array[i], ob, mat_geom[i]); + + /* Depth Prepass */ + DRWShadingGroup *depth_shgrp = NULL; + DRWShadingGroup *depth_clip_shgrp; + + Material *ma = give_current_material(ob, i + 1); + + if (ma != NULL && (ma->use_nodes && ma->nodetree)) { + if (ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED)) { + Scene *scene = draw_ctx->scene; + DRWPass *depth_pass, *depth_clip_pass; + struct GPUMaterial *gpumat = EEVEE_material_mesh_depth_get(scene, ma, (ma->blend_method == MA_BM_HASHED)); + + depth_pass = do_cull ? psl->depth_pass_cull : psl->depth_pass; + depth_clip_pass = do_cull ? psl->depth_pass_clip_cull : psl->depth_pass_clip; + + /* Use same shader for both. */ + depth_shgrp = DRW_shgroup_material_create(gpumat, depth_pass); + depth_clip_shgrp = DRW_shgroup_material_create(gpumat, depth_clip_pass); + + if (ma->blend_method == MA_BM_CLIP) { + DRW_shgroup_uniform_float(depth_shgrp, "alphaThreshold", &ma->alpha_threshold, 1); + DRW_shgroup_uniform_float(depth_clip_shgrp, "alphaThreshold", &ma->alpha_threshold, 1); + } + } + + /* Shadow Pass */ + /* TODO clipped shadow map */ + EEVEE_lights_cache_shcaster_add(sldata, psl, mat_geom[i], ob->obmat); + } + + if (depth_shgrp == NULL) { + depth_shgrp = do_cull ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp; + depth_clip_shgrp = do_cull ? stl->g_data->depth_shgrp_clip_cull : stl->g_data->depth_shgrp_clip; + + /* Shadow Pass */ + EEVEE_lights_cache_shcaster_add(sldata, psl, mat_geom[i], ob->obmat); + } + + ADD_SHGROUP_CALL(depth_shgrp, ob, mat_geom[i]); + ADD_SHGROUP_CALL(depth_clip_shgrp, ob, mat_geom[i]); } } } diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 7f45e72c713..92a4df27718 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -69,7 +69,11 @@ enum { /* Max number of variation */ /* IMPORTANT : Leave it last and set * it's value accordingly. */ - VAR_MAT_MAX = (1 << 6) + VAR_MAT_MAX = (1 << 6), + /* These are options that are not counted in VAR_MAT_MAX + * because they are not cumulative with the others above. */ + VAR_MAT_CLIP = (1 << 7), + VAR_MAT_HASH = (1 << 8), }; typedef struct EEVEE_PassList { @@ -438,14 +442,14 @@ EEVEE_LampEngineData *EEVEE_lamp_data_get(Object *ob); struct GPUTexture *EEVEE_materials_get_util_tex(void); /* XXX */ void EEVEE_materials_init(EEVEE_StorageList *stl); void EEVEE_materials_cache_init(EEVEE_Data *vedata); -void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob, struct Gwn_Batch *geom); +void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_SceneLayerData *sldata, Object *ob); 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 GPUMaterial *EEVEE_material_mesh_lightprobe_get(struct Scene *scene, Material *ma); struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals); +struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, Material *ma, bool use_hashed_alpha); struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma, bool use_ao, bool use_bent_normals); void EEVEE_materials_free(void); void EEVEE_draw_default_passes(EEVEE_PassList *psl); diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index b15c3b6d452..edf13b911dc 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -19,8 +19,6 @@ uniform vec4 viewvecs[2]; /* ------- Structures -------- */ #ifdef VOLUMETRICS -#define NODETREE_EXEC - struct Closure { vec3 absorption; vec3 scatter; @@ -49,12 +47,35 @@ Closure closure_add(Closure cl1, Closure cl2) cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ return cl; } +#else -Closure nodetree_exec(void); /* Prototype */ +struct Closure { + vec3 radiance; + float opacity; +}; + +#define CLOSURE_DEFAULT Closure(vec3(0.0), 0.0) + +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.radiance = mix(cl1.radiance, cl2.radiance, fac); + cl.opacity = mix(cl1.opacity, cl2.opacity, fac); + return cl; +} +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.radiance = cl1.radiance + cl2.radiance; + cl.opacity = cl1.opacity + cl2.opacity; + return cl; +} #endif /* VOLUMETRICS */ +Closure nodetree_exec(void); /* Prototype */ + struct LightData { vec4 position_influence; /* w : InfluenceRadius */ diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index 6317dcea0e9..f921d56e3bc 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -1,7 +1,85 @@ +#ifdef USE_ALPHA_HASH + +/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ +float hash(vec2 a) { + return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x)))); +} + +float hash3d(vec3 a) { + return hash(vec2(hash(a.xy), a.z)); +} + +//uniform float hashScale; +float hashScale = 0.001; + +float hashed_alpha_threshold(vec3 co) +{ + /* Find the discretized derivatives of our coordinates. */ + float max_deriv = max(length(dFdx(co)), length(dFdy(co))); + float pix_scale = 1.0 / (hashScale * max_deriv); + + /* Find two nearest log-discretized noise scales. */ + float pix_scale_log = log2(pix_scale); + vec2 pix_scales; + pix_scales.x = exp2(floor(pix_scale_log)); + pix_scales.y = exp2(ceil(pix_scale_log)); + + /* Compute alpha thresholds at our two noise scales. */ + vec2 alpha; + alpha.x = hash3d(floor(pix_scales.x * co)); + alpha.y = hash3d(floor(pix_scales.y * co)); + + /* Factor to interpolate lerp with. */ + float fac = fract(log2(pix_scale)); + + /* Interpolate alpha threshold from noise at two scales. */ + float x = mix(alpha.x, alpha.y, fac); + + /* Pass into CDF to compute uniformly distrib threshold. */ + float a = min(fac, 1.0 - fac); + float one_a = 1.0 - a; + float denom = 1.0 / (2 * a * one_a); + float one_x = (1 - x); + vec3 cases = vec3( + (x * x) * denom, + (x - 0.5 * a) / one_a, + 1.0 - (one_x * one_x * denom) + ); + + /* Find our final, uniformly distributed alpha threshold. */ + float threshold = (x < one_a) ? ((x < a) ? cases.x : cases.y) : cases.z; + + /* Avoids threshold == 0. */ + threshold = clamp(threshold, 1.0e-6, 1.0); + + return threshold; +} + +#endif + +#ifdef USE_ALPHA_CLIP +uniform float alphaThreshold; +#endif + void main() { /* For now do nothing. - * In the future, output object motion blur. - * This pass could also be controlled but nodetree (pixel depth offset, stochastic transparency). */ + * In the future, output object motion blur. */ + +#if defined(USE_ALPHA_HASH) || defined(USE_ALPHA_CLIP) +#define NODETREE_EXEC + + Closure cl = nodetree_exec(); + +#if defined(USE_ALPHA_HASH) + /* Hashed Alpha Testing */ + if (cl.opacity < hashed_alpha_threshold(worldPosition)) + discard; +#elif defined(USE_ALPHA_CLIP) + /* Alpha clip */ + if (cl.opacity <= alphaThreshold) + discard; +#endif +#endif } diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index a99769c7fec..7f44dd53163 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,6 +1,8 @@ #ifdef VOLUMETRICS +#define NODETREE_EXEC + #define VOLUMETRIC_INTEGRATION_MAX_STEP 256 #define VOLUMETRIC_SHADOW_MAX_STEP 128 |