diff options
author | Joe Eagar <joeedh@gmail.com> | 2020-03-25 04:34:13 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2020-03-25 17:45:24 +0300 |
commit | 035a3760afd23c3b28a845ba23f500931799e52b (patch) | |
tree | f7dd4df519133f0f0edc03c2e1400626efbe241a /source/blender/draw/engines/eevee | |
parent | b4c05a9c89358fb14715009923241efdd007d3dc (diff) |
Alpha hash support for hair in EEvee
This patch adds support for alpha hash for hair rendering in EEvee. Here's a comparison of with alpha hashing:
{F7588610}
And no alpha hashing:
{F7588615}
Note that this needs "soft shadows" enabled, otherwise shadows will be noisy; here's a render with soft shadows disabled:
{F7588621}
Reviewed By: fclem
Differential Revision: https://developer.blender.org/D5221
Diffstat (limited to 'source/blender/draw/engines/eevee')
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_materials.c | 150 | ||||
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/shadow_vert.glsl | 29 |
2 files changed, 169 insertions, 10 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 2062f4d83c9..79087ddfa9e 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -939,6 +939,45 @@ struct GPUMaterial *EEVEE_material_mesh_depth_get(struct Scene *scene, return mat; } +static struct GPUMaterial *EEVEE_material_hair_depth_get(struct Scene *scene, + Material *ma, + bool use_hashed_alpha, + bool is_shadow) +{ + const void *engine = &DRW_engine_viewport_eevee_type; + int options = VAR_MAT_MESH | VAR_MAT_HAIR; + + SET_FLAG_FROM_TEST(options, use_hashed_alpha, VAR_MAT_HASH); + SET_FLAG_FROM_TEST(options, !use_hashed_alpha, VAR_MAT_CLIP); + SET_FLAG_FROM_TEST(options, is_shadow, VAR_MAT_SHADOW); + + GPUMaterial *mat = DRW_shader_find_from_material(ma, engine, options, true); + if (mat) { + return mat; + } + + char *defines = eevee_get_defines(options); + + char *frag_str = BLI_string_joinN(e_data.frag_shader_lib, datatoc_prepass_frag_glsl); + + mat = DRW_shader_create_from_material(scene, + ma, + engine, + options, + false, + (is_shadow) ? e_data.vert_shadow_shader_str : + e_data.vert_shader_str, + NULL, + frag_str, + defines, + false); + + MEM_freeN(frag_str); + MEM_freeN(defines); + + return mat; +} + struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma) { const void *engine = &DRW_engine_viewport_eevee_type; @@ -1741,31 +1780,92 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, DRWShadingGroup *shgrp = NULL; Material *ma = eevee_object_material_get(ob, matnr - 1); + const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; + const bool use_gpumat = ma->use_nodes && ma->nodetree && !holdout; + const bool use_alpha_hash = (ma->blend_method == MA_BM_HASHED); + const bool use_alpha_clip = (ma->blend_method == MA_BM_CLIP); + const bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); + + GPUMaterial *gpumat = use_gpumat ? EEVEE_material_hair_get(scene, ma) : NULL; + eGPUMaterialStatus status_mat_surface = gpumat ? GPU_material_status(gpumat) : GPU_MAT_SUCCESS; float *color_p = &ma->r; float *metal_p = &ma->metallic; float *spec_p = &ma->spec; float *rough_p = &ma->roughness; - bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); - const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; + /* Depth prepass. */ + if (use_gpumat && (use_alpha_clip || use_alpha_hash)) { + GPUMaterial *gpumat_depth = EEVEE_material_hair_depth_get(scene, ma, use_alpha_hash, false); + + eGPUMaterialStatus status_mat_depth = GPU_material_status(gpumat_depth); - shgrp = DRW_shgroup_hair_create(ob, psys, md, psl->depth_pass, e_data.default_hair_prepass_sh); + if (status_mat_depth != GPU_MAT_SUCCESS) { + /* Mixing both flags. If depth shader fails, show it to the user by not using + * the surface shader. */ + status_mat_surface = status_mat_depth; + } + else { + const bool use_diffuse = GPU_material_flag_get(gpumat_depth, GPU_MATFLAG_DIFFUSE); + const bool use_glossy = GPU_material_flag_get(gpumat_depth, GPU_MATFLAG_GLOSSY); + const bool use_refract = GPU_material_flag_get(gpumat_depth, GPU_MATFLAG_REFRACT); - shgrp = DRW_shgroup_hair_create( - ob, psys, md, psl->depth_pass_clip, e_data.default_hair_prepass_clip_sh); + for (int i = 0; i < 2; i++) { + DRWPass *pass = (i == 0) ? psl->depth_pass : psl->depth_pass_clip; + + shgrp = DRW_shgroup_material_hair_create(ob, psys, md, pass, gpumat_depth); + + add_standard_uniforms(shgrp, + sldata, + vedata, + NULL, + NULL, + use_diffuse, + use_glossy, + use_refract, + false, + false, + DEFAULT_RENDER_PASS_FLAG); + + /* Unfortunately needed for correctness but not 99% of the time not needed. + * TODO detect when needed? */ + DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block( + shgrp, "renderpass_block", EEVEE_material_default_render_pass_ubo_get(sldata)); + DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); + + if (use_alpha_clip) { + DRW_shgroup_uniform_float(shgrp, "alphaThreshold", &ma->alpha_threshold, 1); + } + } + } + } + + /* Fallback to default shader */ + if (shgrp == NULL) { + for (int i = 0; i < 2; i++) { + DRWPass *depth_pass = (i == 0) ? psl->depth_pass : psl->depth_pass_clip; + struct GPUShader *depth_sh = (i == 0) ? e_data.default_hair_prepass_sh : + e_data.default_hair_prepass_clip_sh; + DRW_shgroup_hair_create(ob, psys, md, depth_pass, depth_sh); + } + } shgrp = NULL; - if (ma->use_nodes && ma->nodetree && !holdout) { + if (gpumat) { static int ssr_id; ssr_id = (use_ssr) ? 1 : -1; static float half = 0.5f; static float error_col[3] = {1.0f, 0.0f, 1.0f}; static float compile_col[3] = {0.5f, 0.5f, 0.5f}; - struct GPUMaterial *gpumat = EEVEE_material_hair_get(scene, ma); - switch (GPU_material_status(gpumat)) { + switch (status_mat_surface) { case GPU_MAT_SUCCESS: { bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE); bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY); @@ -1860,8 +1960,38 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, } /* Shadows */ - DRW_shgroup_hair_create(ob, psys, md, psl->shadow_pass, e_data.default_hair_prepass_sh); - *cast_shadow = true; + char blend_shadow = use_gpumat ? ma->blend_shadow : MA_BS_SOLID; + const bool shadow_alpha_hash = (blend_shadow == MA_BS_HASHED); + switch (blend_shadow) { + case MA_BS_SOLID: + DRW_shgroup_hair_create(ob, psys, md, psl->shadow_pass, e_data.default_hair_prepass_sh); + *cast_shadow = true; + break; + case MA_BS_CLIP: + case MA_BS_HASHED: + gpumat = EEVEE_material_hair_depth_get(scene, ma, shadow_alpha_hash, true); + shgrp = DRW_shgroup_material_hair_create(ob, psys, md, psl->shadow_pass, gpumat); + /* Unfortunately needed for correctness but not 99% of the time not needed. + * TODO detect when needed? */ + DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block( + shgrp, "renderpass_block", EEVEE_material_default_render_pass_ubo_get(sldata)); + DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); + + if (!shadow_alpha_hash) { + DRW_shgroup_uniform_float(shgrp, "alphaThreshold", &ma->alpha_threshold, 1); + } + *cast_shadow = true; + break; + case MA_BS_NONE: + default: + break; + } } void EEVEE_materials_cache_populate(EEVEE_Data *vedata, diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 6b06aab34d2..c42f905cf7e 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -9,6 +9,14 @@ out vec3 worldNormal; out vec3 viewNormal; #endif +#ifdef HAIR_SHADER +out vec3 hairTangent; +out float hairThickTime; +out float hairThickness; +out float hairTime; +flat out int hairStrandID; +#endif + void main() { #ifdef GPU_INTEL @@ -18,13 +26,34 @@ void main() gl_Position.x = float(gl_VertexID); #endif +#ifdef HAIR_SHADER + hairStrandID = hair_get_strand_id(); + vec3 world_pos, binor; + hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), + ModelMatrixInverse, + ViewMatrixInverse[3].xyz, + ViewMatrixInverse[2].xyz, + world_pos, + hairTangent, + binor, + hairTime, + hairThickness, + hairThickTime); + + worldNormal = cross(hairTangent, binor); +#else vec3 world_pos = point_object_to_world(pos); +#endif + gl_Position = point_world_to_ndc(world_pos); #ifdef MESH_SHADER worldPosition = world_pos; viewPosition = point_world_to_view(worldPosition); +# ifndef HAIR_SHADER worldNormal = normalize(normal_object_to_world(nor)); +# endif + /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); # ifdef USE_ATTR |