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 | |
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.
11 files changed, 263 insertions, 25 deletions
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 2991dc110ee..f008254d973 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -1155,6 +1155,26 @@ class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel): layout.prop(raym, "gloss_factor", text="Roughness") +class EEVEE_MATERIAL_PT_options(MaterialButtonsPanel, Panel): + bl_label = "Options" + bl_context = "material" + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return context.material and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = context.material + + layout.prop(mat, "blend_method") + + if mat.blend_method == "CLIP": + layout.prop(mat, "alpha_threshold") + classes = ( MATERIAL_MT_sss_presets, MATERIAL_MT_specials, @@ -1185,6 +1205,7 @@ classes = ( MATERIAL_PT_custom_props, EEVEE_MATERIAL_PT_context_material, EEVEE_MATERIAL_PT_surface, + EEVEE_MATERIAL_PT_options, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 824151f9c98..e7232322a36 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -207,6 +207,8 @@ void BKE_material_init(Material *ma) ma->mode2 = MA_CASTSHADOW; ma->shade_flag = MA_APPROX_OCCLUSION; ma->preview = NULL; + + ma->alpha_threshold = 0.5f; } Material *BKE_material_add(Main *bmain, const char *name) 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 diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index e9f7fb338c9..3235dad8d04 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -14,7 +14,7 @@ uniform vec4 CameraTexCoFactors; /* Old glsl mode compat. */ -#ifndef NODETREE_EXEC +#ifndef CLOSURE_DEFAULT struct Closure { vec3 radiance; @@ -41,7 +41,7 @@ Closure closure_add(Closure cl1, Closure cl2) Closure nodetree_exec(void); /* Prototype */ -#endif /* NODETREE_EXEC */ +#endif /* CLOSURE_DEFAULT */ /* Converters */ @@ -2890,7 +2890,7 @@ void node_bsdf_transparent(vec4 color, out Closure result) { /* this isn't right */ result.radiance = color.rgb; - result.opacity = 0.0; + result.opacity = color.a; } void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result) @@ -4031,7 +4031,11 @@ void node_eevee_specular( void node_output_eevee_material(Closure surface, out Closure result) { +#if defined(USE_ALPHA_HASH) || defined(USE_ALPHA_CLIP) + result = surface; +#else result = Closure(surface.radiance, length(viewPosition)); +#endif } #endif /* EEVEE_ENGINE */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 09a4e42cb8f..fc0f4df4170 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -211,6 +211,10 @@ typedef struct Material { char nmap_tangent_names[9][64]; /* [MAX_MTFACE+1][MAX_NAME]; +1 for empty name */ int nmap_tangent_names_count, pad5; + /* Transparency */ + float alpha_threshold; + char blend_method, pad6[3]; + /* image to use for image/uv space, also bake target * (not to be used shading/rendering pipeline, this is editor featyure only!). */ struct Image *edit_image; @@ -491,5 +495,14 @@ typedef struct Material { #define MA_VOL_SHADE_MULTIPLE 3 #define MA_VOL_SHADE_SHADEDPLUSMULTIPLE 4 +/* blend_method */ +enum { + MA_BM_SOLID, + MA_BM_ADD, + MA_BM_MULTIPLY, + MA_BM_CLIP, + MA_BM_HASHED, +}; + #endif diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index e60832d86e6..a3c1ae7378e 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1805,6 +1805,15 @@ void RNA_def_material(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem prop_eevee_blend_items[] = { + {MA_BM_SOLID, "OPAQUE", 0, "Opaque", "Render surface without transparency"}, + // {MA_BM_ADD, "ADD", 0, "Additive", "Render surface and blend the result with additive blending"}, + // {MA_BM_MULTIPLY, "MULTIPLY", 0, "Multiply", "Render surface and blend the result with multiplicative blending"}, + {MA_BM_CLIP, "CLIP", 0, "Alpha Clip", "Use the alpha threshold to clip the visibility (binary visibility)"}, + {MA_BM_HASHED, "HASHED", 0, "Alpha Hashed", "Use noise to dither the binary visibility (works well with multi-samples)"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "Material", "ID"); RNA_def_struct_ui_text(srna, "Material", "Material data-block to define the appearance of geometric objects for rendering"); @@ -1827,7 +1836,18 @@ void RNA_def_material(BlenderRNA *brna) RNA_def_property_enum_items(prop, transparency_items); RNA_def_property_ui_text(prop, "Transparency Method", "Method to use for rendering transparency"); RNA_def_property_update(prop, 0, "rna_Material_update"); - + + /* Blending (only Eevee for now) */ + prop = RNA_def_property(srna, "blend_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_eevee_blend_items); + RNA_def_property_ui_text(prop, "Blend Mode", "Blend Mode for Transparent Faces"); + RNA_def_property_update(prop, 0, "rna_Material_draw_update"); + + prop = RNA_def_property(srna, "alpha_threshold", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0, 1); + RNA_def_property_ui_text(prop, "Clip Threshold", "A pixel is rendered only if its alpha value is above this threshold"); + RNA_def_property_update(prop, 0, "rna_Material_update"); + /* For Preview Render */ prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "pr_type"); |