From ea989ebf943906e5ba7c640f011f8504023659cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cle=CC=81ment=20Foucault?= Date: Tue, 9 Jan 2024 16:27:30 +1300 Subject: [PATCH] EEVEE/EEVEE-Next: Split Diffuse and Subsurface closure Even if related, they don't have the same performance impact. To avoid any performance hit, we replace the Diffuse by a Subsurface Closure for legacy EEVEE and use the subsurface closure only where needed for EEVEE-Next leveraging the random sampling. This increases the compatibility with cycles that doesn't modulate the radius of the subsurface anymore. This change is only present in EEVEE-Next. This commit changes the principled BSDF code so that it is easier to follow the flow of data. For legacy EEVEE, the SSS switch is moved to a `radius == -1` check. --- .../shaders/closure_eval_surface_lib.glsl | 33 +++++-- .../shaders/eevee_nodetree_lib.glsl | 22 +++-- .../eevee_subsurface_convolve_comp.glsl | 4 +- .../shaders/eevee_subsurface_setup_comp.glsl | 4 +- .../gpu/shaders/gpu_shader_codegen_lib.glsl | 16 +++- .../material/gpu_shader_material_diffuse.glsl | 1 - .../gpu_shader_material_eevee_specular.glsl | 10 ++- .../material/gpu_shader_material_hair.glsl | 4 - .../gpu_shader_material_principled.glsl | 86 +++++++++++++------ .../material/gpu_shader_material_sheen.glsl | 1 - ...shader_material_subsurface_scattering.glsl | 20 +++-- .../material/gpu_shader_material_toon.glsl | 1 - .../nodes/node_shader_bsdf_principled.cc | 2 +- .../node_shader_subsurface_scattering.cc | 2 +- 14 files changed, 137 insertions(+), 69 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl index 92f46a59c8b..deea4351520 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl @@ -27,9 +27,9 @@ vec3 out_ssr_N; bool aov_is_valid = false; vec3 out_aov; -bool output_sss(ClosureDiffuse diffuse, ClosureOutputDiffuse diffuse_out) +bool output_sss(ClosureSubsurface diffuse, ClosureOutputDiffuse diffuse_out) { - if (diffuse.sss_id == 0u || !do_sss || !sssToggle || outputSssId == 0) { + if (diffuse.sss_radius.r == -1.0 || !do_sss || !sssToggle || outputSssId == 0) { return false; } if (renderPassSSSColor) { @@ -71,6 +71,7 @@ void output_aov(vec4 color, float value, uint hash) } /* Single BSDFs. */ + CLOSURE_EVAL_FUNCTION_DECLARE_1(DiffuseBSDF, Diffuse) Closure closure_eval(ClosureDiffuse diffuse) { @@ -83,8 +84,24 @@ Closure closure_eval(ClosureDiffuse diffuse) CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse); Closure closure = CLOSURE_DEFAULT; - if (!output_sss(diffuse, out_Diffuse_0)) { - closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight; + return closure; +} + +/* NOTE: Reuse the diffuse eval function. */ +Closure closure_eval(ClosureSubsurface subsurface) +{ + /* Glue with the old system. */ + CLOSURE_VARS_DECLARE_1(Diffuse); + + in_Diffuse_0.N = subsurface.N; + in_Diffuse_0.albedo = subsurface.color; + + CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse); + + Closure closure = CLOSURE_DEFAULT; + if (!output_sss(subsurface, out_Diffuse_0)) { + closure.radiance += out_Diffuse_0.radiance * subsurface.color * subsurface.weight; } return closure; } @@ -197,7 +214,7 @@ Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction) /* Dielectric BSDF */ CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy) -Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection) +Closure closure_eval(ClosureSubsurface diffuse, ClosureReflection reflection) { #if defined(DO_SPLIT_CLOSURE_EVAL) Closure closure = closure_eval(diffuse); @@ -230,7 +247,9 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection) /* Specular BSDF */ CLOSURE_EVAL_FUNCTION_DECLARE_3(SpecularBSDF, Diffuse, Glossy, Glossy) -Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, ClosureReflection coat) +Closure closure_eval(ClosureSubsurface diffuse, + ClosureReflection reflection, + ClosureReflection coat) { #if defined(DO_SPLIT_CLOSURE_EVAL) Closure closure = closure_eval(diffuse); @@ -267,7 +286,7 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, Closu /* Principled BSDF */ CLOSURE_EVAL_FUNCTION_DECLARE_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction) -Closure closure_eval(ClosureDiffuse diffuse, +Closure closure_eval(ClosureSubsurface diffuse, ClosureReflection reflection, ClosureReflection coat, ClosureRefraction refraction) diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 210eea740cf..bef2fe3bfae 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -51,10 +51,10 @@ ClosureType closure_type_get(ClosureRefraction cl) return CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; } -// ClosureType closure_type_get(ClosureSubsurface cl) -// { -// return CLOSURE_BSSRDF_BURLEY_ID; -// } +ClosureType closure_type_get(ClosureSubsurface cl) +{ + return CLOSURE_BSSRDF_BURLEY_ID; +} /** * Returns true if the closure is to be selected based on the input weight. @@ -127,11 +127,15 @@ Closure closure_eval(ClosureDiffuse diffuse) { ClosureUndetermined cl; closure_base_copy(cl, diffuse); - /* TODO: Have dedicated ClosureSubsurface */ - if (diffuse.sss_id != 0u) { - cl.type = CLOSURE_BSSRDF_BURLEY_ID; - cl.data.rgb = diffuse.sss_radius; - } + closure_select(g_diffuse_data, g_diffuse_rand, cl); + return Closure(0); +} + +Closure closure_eval(ClosureSubsurface diffuse) +{ + ClosureUndetermined cl; + closure_base_copy(cl, diffuse); + cl.data.rgb = diffuse.sss_radius; closure_select(g_diffuse_data, g_diffuse_rand, cl); return Closure(0); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl index d4623162330..e2a4a4ccc1f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl @@ -97,9 +97,7 @@ void main(void) return; } - /* TODO SSS closure. */ - ClosureDiffuse closure = to_closure_diffuse(gbuffer_closure_get(gbuf, 0)); - + ClosureSubsurface closure = to_closure_subsurface(gbuffer_closure_get(gbuf, 0)); float max_radius = reduce_max(closure.sss_radius); float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3]; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_setup_comp.glsl index bb858fe4b6d..a1e529a647d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_setup_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_subsurface_setup_comp.glsl @@ -26,12 +26,10 @@ void main(void) GBufferReader gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_normal_tx, texel); if (gbuffer_closure_get(gbuf, 0).type == CLOSURE_BSSRDF_BURLEY_ID) { - /* TODO SSS closure. */ - vec3 radiance = imageLoad(direct_light_img, texel).rgb + imageLoad(indirect_light_img, texel).rgb; - ClosureDiffuse closure = to_closure_diffuse(gbuffer_closure_get(gbuf, 0)); + ClosureSubsurface closure = to_closure_subsurface(gbuffer_closure_get(gbuf, 0)); float max_radius = reduce_max(closure.sss_radius); imageStore(radiance_img, texel, vec4(radiance, 0.0)); diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 4c1b9e58256..253af081591 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -174,8 +174,13 @@ struct ClosureDiffuse { float weight; vec3 color; vec3 N; +}; + +struct ClosureSubsurface { + float weight; + vec3 color; + vec3 N; vec3 sss_radius; - uint sss_id; }; struct ClosureTranslucent { @@ -234,7 +239,14 @@ ClosureDiffuse to_closure_diffuse(ClosureUndetermined cl) ClosureDiffuse closure; closure.N = cl.N; closure.color = cl.color; - /* TODO(fclem): BSSSRDF closure. */ + return closure; +} + +ClosureSubsurface to_closure_subsurface(ClosureUndetermined cl) +{ + ClosureSubsurface closure; + closure.N = cl.N; + closure.color = cl.color; closure.sss_radius = cl.data.xyz; return closure; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl index 6d358997bc8..57bc9c39ee3 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl @@ -8,7 +8,6 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Cl diffuse_data.weight = weight; diffuse_data.color = color.rgb; diffuse_data.N = N; - diffuse_data.sss_id = 0u; result = closure_eval(diffuse_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index bfe19dac0b0..c89a0fca09c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -39,18 +39,26 @@ void node_eevee_specular(vec4 diffuse, float alpha = (1.0 - transp) * weight; +#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES + /* EEVEE Legacy evaluates the subsurface as a diffuse closure. + * So this has no performance penalty. However, using a separate closure for subsurface + * (just like for EEVEE-Next) would induce a huge performance hit. */ + ClosureSubsurface diffuse_data; +#else ClosureDiffuse diffuse_data; +#endif diffuse_data.weight = alpha; diffuse_data.color = diffuse.rgb; diffuse_data.N = N; - diffuse_data.sss_id = 0u; +#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES /* WORKAROUND: Nasty workaround to the current interface with the closure evaluation. * Ideally the occlusion input should be move to the output node or removed all-together. * This is temporary to avoid a regression in 3.2 and should be removed after EEVEE-Next rewrite. */ diffuse_data.sss_radius.r = occlusion; diffuse_data.sss_radius.g = -1.0; /* Flag */ +#endif ClosureReflection reflection_data; reflection_data.weight = alpha; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl index ce294637273..fddc9d7aaeb 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl @@ -27,8 +27,6 @@ void node_bsdf_hair(vec4 color, hair_data.weight = weight; hair_data.color = color.rgb; hair_data.N = g_data.N; - hair_data.sss_radius = vec3(0.0); - hair_data.sss_id = 0u; #endif result = closure_eval(hair_data); } @@ -68,8 +66,6 @@ void node_bsdf_hair_principled(vec4 color, hair_data.weight = weight; hair_data.color = color.rgb; hair_data.N = g_data.N; - hair_data.sss_radius = vec3(0.0); - hair_data.sss_id = 0u; #endif result = closure_eval(hair_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index c1a95d5ad82..120ce506565 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -59,7 +59,7 @@ void node_bsdf_principled(vec4 base_color, const float do_coat, const float do_refraction, const float do_multiscatter, - float do_sss, + const float do_sss, out Closure result) { /* Match cycles. */ @@ -87,13 +87,6 @@ void node_bsdf_principled(vec4 base_color, base_color = max(base_color, vec4(0.0)); vec4 clamped_base_color = min(base_color, vec4(1.0)); - vec4 diffuse_sss_base_color = base_color; - if (subsurface_weight > 0.0) { - /* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in Cycles. - * So it's clamped there and we clamp here for consistency with Cycles. */ - diffuse_sss_base_color = mix(diffuse_sss_base_color, clamped_base_color, subsurface_weight); - } - N = safe_normalize(N); CN = safe_normalize(CN); vec3 V = coordinate_incoming(g_data.P); @@ -106,19 +99,14 @@ void node_bsdf_principled(vec4 base_color, weight *= alpha; /* First layer: Sheen */ - ClosureDiffuse diffuse_data; - diffuse_data.N = N; - + vec3 sheen_data_color = vec3(0.0); if (sheen_weight > 0.0) { /* TODO: Maybe sheen_weight should be specular. */ vec3 sheen_color = sheen_weight * sheen_tint.rgb * principled_sheen(NV, sheen_roughness); - diffuse_data.color = weight * sheen_color; + sheen_data_color = weight * sheen_color; /* Attenuate lower layers */ weight *= max((1.0 - math_reduce_max(sheen_color)), 0.0); } - else { - diffuse_data.color = vec3(0.0); - } /* Second layer: Coat */ ClosureReflection coat_data; @@ -218,25 +206,69 @@ void node_bsdf_principled(vec4 base_color, bsdf_lut(F0, F90, vec3(0.0), NV, roughness, eta, do_multiscatter != 0.0, reflectance, unused); reflection_data.color += weight * reflectance; + /* Adjust the weight of picking the closure. */ + reflection_data.color *= coat_tint.rgb; + reflection_data.weight = math_average(reflection_data.color); + reflection_data.color *= safe_rcp(reflection_data.weight); + /* Attenuate lower layers */ weight *= max((1.0 - math_reduce_max(reflectance)), 0.0); } - /* Diffuse component */ - if (true) { - diffuse_data.sss_radius = subsurface_weight * - max(subsurface_radius * subsurface_scale, vec3(0.0)); - diffuse_data.sss_id = uint(do_sss); - diffuse_data.color += weight * diffuse_sss_base_color.rgb * coat_tint.rgb; +#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES + /* EEVEE Legacy evaluates the subsurface as a diffuse closure. + * So this has no performance penalty. However, using a separate closure for subsurface + * (just like for EEVEE-Next) would induce a huge performance hit. */ + ClosureSubsurface diffuse_data; +#else + ClosureDiffuse diffuse_data; +#endif + diffuse_data.N = N; + + subsurface_radius = max(subsurface_radius * subsurface_scale, vec3(0.0)); + + /* Subsurface component */ + if (subsurface_weight > 0.0) { +#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES + /* For Legacy EEVEE, Subsurface is just part of the diffuse. Just evaluate the mixed color. */ + /* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in + * Cycles. So it's clamped there and we clamp here for consistency with Cycles. */ + base_color = mix(base_color, clamped_base_color, subsurface_weight); + + if (do_sss == 0.0) { + diffuse_data.sss_radius = vec3(-1); + } + else { + diffuse_data.sss_radius = subsurface_weight * subsurface_radius; + } +#else + ClosureSubsurface sss_data; + sss_data.N = N; + sss_data.sss_radius = subsurface_radius; + /* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in + * Cycles. So it's clamped there and we clamp here for consistency with Cycles. */ + sss_data.color = (subsurface_weight * weight) * clamped_base_color.rgb * coat_tint.rgb; + /* Add energy of the sheen layer until we have proper sheen BSDF. */ + sss_data.color += sheen_data_color; + + sss_data.weight = math_average(sss_data.color); + sss_data.color *= safe_rcp(sss_data.weight); + result = closure_eval(sss_data); + + /* Attenuate lower layers */ + weight *= max((1.0 - subsurface_weight), 0.0); +#endif } - /* Adjust the weight of picking the closure. */ - reflection_data.color *= coat_tint.rgb; - reflection_data.weight = math_average(reflection_data.color); - reflection_data.color *= safe_rcp(reflection_data.weight); + /* Diffuse component */ + if (true) { + diffuse_data.color = weight * base_color.rgb * coat_tint.rgb; + /* Add energy of the sheen layer until we have proper sheen BSDF. */ + diffuse_data.color += sheen_data_color; - diffuse_data.weight = math_average(diffuse_data.color); - diffuse_data.color *= safe_rcp(diffuse_data.weight); + diffuse_data.weight = math_average(diffuse_data.color); + diffuse_data.color *= safe_rcp(diffuse_data.weight); + } /* Ref. #98190: Defines are optimizations for old compilers. * Might become unnecessary with EEVEE-Next. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_sheen.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_sheen.glsl index b8edd149207..2104b0215b3 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_sheen.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_sheen.glsl @@ -13,7 +13,6 @@ void node_bsdf_sheen(vec4 color, float roughness, vec3 N, float weight, out Clos diffuse_data.weight = weight; diffuse_data.color = color.rgb; diffuse_data.N = N; - diffuse_data.sss_id = 0u; result = closure_eval(diffuse_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index 6bfb2cf8816..7ea7556f339 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -9,7 +9,7 @@ void node_subsurface_scattering(vec4 color, float anisotropy, vec3 N, float weight, - float do_sss, + const float do_sss, out Closure result) { color = max(color, vec4(0.0)); @@ -18,12 +18,16 @@ void node_subsurface_scattering(vec4 color, ior = max(ior, 1e-5); N = safe_normalize(N); - ClosureDiffuse diffuse_data; - diffuse_data.weight = weight; - diffuse_data.color = color.rgb; - diffuse_data.N = N; - diffuse_data.sss_radius = radius * scale; - diffuse_data.sss_id = uint(do_sss); + ClosureSubsurface sss_data; + sss_data.weight = weight; + sss_data.color = color.rgb; + sss_data.N = N; + sss_data.sss_radius = radius * scale; - result = closure_eval(diffuse_data); +#ifdef GPU_SHADER_EEVEE_LEGACY_DEFINES + if (do_sss == 0.0) { + sss_data.sss_radius = vec3(-1); + } +#endif + result = closure_eval(sss_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl index cb34284b9c9..a4b8256d364 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl @@ -13,7 +13,6 @@ void node_bsdf_toon( diffuse_data.weight = weight; diffuse_data.color = color.rgb; diffuse_data.N = N; - diffuse_data.sss_id = 0u; result = closure_eval(diffuse_data); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index 8f140ea9c2a..1fa63c6fc93 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -330,7 +330,7 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPU_constant(&use_coat_f), GPU_constant(&use_refract_f), GPU_constant(&use_multi_scatter), - GPU_uniform(&use_sss)); + GPU_constant(&use_sss)); } static void node_shader_update_principled(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc index 3f890b47f4d..af9c98d4c38 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc @@ -59,7 +59,7 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE); - return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_uniform(&use_sss)); + return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_constant(&use_sss)); } static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node)