diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-02-07 20:03:58 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-02-07 20:03:58 +0300 |
commit | fe008592024c3081c5b43b82482d46e41262fdb1 (patch) | |
tree | 0c42627c2b504dbb2151a1daea726255506fba14 /source/blender/draw | |
parent | e49b702527440997ef32967ef368c1212a2311f9 (diff) |
EEVEE: Rewrite closure_lit_lib to reduce complexity
This rewrite improves:
- Code clarity: Less duplicated code and removes a few hacks.
- Compile time performance: Shader code was divided by 3 in average.
I did not profile the compilation time but it is faster.
- Shading Perf: Noticed a 25% performance improvement on
the shading pass with default dielectric principled bsdf.
- Fix Principled Tint being white if color is black
- It seems to have fixed issues on some drivers giving some incorect
results.
- Changes Principled BSDF support to be less hacky.
Diffstat (limited to 'source/blender/draw')
12 files changed, 852 insertions, 620 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c index ed7d53b51af..230f29a1102 100644 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c @@ -191,6 +191,12 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output); DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output); DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->color_double_buffer); + DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); + DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); + DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); + DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); @@ -198,7 +204,6 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_uniform_int(grp, "neighborOffset", &effects->ssr_neighbor_ofs, 1); if ((effects->enabled_effects & EFFECT_GTAO) != 0) { - DRW_shgroup_uniform_texture(grp, "utilTex", EEVEE_materials_get_util_tex()); DRW_shgroup_uniform_texture_ref(grp, "horizonBuffer", &effects->gtao_horizons); } diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 7a277c18f01..e045aa482a8 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -281,21 +281,7 @@ static void eevee_shader_library_ensure(void) DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib); DRW_SHADER_LIB_ADD(e_data.lib, closure_lib); DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib); - - /* Add one for each Closure */ - e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl, - datatoc_closure_lit_lib_glsl); - - DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl"); + DRW_SHADER_LIB_ADD(e_data.lib, closure_lit_lib); e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_frag_glsl); 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 3560ae62a84..25eb0133616 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -79,7 +79,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) { /* Unreal specular matching : if specular color is below 2% intensity, * treat as shadowning */ - return saturate(50.0 * dot(f0, vec3(0.3, 0.6, 0.1))) * lut.y * abs(f90) + lut.x * f0; + return lut.y * f90 + lut.x * f0; } /* Multi-scattering brdf approximation from : @@ -87,11 +87,7 @@ vec3 F_brdf_single_scatter(vec3 f0, vec3 f90, vec2 lut) * by Carmelo J. Fdez-Agüera. */ vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) { - vec3 FssEss = F_brdf_single_scatter(f0, f90, lut); - /* Hack to avoid many more shader variations. */ - if (f90.g < 0.0) { - return FssEss; - } + vec3 FssEss = lut.y * f90 + lut.x * f0; float Ess = lut.x + lut.y; float Ems = 1.0 - Ess; @@ -102,8 +98,6 @@ vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut) return FssEss + Fms * Ems; } -#define F_brdf(f0, f90, lut) F_brdf_multi_scatter(f0, f90, lut) - /* GGX */ float D_ggx_opti(float NH, float a2) { diff --git a/source/blender/draw/engines/eevee/shaders/closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl index b56a186ab3f..9ca25ef240f 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl @@ -147,17 +147,27 @@ Closure closure_emission(vec3 rgb) #ifndef VOLUMETRICS +/* Let radiance passthrough or replace it to get the BRDF and color + * to applied to the SSR result. */ +vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id) +{ + return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance; +} + void closure_load_ssr_data( - vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl) + vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl) { /* Still encode to avoid artifacts in the SSR pass. */ vec3 vN = normalize(mat3(ViewMatrix) * N); - cl.ssr_normal = normal_encode(vN, viewVec); + cl.ssr_normal = normal_encode(vN, viewCameraVec); - if (ssr_id == outputSsrId) { - cl.ssr_data = vec4(ssr_spec, roughness); + if (ssrToggle && int(ssr_id) == outputSsrId) { + cl.ssr_data = vec4(ssr_radiance, roughness); cl.flag |= CLOSURE_SSR_FLAG; } + else { + cl.radiance += ssr_radiance; + } } void closure_load_sss_data( @@ -169,13 +179,11 @@ void closure_load_sss_data( cl.sss_radius = radius; cl.sss_albedo = sss_albedo; cl.flag |= CLOSURE_SSS_FLAG; - cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0)); + /* Irradiance will be convolved by SSSS pass. Do not add to radiance. */ + sss_irradiance = vec3(0); } - else # endif - { - cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo); - } + cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo; } #endif diff --git a/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl index 30ce60f3ec0..63b205b7aec 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl @@ -1,545 +1,757 @@ #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lights_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) #pragma BLENDER_REQUIRE(ssr_lib.glsl) /** - * AUTO CONFIG - * We include the file multiple times each time with a different configuration. - * This leads to a lot of deadcode. Better idea would be to only generate the one needed. - */ -#if !defined(SURFACE_DEFAULT) -# define SURFACE_DEFAULT -# define CLOSURE_NAME eevee_closure_default -# define CLOSURE_DIFFUSE -# define CLOSURE_GLOSSY -#endif /* SURFACE_DEFAULT */ - -#if !defined(SURFACE_DEFAULT_CLEARCOAT) && !defined(CLOSURE_NAME) -# define SURFACE_DEFAULT_CLEARCOAT -# define CLOSURE_NAME eevee_closure_default_clearcoat -# define CLOSURE_DIFFUSE -# define CLOSURE_GLOSSY -# define CLOSURE_CLEARCOAT -#endif /* SURFACE_DEFAULT_CLEARCOAT */ - -#if !defined(SURFACE_PRINCIPLED) && !defined(CLOSURE_NAME) -# define SURFACE_PRINCIPLED -# define CLOSURE_NAME eevee_closure_principled -# define CLOSURE_DIFFUSE -# define CLOSURE_GLOSSY -# define CLOSURE_CLEARCOAT -# define CLOSURE_REFRACTION -# define CLOSURE_SUBSURFACE -#endif /* SURFACE_PRINCIPLED */ - -#if !defined(SURFACE_CLEARCOAT) && !defined(CLOSURE_NAME) -# define SURFACE_CLEARCOAT -# define CLOSURE_NAME eevee_closure_clearcoat -# define CLOSURE_GLOSSY -# define CLOSURE_CLEARCOAT -#endif /* SURFACE_CLEARCOAT */ - -#if !defined(SURFACE_DIFFUSE) && !defined(CLOSURE_NAME) -# define SURFACE_DIFFUSE -# define CLOSURE_NAME eevee_closure_diffuse -# define CLOSURE_DIFFUSE -#endif /* SURFACE_DIFFUSE */ - -#if !defined(SURFACE_SUBSURFACE) && !defined(CLOSURE_NAME) -# define SURFACE_SUBSURFACE -# define CLOSURE_NAME eevee_closure_subsurface -# define CLOSURE_DIFFUSE -# define CLOSURE_SUBSURFACE -#endif /* SURFACE_SUBSURFACE */ - -#if !defined(SURFACE_SKIN) && !defined(CLOSURE_NAME) -# define SURFACE_SKIN -# define CLOSURE_NAME eevee_closure_skin -# define CLOSURE_DIFFUSE -# define CLOSURE_SUBSURFACE -# define CLOSURE_GLOSSY -#endif /* SURFACE_SKIN */ - -#if !defined(SURFACE_GLOSSY) && !defined(CLOSURE_NAME) -# define SURFACE_GLOSSY -# define CLOSURE_NAME eevee_closure_glossy -# define CLOSURE_GLOSSY -#endif /* SURFACE_GLOSSY */ - -#if !defined(SURFACE_REFRACT) && !defined(CLOSURE_NAME) -# define SURFACE_REFRACT -# define CLOSURE_NAME eevee_closure_refraction -# define CLOSURE_REFRACTION -#endif /* SURFACE_REFRACT */ - -#if !defined(SURFACE_GLASS) && !defined(CLOSURE_NAME) -# define SURFACE_GLASS -# define CLOSURE_NAME eevee_closure_glass -# define CLOSURE_GLOSSY -# define CLOSURE_REFRACTION -#endif /* SURFACE_GLASS */ - -/* Safety : CLOSURE_CLEARCOAT implies CLOSURE_GLOSSY */ -#ifdef CLOSURE_CLEARCOAT -# ifndef CLOSURE_GLOSSY -# define CLOSURE_GLOSSY -# endif -#endif /* CLOSURE_CLEARCOAT */ - -void CLOSURE_NAME(vec3 N -#ifdef CLOSURE_DIFFUSE - , - vec3 albedo -#endif -#ifdef CLOSURE_GLOSSY - , - vec3 f0, - vec3 f90, - int ssr_id -#endif -#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION) - , - float roughness -#endif -#ifdef CLOSURE_CLEARCOAT - , - vec3 C_N, - float C_intensity, - float C_roughness -#endif -#if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE) - , - float ao -#endif -#ifdef CLOSURE_SUBSURFACE - , - float sss_scale -#endif -#ifdef CLOSURE_REFRACTION - , - float ior -#endif - , - const bool use_contact_shadows -#ifdef CLOSURE_DIFFUSE - , - out vec3 out_diff -#endif -#ifdef CLOSURE_GLOSSY - , - out vec3 out_spec -#endif -#ifdef CLOSURE_REFRACTION - , - out vec3 out_refr -#endif -#ifdef CLOSURE_GLOSSY - , - out vec3 ssr_spec -#endif -) + * Extensive use of Macros to be able to change the maximum amount of evaluated closure easily. + * NOTE: GLSL does not support variadic macros. + * + * Example + * // Declare the cl_eval function + * CLOSURE_EVAL_FUNCTION_DECLARE_3(name, Diffuse, Glossy, Refraction); + * // Declare the inputs & outputs + * CLOSURE_VARS_DECLARE(Diffuse, Glossy, Refraction); + * // Specify inputs + * in_Diffuse_0.N = N; + * ... + * // Call the cl_eval function + * CLOSURE_EVAL_FUNCTION_3(name, Diffuse, Glossy, Refraction); + * // Get the cl_out + * closure.radiance = out_Diffuse_0.radiance + out_Glossy_1.radiance + out_Refraction_2.radiance; + **/ + +#define CLOSURE_VARS_DECLARE(t0, t1, t2, t3) \ + ClosureInput##t0 in_##t0##_0 = CLOSURE_INPUT_##t0##_DEFAULT; \ + ClosureInput##t1 in_##t1##_1 = CLOSURE_INPUT_##t1##_DEFAULT; \ + ClosureInput##t2 in_##t2##_2 = CLOSURE_INPUT_##t2##_DEFAULT; \ + ClosureInput##t3 in_##t3##_3 = CLOSURE_INPUT_##t3##_DEFAULT; \ + ClosureOutput##t0 out_##t0##_0; \ + ClosureOutput##t1 out_##t1##_1; \ + ClosureOutput##t2 out_##t2##_2; \ + ClosureOutput##t3 out_##t3##_3; + +#define CLOSURE_EVAL_DECLARE(t0, t1, t2, t3) \ + ClosureEval##t0 eval_##t0##_0 = closure_##t0##_eval_init(in_##t0##_0, cl_common, out_##t0##_0); \ + ClosureEval##t1 eval_##t1##_1 = closure_##t1##_eval_init(in_##t1##_1, cl_common, out_##t0##_0); \ + ClosureEval##t2 eval_##t2##_2 = closure_##t2##_eval_init(in_##t2##_2, cl_common, out_##t0##_0); \ + ClosureEval##t3 eval_##t3##_3 = closure_##t3##_eval_init(in_##t3##_3, cl_common, out_##t0##_0); + +#define CLOSURE_META_SUBROUTINE(subroutine, t0, t1, t2, t3) \ + closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, out_##t0##_0); \ + closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, out_##t1##_1); \ + closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, out_##t2##_2); \ + closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, out_##t3##_3); + +#define CLOSURE_META_SUBROUTINE_DATA(subroutine, sub_data, t0, t1, t2, t3) \ + closure_##t0##_##subroutine(in_##t0##_0, eval_##t0##_0, cl_common, sub_data, out_##t0##_0); \ + closure_##t1##_##subroutine(in_##t1##_1, eval_##t1##_1, cl_common, sub_data, out_##t1##_1); \ + closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \ + closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3); + +/* Inputs are inout so that callers can get the final inputs used for evaluation. */ +#define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \ + void closure_##name##_eval(inout ClosureInput##t0 in_##t0##_0, \ + inout ClosureInput##t1 in_##t1##_1, \ + inout ClosureInput##t2 in_##t2##_2, \ + inout ClosureInput##t3 in_##t3##_3, \ + out ClosureOutput##t0 out_##t0##_0, \ + out ClosureOutput##t1 out_##t1##_1, \ + out ClosureOutput##t2 out_##t2##_2, \ + out ClosureOutput##t3 out_##t3##_3) \ + { \ + ClosureEvalCommon cl_common = closure_Common_eval_init(); \ + CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \ +\ + ClosurePlanarData planar; \ + PLANAR_ITER_BEGIN(planar) \ + { \ + CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \ + } \ + PLANAR_ITER_END \ +\ + ClosureCubemapData cube; \ + CUBEMAP_ITER_BEGIN(cube) \ + { \ + CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \ + } \ + CUBEMAP_ITER_END \ +\ + ClosureGridData grid; \ + GRID_ITER_BEGIN(grid) \ + { \ + CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \ + } \ + GRID_ITER_END \ +\ + CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \ +\ + ClosureLightData light; \ + LIGHT_ITER_BEGIN(light) \ + { \ + CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \ + } \ + LIGHT_ITER_END \ +\ + CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \ + } + +#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \ + closure_##name##_eval(in_##t0##_0, \ + in_##t1##_1, \ + in_##t2##_2, \ + in_##t3##_3, \ + out_##t0##_0, \ + out_##t1##_1, \ + out_##t2##_2, \ + out_##t3##_3) + +#define CLOSURE_EVAL_FUNCTION_DECLARE_1(name, t0) \ + CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, Dummy, Dummy, Dummy) +#define CLOSURE_EVAL_FUNCTION_DECLARE_2(name, t0, t1) \ + CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, Dummy, Dummy) +#define CLOSURE_EVAL_FUNCTION_DECLARE_3(name, t0, t1, t2) \ + CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, Dummy) +#define CLOSURE_EVAL_FUNCTION_DECLARE_4(name, t0, t1, t2, t3) \ + CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) + +#define CLOSURE_VARS_DECLARE_1(t0) CLOSURE_VARS_DECLARE(t0, Dummy, Dummy, Dummy) +#define CLOSURE_VARS_DECLARE_2(t0, t1) CLOSURE_VARS_DECLARE(t0, t1, Dummy, Dummy) +#define CLOSURE_VARS_DECLARE_3(t0, t1, t2) CLOSURE_VARS_DECLARE(t0, t1, t2, Dummy) +#define CLOSURE_VARS_DECLARE_4(t0, t1, t2, t3) CLOSURE_VARS_DECLARE(t0, t1, t2, t3) + +#define CLOSURE_EVAL_FUNCTION_1(name, t0) CLOSURE_EVAL_FUNCTION(name, t0, Dummy, Dummy, Dummy) +#define CLOSURE_EVAL_FUNCTION_2(name, t0, t1) CLOSURE_EVAL_FUNCTION(name, t0, t1, Dummy, Dummy) +#define CLOSURE_EVAL_FUNCTION_3(name, t0, t1, t2) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, Dummy) +#define CLOSURE_EVAL_FUNCTION_4(name, t0, t1, t2, t3) CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) + +/* -------------------------------------------------------------------- */ +/** \name Common cl_eval data + * + * Eval data not dependant on input parameters. All might not be used but unused ones + * will be optimized out. + * \{ */ + +struct ClosureEvalCommon { + vec3 V; /** View vector. */ + vec3 P; /** Surface position. */ + vec3 N; /** Normal vector, always facing camera. */ + vec3 vN; /** Normal vector, always facing camera. (viewspace) */ + vec3 vP; /** Surface position. (viewspace) */ + vec3 vNg; /** Geometric normal, always facing camera. (viewspace) */ + vec4 rand; /** Random numbers. 3 random sequences. zw is a random point on a circle. */ + + float specular_accum; /** Specular probe accumulator. Shared between planar and cubemap probe. */ + float diffuse_accum; /** Diffuse probe accumulator. */ + float tracing_depth; /** Viewspace depth to start raytracing from. */ +}; + +/* Common cl_out struct used by most closures. */ +struct ClosureOutput { + vec3 radiance; +}; + +ClosureEvalCommon closure_Common_eval_init(void) { -#ifdef CLOSURE_DIFFUSE - out_diff = vec3(0.0); -#endif + ClosureEvalCommon cl_eval; + cl_eval.rand = texelfetch_noise_tex(gl_FragCoord.xy); + cl_eval.V = cameraVec; + cl_eval.P = worldPosition; + cl_eval.N = gl_FrontFacing ? worldNormal : -worldNormal; + cl_eval.vN = gl_FrontFacing ? viewNormal : -viewNormal; + cl_eval.vP = viewPosition; + cl_eval.vNg = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition))); + /* TODO(fclem) See if we can avoid this complicated setup. */ + cl_eval.tracing_depth = gl_FragCoord.z; + /* Constant bias (due to depth buffer precision) */ + /* Magic numbers for 24bits of precision. + * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */ + cl_eval.tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z); + /* Convert to view Z. */ + cl_eval.tracing_depth = get_view_z_from_depth(cl_eval.tracing_depth); -#ifdef CLOSURE_GLOSSY - out_spec = vec3(0.0); -#endif + cl_eval.specular_accum = 1.0; + cl_eval.diffuse_accum = 1.0; + return cl_eval; +} -#ifdef CLOSURE_REFRACTION - out_refr = vec3(0.0); -#endif +/** \} */ -#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) - /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ - return; -#else +/* -------------------------------------------------------------------- */ +/** \name Light Loop + * + * \{ */ - /* Zero length vectors cause issues, see: T51979. */ - float len = length(N); - if (isnan(len)) { - return; - } - N /= len; +struct ClosureLightData { + LightData data; /** Light Data. */ + vec4 L; /** Non-Normalized Light Vector (surface to light) with length in W component. */ + float vis; /** Light visibility. */ + float contact_shadow; /** Result of contact shadow tracing. */ +}; -# ifdef CLOSURE_CLEARCOAT - len = length(C_N); - if (isnan(len)) { - return; - } - C_N /= len; -# endif +ClosureLightData closure_light_eval_init(ClosureEvalCommon cl_common, int light_id) +{ + ClosureLightData light; + light.data = lights_data[light_id]; + + light.L.xyz = light.data.l_position - cl_common.P; + light.L.w = length(light.L.xyz); + + light.vis = light_visibility(light.data, cl_common.P, light.L); + light.contact_shadow = light_contact_shadows(light.data, + cl_common.P, + cl_common.vP, + cl_common.tracing_depth, + cl_common.vNg, + cl_common.rand.x, + light.vis); + + return light; +} -# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION) - roughness = clamp(roughness, 1e-8, 0.9999); - float roughnessSquared = roughness * roughness; -# endif +#define LIGHT_ITER_BEGIN(light) \ + for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \ + light = closure_light_eval_init(cl_common, i); \ + if (light.vis < 1e-8) { \ + continue; \ + } -# ifdef CLOSURE_CLEARCOAT - C_roughness = clamp(C_roughness, 1e-8, 0.9999); - float C_roughnessSquared = C_roughness * C_roughness; -# endif +#define LIGHT_ITER_END } - vec3 V = cameraVec; +/** \} */ - vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); +/* -------------------------------------------------------------------- */ +/** \name Glossy Probe Loop + * + * \{ */ - /* ---------------------------------------------------------------- */ - /* -------------------- SCENE LIGHTS LIGHTING --------------------- */ - /* ---------------------------------------------------------------- */ +struct ClosureCubemapData { + int id; /** Probe id. */ + float attenuation; /** Attenuation. */ +}; -# ifdef CLOSURE_GLOSSY - vec2 lut_uv = lut_coords_ltc(dot(N, V), roughness); - vec4 ltc_mat = texture(utilTex, vec3(lut_uv, 0.0)).rgba; -# endif +ClosureCubemapData closure_cubemap_eval_init(int cube_id, inout ClosureEvalCommon cl_common) +{ + ClosureCubemapData cube; + cube.id = cube_id; + cube.attenuation = probe_attenuation_cube(cube_id, cl_common.P); + cube.attenuation = min(cube.attenuation, cl_common.specular_accum); + cl_common.specular_accum -= cube.attenuation; + return cube; +} -# ifdef CLOSURE_CLEARCOAT - vec2 lut_uv_clear = lut_coords_ltc(dot(C_N, V), C_roughness); - vec4 ltc_mat_clear = texture(utilTex, vec3(lut_uv_clear, 0.0)).rgba; - vec3 out_spec_clear = vec3(0.0); -# endif +#define CUBEMAP_ITER_BEGIN(cube) \ + /* Starts at 1 because 0 is world cubemap. */ \ + for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; i++) { \ + cube = closure_cubemap_eval_init(i, cl_common); \ + if (cube.attenuation < 1e-8) { \ + continue; \ + } - float tracing_depth = gl_FragCoord.z; - /* Constant bias (due to depth buffer precision) */ - /* Magic numbers for 24bits of precision. - * From http://terathon.com/gdc07_lengyel.pdf (slide 26) */ - tracing_depth -= mix(2.4e-7, 4.8e-7, gl_FragCoord.z); - /* Convert to view Z. */ - tracing_depth = get_view_z_from_depth(tracing_depth); +#define CUBEMAP_ITER_END } - vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition))); +/** \} */ - for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) { - LightData ld = lights_data[i]; +/* -------------------------------------------------------------------- */ +/** \name Glossy Planar probe Loop + * + * Should be run first, as it is replace by the SSR pass if SSR is enabled. + * \{ */ - vec4 l_vector; /* Non-Normalized Light Vector with length in last component. */ - l_vector.xyz = ld.l_position - worldPosition; - l_vector.w = length(l_vector.xyz); +struct ClosurePlanarData { + int id; /** Probe id. */ + PlanarData data; /** planars_data[id]. */ + float attenuation; /** Attenuation. */ +}; - float l_vis = light_visibility(ld, - worldPosition, - viewPosition, - tracing_depth, - true_normal, - rand.x, - use_contact_shadows, - l_vector); +ClosurePlanarData closure_planar_eval_init(int planar_id, inout ClosureEvalCommon cl_common) +{ + ClosurePlanarData planar; + planar.id = planar_id; + planar.data = planars_data[planar_id]; + planar.attenuation = probe_attenuation_planar(planar.data, cl_common.P, cl_common.N, 0.0); + planar.attenuation = min(planar.attenuation, cl_common.specular_accum); + cl_common.specular_accum -= planar.attenuation; + return planar; +} - if (l_vis < 1e-8) { - continue; +#define PLANAR_ITER_BEGIN(planar) \ + /* Starts at 1 because 0 is world probe */ \ + for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumPlanar && i < MAX_PLANAR; i++) { \ + planar = closure_planar_eval_init(i, cl_common); \ + if (planar.attenuation < 1e-8) { \ + continue; \ } - vec3 l_color_vis = ld.l_color * l_vis; +#define PLANAR_ITER_END } -# ifdef CLOSURE_DIFFUSE - out_diff += l_color_vis * light_diffuse(ld, N, V, l_vector); -# endif +/** \} */ -# ifdef CLOSURE_GLOSSY - out_spec += l_color_vis * light_specular(ld, ltc_mat, N, V, l_vector) * ld.l_spec; -# endif +/* -------------------------------------------------------------------- */ +/** \name Irradiance Grid Loop + * + * \{ */ -# ifdef CLOSURE_CLEARCOAT - out_spec_clear += l_color_vis * light_specular(ld, ltc_mat_clear, C_N, V, l_vector) * - ld.l_spec; -# endif - } +struct ClosureGridData { + int id; /** Grid id. */ + GridData data; /** grids_data[id] */ + float attenuation; /** Attenuation. */ + vec3 local_pos; /** Local position inside the grid. */ +}; + +ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common) +{ + ClosureGridData grid; + grid.id = id; + grid.data = grids_data[id]; + grid.attenuation = probe_attenuation_grid(grid.data, cl_common.P, grid.local_pos); + grid.attenuation = min(grid.attenuation, cl_common.specular_accum); + cl_common.diffuse_accum -= grid.attenuation; + return grid; +} -# ifdef CLOSURE_GLOSSY - vec2 brdf_lut_lights = texture(utilTex, vec3(lut_uv, 1.0)).ba; - out_spec *= F_brdf(f0, f90, brdf_lut_lights.xy); -# endif - -# ifdef CLOSURE_CLEARCOAT - vec2 brdf_lut_lights_clear = texture(utilTex, vec3(lut_uv_clear, 1.0)).ba; - out_spec_clear *= F_brdf(vec3(0.04), vec3(1.0), brdf_lut_lights_clear.xy); - out_spec += out_spec_clear * C_intensity; -# endif - - /* ---------------------------------------------------------------- */ - /* ---------------- SPECULAR ENVIRONMENT LIGHTING ----------------- */ - /* ---------------------------------------------------------------- */ - - /* Accumulate incoming light from all sources until accumulator is full. Then apply Occlusion and - * BRDF. */ -# ifdef CLOSURE_GLOSSY - vec4 spec_accum = vec4(0.0); -# endif - -# ifdef CLOSURE_CLEARCOAT - vec4 C_spec_accum = vec4(0.0); -# endif - -# ifdef CLOSURE_REFRACTION - vec4 refr_accum = vec4(0.0); -# endif - -# ifdef CLOSURE_GLOSSY - /* ---------------------------- */ - /* Planar Reflections */ - /* ---------------------------- */ - - for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar && spec_accum.a < 0.999; i++) { - PlanarData pd = planars_data[i]; - - /* Fade on geometric normal. */ - float fade = probe_attenuation_planar( - pd, worldPosition, (gl_FrontFacing) ? worldNormal : -worldNormal, roughness); - - if (fade > 0.0) { - if (!(ssrToggle && ssr_id == outputSsrId)) { - vec3 spec = probe_evaluate_planar(float(i), pd, worldPosition, N, V, roughness, fade); - accumulate_light(spec, fade, spec_accum); - } - -# ifdef CLOSURE_CLEARCOAT - vec3 C_spec = probe_evaluate_planar(float(i), pd, worldPosition, C_N, V, C_roughness, fade); - accumulate_light(C_spec, fade, C_spec_accum); -# endif +#define GRID_ITER_BEGIN(grid) \ + /* Starts at 1 because 0 is world irradiance. */ \ + for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; i++) { \ + grid = closure_grid_eval_init(i, cl_common); \ + if (grid.attenuation < 1e-8) { \ + continue; \ } + +#define GRID_ITER_END } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dummy Closure + * + * Dummy closure type that will be optimized out by the compiler. + * \{ */ + +#define ClosureInputDummy ClosureOutput +#define ClosureOutputDummy ClosureOutput +#define ClosureEvalDummy ClosureOutput +#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0)) +#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY +#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY +#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Dummy_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Dummy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Dummy_indirect_end(cl_in, cl_eval, cl_common, cl_out) +#define closure_Dummy_light_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Dummy_eval_end(cl_in, cl_eval, cl_common, cl_out) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glossy Closure + * \{ */ + +struct ClosureInputGlossy { + vec3 N; /** Shading normal. */ + float roughness; /** Input roughness, not squared. */ +}; + +#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0) + +struct ClosureEvalGlossy { + vec4 ltc_mat; /** LTC matrix values. */ + float ltc_brdf_scale; /** LTC BRDF scaling. */ + vec3 probe_sampling_dir; /** Direction to sample probes from. */ +}; + +/* Stubs. */ +#define ClosureOutputGlossy ClosureOutput +#define closure_Glossy_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) + +#ifdef STEP_RESOLVE /* SSR */ +/* Prototype. */ +void ssr_resolve(ClosureInputGlossy cl_in, + inout ClosureEvalCommon cl_common, + inout ClosureOutputGlossy cl_out); +#endif + +ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in, + inout ClosureEvalCommon cl_common, + out ClosureOutputGlossy cl_out) +{ + cl_in.N = safe_normalize(cl_in.N); + cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999); + cl_out.radiance = vec3(0.0); + +#ifdef STEP_RESOLVE /* SSR */ + ssr_resolve(cl_in, cl_common, cl_out); +#endif + + float NV = dot(cl_in.N, cl_common.V); + vec2 lut_uv = lut_coords_ltc(NV, cl_in.roughness); + + ClosureEvalGlossy cl_eval; + cl_eval.ltc_mat = texture(utilTex, vec3(lut_uv, LTC_MAT_LAYER)); + cl_eval.probe_sampling_dir = specular_dominant_dir(cl_in.N, cl_common.V, sqr(cl_in.roughness)); + + /* The brdf split sum LUT is applied after the radiance accumulation. + * Correct the LTC so that its energy is constant. */ + /* TODO(fclem) Optimize this so that only one scale factor is stored. */ + vec4 ltc_brdf = texture(utilTex, vec3(lut_uv, LTC_BRDF_LAYER)).barg; + vec2 split_sum_brdf = ltc_brdf.zw; + cl_eval.ltc_brdf_scale = (ltc_brdf.x + ltc_brdf.y) / (split_sum_brdf.x + split_sum_brdf.y); + return cl_eval; +} + +void closure_Glossy_light_eval(ClosureInputGlossy cl_in, + ClosureEvalGlossy cl_eval, + ClosureEvalCommon cl_common, + ClosureLightData light, + inout ClosureOutputGlossy cl_out) +{ + float radiance = light_specular(light.data, cl_eval.ltc_mat, cl_in.N, cl_common.V, light.L); + radiance *= cl_eval.ltc_brdf_scale; + cl_out.radiance += light.data.l_color * (light.data.l_spec * light.contact_shadow * radiance); +} + +void closure_Glossy_planar_eval(ClosureInputGlossy cl_in, + ClosureEvalGlossy cl_eval, + ClosureEvalCommon cl_common, + ClosurePlanarData planar, + inout ClosureOutputGlossy cl_out) +{ +#ifndef STEP_RESOLVE /* SSR already evaluates planar reflections. */ + vec3 probe_radiance = probe_evaluate_planar( + planar.id, planar.data, cl_common.P, cl_in.N, cl_common.V, cl_in.roughness); + cl_out.radiance += planar.attenuation * probe_radiance; +#endif +} + +void closure_Glossy_cubemap_eval(ClosureInputGlossy cl_in, + ClosureEvalGlossy cl_eval, + ClosureEvalCommon cl_common, + ClosureCubemapData cube, + inout ClosureOutputGlossy cl_out) +{ + vec3 probe_radiance = probe_evaluate_cube( + cube.id, cl_common.P, cl_eval.probe_sampling_dir, cl_in.roughness); + cl_out.radiance += cube.attenuation * probe_radiance; +} + +void closure_Glossy_indirect_end(ClosureInputGlossy cl_in, + ClosureEvalGlossy cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputGlossy cl_out) +{ + /* If not enough light has been accumulated from probes, use the world specular cubemap + * to fill the remaining energy needed. */ + if (specToggle && cl_common.specular_accum > 0.0) { + vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, cl_in.roughness); + cl_out.radiance += cl_common.specular_accum * probe_radiance; } -# endif -# ifdef CLOSURE_GLOSSY - vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); -# endif + /* TODO(fclem) Apply occlusion. */ +} -# ifdef CLOSURE_CLEARCOAT - vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared); -# endif +void closure_Glossy_eval_end(ClosureInputGlossy cl_in, + ClosureEvalGlossy cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputGlossy cl_out) +{ +#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) + /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ + cl_out.radiance = vec3(0.0); + return; +#endif + + if (!specToggle) { + cl_out.radiance = vec3(0.0); + } +} -# ifdef CLOSURE_REFRACTION +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refraction Closure + * \{ */ + +struct ClosureInputRefraction { + vec3 N; /** Shading normal. */ + float roughness; /** Input roughness, not squared. */ + float ior; /** Index of refraction ratio. */ +}; + +#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0) + +struct ClosureEvalRefraction { + vec3 P; /** LTC matrix values. */ + vec3 ltc_brdf; /** LTC BRDF values. */ + vec3 probe_sampling_dir; /** Direction to sample probes from. */ + float probes_weight; /** Factor to apply to probe radiance. */ +}; + +/* Stubs. */ +#define ClosureOutputRefraction ClosureOutput +#define closure_Refraction_grid_eval(cl_in, cl_eval, cl_common, data, cl_out) + +ClosureEvalRefraction closure_Refraction_eval_init(inout ClosureInputRefraction cl_in, + ClosureEvalCommon cl_common, + out ClosureOutputRefraction cl_out) +{ + cl_in.N = safe_normalize(cl_in.N); + cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999); + cl_in.ior = max(cl_in.ior, 1e-5); + cl_out.radiance = vec3(0.0); + + ClosureEvalRefraction cl_eval; + vec3 cl_V; + float eval_ior; /* Refract the view vector using the depth heuristic. * Then later Refract a second time the already refracted * ray using the inverse ior. */ - float final_ior = (refractionDepth > 0.0) ? 1.0 / ior : ior; - vec3 refr_V = (refractionDepth > 0.0) ? -refract(-V, N, final_ior) : V; - vec3 refr_pos = (refractionDepth > 0.0) ? - line_plane_intersect( - worldPosition, refr_V, worldPosition - N * refractionDepth, N) : - worldPosition; - vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior); -# endif - -# ifdef CLOSURE_REFRACTION -/* ---------------------------- */ -/* Screen Space Refraction */ -/* ---------------------------- */ -# ifdef USE_REFRACTION - if (ssrefractToggle && roughness < ssrMaxRoughness + 0.2) { - /* Find approximated position of the 2nd refraction event. */ - vec3 refr_vpos = (refractionDepth > 0.0) ? transform_point(ViewMatrix, refr_pos) : - viewPosition; - vec4 trans = screen_space_refraction(refr_vpos, N, refr_V, final_ior, roughnessSquared, rand); - trans.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); - accumulate_light(trans.rgb, trans.a, refr_accum); + if (refractionDepth > 0.0) { + eval_ior = 1.0 / cl_in.ior; + cl_V = -refract(-cl_common.V, cl_in.N, eval_ior); + vec3 plane_pos = cl_common.P - cl_in.N * refractionDepth; + cl_eval.P = line_plane_intersect(cl_common.P, cl_V, plane_pos, cl_in.N); } -# endif - -# endif - - /* ---------------------------- */ - /* Specular probes */ - /* ---------------------------- */ -# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_REFRACTION) - -# if defined(CLOSURE_GLOSSY) && defined(CLOSURE_REFRACTION) -# define GLASS_ACCUM 1 -# define ACCUM min(refr_accum.a, spec_accum.a) -# elif defined(CLOSURE_REFRACTION) -# define GLASS_ACCUM 0 -# define ACCUM refr_accum.a -# else -# define GLASS_ACCUM 0 -# define ACCUM spec_accum.a -# endif - - /* Starts at 1 because 0 is world probe */ - for (int i = 1; ACCUM < 0.999 && i < prbNumRenderCube && i < MAX_PROBE; i++) { - float fade = probe_attenuation_cube(i, worldPosition); - - if (fade > 0.0) { - -# if GLASS_ACCUM - if (spec_accum.a < 0.999) { -# endif -# ifdef CLOSURE_GLOSSY - if (!(ssrToggle && ssr_id == outputSsrId)) { - vec3 spec = probe_evaluate_cube(i, worldPosition, spec_dir, roughness); - accumulate_light(spec, fade, spec_accum); - } -# endif - -# ifdef CLOSURE_CLEARCOAT - vec3 C_spec = probe_evaluate_cube(i, worldPosition, C_spec_dir, C_roughness); - accumulate_light(C_spec, fade, C_spec_accum); -# endif -# if GLASS_ACCUM - } -# endif - -# if GLASS_ACCUM - if (refr_accum.a < 0.999) { -# endif -# ifdef CLOSURE_REFRACTION - vec3 trans = probe_evaluate_cube(i, refr_pos, refr_dir, roughnessSquared); - accumulate_light(trans, fade, refr_accum); -# endif -# if GLASS_ACCUM - } -# endif - } + else { + eval_ior = cl_in.ior; + cl_V = cl_common.V; + cl_eval.P = cl_common.P; } -# undef GLASS_ACCUM -# undef ACCUM - -/* ---------------------------- */ -/* World Probe */ -/* ---------------------------- */ -# ifdef CLOSURE_GLOSSY - if (spec_accum.a < 0.999) { - if (!(ssrToggle && ssr_id == outputSsrId)) { - vec3 spec = probe_evaluate_world_spec(spec_dir, roughness); - accumulate_light(spec, 1.0, spec_accum); - } + cl_eval.probe_sampling_dir = refraction_dominant_dir(cl_in.N, cl_V, cl_in.roughness, eval_ior); + cl_eval.probes_weight = 1.0; -# ifdef CLOSURE_CLEARCOAT - vec3 C_spec = probe_evaluate_world_spec(C_spec_dir, C_roughness); - accumulate_light(C_spec, 1.0, C_spec_accum); -# endif +#ifdef USE_REFRACTION + if (ssrefractToggle && cl_in.roughness < ssrMaxRoughness + 0.2) { + /* Find approximated position of the 2nd refraction event. */ + vec3 vP = (refractionDepth > 0.0) ? transform_point(ViewMatrix, cl_eval.P) : cl_common.vP; + vec4 ssr_output = screen_space_refraction( + vP, cl_in.N, cl_V, eval_ior, sqr(cl_in.roughness), cl_common.rand); + ssr_output.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, cl_in.roughness); + cl_out.radiance += ssr_output.rgb * ssr_output.a; + cl_eval.probes_weight -= ssr_output.a; } -# endif +#endif + return cl_eval; +} -# ifdef CLOSURE_REFRACTION - if (refr_accum.a < 0.999) { - vec3 trans = probe_evaluate_world_spec(refr_dir, roughnessSquared); - accumulate_light(trans, 1.0, refr_accum); - } -# endif -# endif /* Specular probes */ - - /* ---------------------------- */ - /* Ambient Occlusion */ - /* ---------------------------- */ -# if defined(CLOSURE_GLOSSY) || defined(CLOSURE_DIFFUSE) - if (!use_contact_shadows) { - /* HACK: Fix for translucent BSDF. (see T65631) */ - N = -N; - } - vec3 bent_normal; - float final_ao = occlusion_compute(N, viewPosition, ao, rand, bent_normal); - if (!use_contact_shadows) { - N = -N; - /* Bypass bent normal. */ - bent_normal = N; - } -# endif - - /* ---------------------------- */ - /* Specular Output */ - /* ---------------------------- */ - float NV = dot(N, V); -# ifdef CLOSURE_GLOSSY - vec2 uv = lut_coords(NV, roughness); - vec2 brdf_lut = texture(utilTex, vec3(uv, 1.0)).rg; - - /* This factor is outputted to be used by SSR in order - * to match the intensity of the regular reflections. */ - ssr_spec = F_brdf(f0, f90, brdf_lut); - float spec_occlu = specular_occlusion(NV, final_ao, roughness); - - /* The SSR pass recompute the occlusion to not apply it to the SSR */ - if (ssrToggle && ssr_id == outputSsrId) { - spec_occlu = 1.0; - } +void closure_Refraction_light_eval(ClosureInputRefraction cl_in, + ClosureEvalRefraction cl_eval, + ClosureEvalCommon cl_common, + ClosureLightData light, + inout ClosureOutputRefraction cl_out) +{ + /* Not implemented yet. */ +} - out_spec += spec_accum.rgb * ssr_spec * spec_occlu; -# endif +void closure_Refraction_planar_eval(ClosureInputRefraction cl_in, + ClosureEvalRefraction cl_eval, + ClosureEvalCommon cl_common, + ClosurePlanarData planar, + inout ClosureOutputRefraction cl_out) +{ + /* Not implemented yet. */ +} -# ifdef CLOSURE_REFRACTION - float btdf = get_btdf_lut(NV, roughness, ior); +void closure_Refraction_cubemap_eval(ClosureInputRefraction cl_in, + ClosureEvalRefraction cl_eval, + ClosureEvalCommon cl_common, + ClosureCubemapData cube, + inout ClosureOutputRefraction cl_out) +{ + vec3 probe_radiance = probe_evaluate_cube( + cube.id, cl_eval.P, cl_eval.probe_sampling_dir, sqr(cl_in.roughness)); + cl_out.radiance += (cube.attenuation * cl_eval.probes_weight) * probe_radiance; +} - out_refr += refr_accum.rgb * btdf; +void closure_Refraction_indirect_end(ClosureInputRefraction cl_in, + ClosureEvalRefraction cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputRefraction cl_out) +{ + /* If not enough light has been accumulated from probes, use the world specular cubemap + * to fill the remaining energy needed. */ + if (specToggle && cl_common.specular_accum > 0.0) { + vec3 probe_radiance = probe_evaluate_world_spec(cl_eval.probe_sampling_dir, + sqr(cl_in.roughness)); + cl_out.radiance += (cl_common.specular_accum * cl_eval.probes_weight) * probe_radiance; + } +} - /* Global toggle for lightprobe baking. */ - out_refr *= float(specToggle); -# endif +void closure_Refraction_eval_end(ClosureInputRefraction cl_in, + ClosureEvalRefraction cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputRefraction cl_out) +{ +#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) + /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ + cl_out.radiance = vec3(0.0); + return; +#endif -# ifdef CLOSURE_CLEARCOAT - NV = dot(C_N, V); - vec2 C_uv = lut_coords(NV, C_roughness); - vec2 C_brdf_lut = texture(utilTex, vec3(C_uv, 1.0)).rg; - vec3 C_fresnel = F_brdf(vec3(0.04), vec3(1.0), C_brdf_lut) * - specular_occlusion(NV, final_ao, C_roughness); + if (!specToggle) { + cl_out.radiance = vec3(0.0); + } +} - out_spec += C_spec_accum.rgb * C_fresnel * C_intensity; -# endif +/** \} */ -# ifdef CLOSURE_GLOSSY - /* Global toggle for lightprobe baking. */ - out_spec *= float(specToggle); -# endif +/* -------------------------------------------------------------------- */ +/** \name Diffuse Closure + * \{ */ - /* ---------------------------------------------------------------- */ - /* ---------------- DIFFUSE ENVIRONMENT LIGHTING ------------------ */ - /* ---------------------------------------------------------------- */ +struct ClosureInputDiffuse { + vec3 N; /** Shading normal. */ + vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */ +}; - /* Accumulate light from all sources until accumulator is full. Then apply Occlusion and BRDF. */ -# ifdef CLOSURE_DIFFUSE - vec4 diff_accum = vec4(0.0); +#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0)) - /* ---------------------------- */ - /* Irradiance Grids */ - /* ---------------------------- */ - /* Start at 1 because 0 is world irradiance */ - for (int i = 1; i < MAX_GRID && i < prbNumRenderGrid && diff_accum.a < 0.999; i++) { - GridData gd = grids_data[i]; +struct ClosureEvalDiffuse { + vec3 bent_normal; /** Normal pointing in the least occluded direction. */ + float ambient_occlusion; /** Final occlusion factor. */ +}; - vec3 localpos; - float fade = probe_attenuation_grid(gd, grids_data[i].localmat, worldPosition, localpos); +/* Stubs. */ +#define ClosureOutputDiffuse ClosureOutput +#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) - if (fade > 0.0) { - vec3 diff = probe_evaluate_grid(gd, worldPosition, bent_normal, localpos); - accumulate_light(diff, fade, diff_accum); - } - } +ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in, + ClosureEvalCommon cl_common, + out ClosureOutputDiffuse cl_out) +{ + cl_in.N = safe_normalize(cl_in.N); + cl_out.radiance = vec3(0.0); + + ClosureEvalDiffuse cl_eval; + float user_ao = 1.0; /* TODO(fclem) wire the real one through ClosureEvalCommon. */ + cl_eval.ambient_occlusion = occlusion_compute( + cl_in.N, cl_common.vP, user_ao, cl_common.rand, cl_eval.bent_normal); + return cl_eval; +} - /* ---------------------------- */ - /* World Diffuse */ - /* ---------------------------- */ - if (diff_accum.a < 0.999 && prbNumRenderGrid > 0) { - vec3 diff = probe_evaluate_world_diff(bent_normal); - accumulate_light(diff, 1.0, diff_accum); +void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in, + ClosureEvalDiffuse cl_eval, + ClosureEvalCommon cl_common, + ClosureLightData light, + inout ClosureOutputDiffuse cl_out) +{ + float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L); + /* TODO(fclem) We could try to shadow lights that are shadowless with the ambient_occlusion + * factor here. */ + cl_out.radiance += light.data.l_color * (light.contact_shadow * radiance); +} + +void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in, + ClosureEvalDiffuse cl_eval, + ClosureEvalCommon cl_common, + ClosureGridData grid, + inout ClosureOutputDiffuse cl_out) +{ + vec3 probe_radiance = probe_evaluate_grid( + grid.data, cl_common.P, cl_eval.bent_normal, grid.local_pos); + cl_out.radiance += grid.attenuation * probe_radiance; +} + +void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in, + ClosureEvalDiffuse cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputDiffuse cl_out) +{ + /* If not enough light has been accumulated from probes, use the world specular cubemap + * to fill the remaining energy needed. */ + if (cl_common.diffuse_accum > 0.0) { + vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.bent_normal); + cl_out.radiance += cl_common.diffuse_accum * probe_radiance; } + /* Apply occlusion on radiance before the light loop. */ + cl_out.radiance *= gtao_multibounce(cl_eval.ambient_occlusion, cl_in.albedo); +} - out_diff += diff_accum.rgb * gtao_multibounce(final_ao, albedo); -# endif +void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in, + ClosureEvalDiffuse cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputDiffuse cl_out) +{ +#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) + /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ + cl_out.radiance = vec3(0.0); + return; #endif } -/* Cleanup for next configuration */ -#undef CLOSURE_NAME +/** \} */ -#ifdef CLOSURE_DIFFUSE -# undef CLOSURE_DIFFUSE -#endif +/* -------------------------------------------------------------------- */ +/** \name Translucent Closure + * \{ */ -#ifdef CLOSURE_GLOSSY -# undef CLOSURE_GLOSSY -#endif +struct ClosureInputTranslucent { + vec3 N; /** Shading normal. */ +}; -#ifdef CLOSURE_CLEARCOAT -# undef CLOSURE_CLEARCOAT -#endif +#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0)) -#ifdef CLOSURE_REFRACTION -# undef CLOSURE_REFRACTION -#endif +/* Stubs. */ +#define ClosureEvalTranslucent ClosureEvalDummy +#define ClosureOutputTranslucent ClosureOutput +#define closure_Translucent_planar_eval(cl_in, cl_eval, cl_common, data, cl_out) +#define closure_Translucent_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out) + +ClosureEvalTranslucent closure_Translucent_eval_init(inout ClosureInputTranslucent cl_in, + ClosureEvalCommon cl_common, + out ClosureOutputTranslucent cl_out) +{ + cl_in.N = safe_normalize(cl_in.N); + cl_out.radiance = vec3(0.0); + return CLOSURE_EVAL_DUMMY; +} + +void closure_Translucent_light_eval(ClosureInputTranslucent cl_in, + ClosureEvalTranslucent cl_eval, + ClosureEvalCommon cl_common, + ClosureLightData light, + inout ClosureOutputTranslucent cl_out) +{ + float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L); + cl_out.radiance += light.data.l_color * radiance; +} + +void closure_Translucent_grid_eval(ClosureInputTranslucent cl_in, + ClosureEvalTranslucent cl_eval, + ClosureEvalCommon cl_common, + ClosureGridData grid, + inout ClosureOutputTranslucent cl_out) +{ + vec3 probe_radiance = probe_evaluate_grid(grid.data, cl_common.P, cl_in.N, grid.local_pos); + cl_out.radiance += grid.attenuation * probe_radiance; +} + +void closure_Translucent_indirect_end(ClosureInputTranslucent cl_in, + ClosureEvalTranslucent cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputTranslucent cl_out) +{ + /* If not enough light has been accumulated from probes, use the world specular cubemap + * to fill the remaining energy needed. */ + if (cl_common.diffuse_accum > 0.0) { + vec3 probe_radiance = probe_evaluate_world_diff(cl_in.N); + cl_out.radiance += cl_common.diffuse_accum * probe_radiance; + } +} -#ifdef CLOSURE_SUBSURFACE -# undef CLOSURE_SUBSURFACE +void closure_Translucent_eval_end(ClosureInputTranslucent cl_in, + ClosureEvalTranslucent cl_eval, + ClosureEvalCommon cl_common, + inout ClosureOutputTranslucent cl_out) +{ +#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND) + /* This makes shader resources become unused and avoid issues with samplers. (see T59747) */ + cl_out.radiance = vec3(0.0); + return; #endif +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl index 95a585f0d9c..a6e346fe057 100644 --- a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl @@ -12,11 +12,20 @@ uniform sampler2DArray utilTex; #define LUT_SIZE 64 -#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) +#define LTC_MAT_LAYER 0 +#define LTC_BRDF_LAYER 1 +#define BRDF_LUT_LAYER 1 +#define NOISE_LAYER 2 +#define LTC_DISK_LAYER 3 /* UNUSED */ +/* Layers 4 to 20 are for BTDF Lut. */ + +#define texelfetch_noise_tex(coord) \ + texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, NOISE_LAYER), 0) /* Return texture coordinates to sample Surface LUT */ vec2 lut_coords(float cosTheta, float roughness) { + /* TODO(fclem) Ugly Acos here. Get rid ot this. Should use same mapping as lut_coords_ltc. */ float theta = acos(cosTheta); vec2 coords = vec2(roughness, theta / M_PI_2); @@ -32,6 +41,11 @@ vec2 lut_coords_ltc(float cosTheta, float roughness) return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; } +vec2 brdf_lut(float cosTheta, float roughness) +{ + return textureLod(utilTex, vec3(lut_coords(cosTheta, roughness), BRDF_LUT_LAYER), 0.0).rg; +} + float get_btdf_lut(float NV, float roughness, float ior) { const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index d50a4eaea3c..f420b369e79 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -2,6 +2,7 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lit_lib.glsl) #pragma BLENDER_REQUIRE(raytrace_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ssr_lib.glsl) @@ -170,7 +171,7 @@ void main() /* Importance sampling bias */ rand.x = mix(rand.x, 0.0, ssrBrdfBias); - vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition); + vec3 W = transform_point(ViewMatrixInverse, viewPosition); vec3 wN = transform_direction(ViewMatrixInverse, N); vec3 T, B; @@ -180,12 +181,12 @@ void main() for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { PlanarData pd = planars_data[i]; - float fade = probe_attenuation_planar(pd, worldPosition, wN, 0.0); + float fade = probe_attenuation_planar(pd, W, wN, 0.0); if (fade > 0.5) { /* Find view vector / reflection plane intersection. */ /* TODO optimize, use view space for all. */ - vec3 tracePosition = line_plane_intersect(worldPosition, cameraVec, pd.pl_plane_eq); + vec3 tracePosition = line_plane_intersect(W, cameraVec, pd.pl_plane_eq); tracePosition = transform_point(ViewMatrix, tracePosition); vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal); @@ -213,6 +214,8 @@ uniform sampler2D pdfBuffer; uniform int neighborOffset; +in vec4 uvcoordsvar; + const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0), ivec2(1, 1), ivec2(-2, 0), @@ -298,7 +301,7 @@ float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index) vec3 get_hit_vector(vec3 hit_pos, PlanarData pd, - vec3 worldPosition, + vec3 P, vec3 N, vec3 V, bool is_planar, @@ -309,7 +312,7 @@ vec3 get_hit_vector(vec3 hit_pos, if (is_planar) { /* Reflect back the hit position to have it in non-reflected world space */ - vec3 trace_pos = line_plane_intersect(worldPosition, V, pd.pl_plane_eq); + vec3 trace_pos = line_plane_intersect(P, V, pd.pl_plane_eq); hit_vec = hit_pos - trace_pos; hit_vec = reflect(hit_vec, pd.pl_normal); /* Modify here so mip texel alignment is correct. */ @@ -317,8 +320,8 @@ vec3 get_hit_vector(vec3 hit_pos, } else { /* Find hit position in previous frame. */ - hit_co = get_reprojected_reflection(hit_pos, worldPosition, N); - hit_vec = hit_pos - worldPosition; + hit_co = get_reprojected_reflection(hit_pos, P, N); + hit_vec = hit_pos - P; } mask = screen_border_mask(hit_co); @@ -339,7 +342,7 @@ vec4 get_ssr_samples(vec4 hit_pdf, ivec4 hit_data[2], PlanarData pd, float planar_index, - vec3 worldPosition, + vec3 P, vec3 N, vec3 V, float roughnessSquared, @@ -379,14 +382,10 @@ vec4 get_ssr_samples(vec4 hit_pdf, /* Get actual hit vector and hit coordinate (from last frame). */ vec4 mask = vec4(1.0); - hit_pos[0] = get_hit_vector( - hit_pos[0], pd, worldPosition, N, V, is_planar.x, hit_co[0].xy, mask.x); - hit_pos[1] = get_hit_vector( - hit_pos[1], pd, worldPosition, N, V, is_planar.y, hit_co[0].zw, mask.y); - hit_pos[2] = get_hit_vector( - hit_pos[2], pd, worldPosition, N, V, is_planar.z, hit_co[1].xy, mask.z); - hit_pos[3] = get_hit_vector( - hit_pos[3], pd, worldPosition, N, V, is_planar.w, hit_co[1].zw, mask.w); + hit_pos[0] = get_hit_vector(hit_pos[0], pd, P, N, V, is_planar.x, hit_co[0].xy, mask.x); + hit_pos[1] = get_hit_vector(hit_pos[1], pd, P, N, V, is_planar.y, hit_co[0].zw, mask.y); + hit_pos[2] = get_hit_vector(hit_pos[2], pd, P, N, V, is_planar.z, hit_co[1].xy, mask.z); + hit_pos[3] = get_hit_vector(hit_pos[3], pd, P, N, V, is_planar.w, hit_co[1].zw, mask.w); vec4 hit_dist; hit_dist.x = length(hit_pos[0]); @@ -476,47 +475,29 @@ vec4 get_ssr_samples(vec4 hit_pdf, return accum; } -void main() +void ssr_resolve(ClosureInputGlossy cl_in, + inout ClosureEvalCommon cl_common, + inout ClosureOutputGlossy cl_out) { - ivec2 fullres_texel = ivec2(gl_FragCoord.xy); # ifdef FULLRES - ivec2 halfres_texel = fullres_texel; + ivec2 texel = ivec2(gl_FragCoord.xy); # else - ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0); + ivec2 texel = ivec2(gl_FragCoord.xy / 2.0); # endif - vec2 uvs = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0)); - - float depth = textureLod(depthBuffer, uvs, 0.0).r; - - /* Early out */ - if (depth == 1.0) { - discard; - } - /* Using world space */ - vec3 viewPosition = get_view_space_from_depth(uvs, depth); /* Needed for viewCameraVec */ - vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition); - vec3 V = cameraVec; - vec3 vN = normal_decode(texelFetch(normalBuffer, fullres_texel, 0).rg, viewCameraVec); - vec3 N = transform_direction(ViewMatrixInverse, vN); - vec4 speccol_roughness = texelFetch(specroughBuffer, fullres_texel, 0).rgba; - - /* Early out */ - if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { - discard; - } - - float roughness = speccol_roughness.a; - float roughnessSquared = max(1e-3, roughness * roughness); + vec3 V = cl_common.V; + vec3 N = cl_in.N; + vec3 P = cl_common.P; - vec4 spec_accum = vec4(0.0); + float roughness = cl_in.roughness; + float roughnessSquared = max(1e-3, sqr(roughness)); /* Resolve SSR */ float cone_cos = cone_cosine(roughnessSquared); float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos; cone_tan *= mix(saturate(dot(N, -V) * 2.0), 1.0, roughness); /* Elongation fit */ - vec2 source_uvs = project_point(pastViewProjectionMatrix, worldPosition).xy * 0.5 + 0.5; + vec2 source_uvs = project_point(pastViewProjectionMatrix, P).xy * 0.5 + 0.5; vec4 ssr_accum = vec4(0.0); float weight_acc = 0.0; @@ -525,16 +506,16 @@ void main() /* TODO optimize with textureGather */ /* Doing these fetches early to hide latency. */ vec4 hit_pdf; - hit_pdf.x = texelFetch(pdfBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).r; - hit_pdf.y = texelFetch(pdfBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).r; - hit_pdf.z = texelFetch(pdfBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).r; - hit_pdf.w = texelFetch(pdfBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).r; + hit_pdf.x = texelFetch(pdfBuffer, texel + neighbors[0 + neighborOffset], 0).r; + hit_pdf.y = texelFetch(pdfBuffer, texel + neighbors[1 + neighborOffset], 0).r; + hit_pdf.z = texelFetch(pdfBuffer, texel + neighbors[2 + neighborOffset], 0).r; + hit_pdf.w = texelFetch(pdfBuffer, texel + neighbors[3 + neighborOffset], 0).r; ivec4 hit_data[2]; - hit_data[0].xy = texelFetch(hitBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).rg; - hit_data[0].zw = texelFetch(hitBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).rg; - hit_data[1].xy = texelFetch(hitBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).rg; - hit_data[1].zw = texelFetch(hitBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).rg; + hit_data[0].xy = texelFetch(hitBuffer, texel + neighbors[0 + neighborOffset], 0).rg; + hit_data[0].zw = texelFetch(hitBuffer, texel + neighbors[1 + neighborOffset], 0).rg; + hit_data[1].xy = texelFetch(hitBuffer, texel + neighbors[2 + neighborOffset], 0).rg; + hit_data[1].zw = texelFetch(hitBuffer, texel + neighbors[3 + neighborOffset], 0).rg; /* Find Planar Reflections affecting this pixel */ PlanarData pd; @@ -542,7 +523,7 @@ void main() for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { pd = planars_data[i]; - float fade = probe_attenuation_planar(pd, worldPosition, N, 0.0); + float fade = probe_attenuation_planar(pd, P, N, 0.0); if (fade > 0.5) { planar_index = float(i); @@ -554,7 +535,7 @@ void main() hit_data, pd, planar_index, - worldPosition, + P, N, V, roughnessSquared, @@ -568,15 +549,49 @@ void main() ssr_accum /= weight_acc; /* fade between 0.5 and 1.0 roughness */ ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); - accumulate_light(ssr_accum.rgb, ssr_accum.a, spec_accum); + + cl_out.radiance += ssr_accum.rgb * ssr_accum.a; + cl_common.specular_accum -= ssr_accum.a; } +} + +CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy) + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + float depth = texelFetch(depthBuffer, texel, 0).r; - /* If SSR contribution is not 1.0, blend with cubemaps */ - if (spec_accum.a < 1.0) { - fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum); + if (depth == 1.0) { + discard; } - fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0); + vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba; + vec3 brdf = speccol_roughness.rgb; + float roughness = speccol_roughness.a; + + if (max_v3(brdf) <= 0.0) { + discard; + } + + viewPosition = get_view_space_from_depth(uvcoordsvar.xy, depth); + worldPosition = transform_point(ViewMatrixInverse, viewPosition); + + vec2 normal_encoded = texelFetch(normalBuffer, texel, 0).rg; + viewNormal = normal_decode(normal_encoded, viewCameraVec); + worldNormal = transform_direction(ViewMatrixInverse, viewNormal); + + CLOSURE_VARS_DECLARE_1(Glossy); + + in_Glossy_0.N = worldNormal; + in_Glossy_0.roughness = roughness; + + /* Do a full deferred evaluation of the glossy BSDF. The only difference is that we inject the + * SSR resolve before the cubemap iter. BRDF term is already computed during main pass and is + * passed as specular color. */ + CLOSURE_EVAL_FUNCTION_1(ssr_resolve, Glossy); + + fragColor = vec4(out_Glossy_0.radiance * brdf, 1.0); } #endif diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl index a2e25b83532..3d1ad95060a 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl @@ -137,9 +137,9 @@ float probe_attenuation_planar(PlanarData pd, vec3 W, vec3 N, float roughness) return fac; } -float probe_attenuation_grid(GridData gd, mat4 localmat, vec3 W, out vec3 localpos) +float probe_attenuation_grid(GridData gd, vec3 W, out vec3 localpos) { - localpos = transform_point(localmat, W); + localpos = transform_point(gd.localmat, W); vec3 pos_to_edge = max(vec3(0.0), abs(localpos) - 1.0); float fade = length(pos_to_edge); return saturate(-fade * gd.g_atten_scale + gd.g_atten_bias); @@ -183,8 +183,7 @@ vec3 probe_evaluate_world_spec(vec3 R, float roughness) return textureLod_cubemapArray(probeCubes, vec4(R, 0.0), roughness * prbLodCubeMax).rgb; } -vec3 probe_evaluate_planar( - float id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness, inout float fade) +vec3 probe_evaluate_planar(int id, PlanarData pd, vec3 W, vec3 N, vec3 V, float roughness) { /* Find view vector / reflection plane intersection. */ vec3 point_on_plane = line_plane_intersect(W, V, pd.pl_plane_eq); diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 949e4d8f04f..b0845057a43 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -252,32 +252,29 @@ float light_attenuation(LightData ld, vec4 l_vector) return vis; } -float light_shadowing(LightData ld, - vec3 W, -#ifndef VOLUMETRICS - vec3 viewPosition, - float tracing_depth, - vec3 true_normal, - float rand_x, - const bool use_contact_shadows, -#endif - float vis) +float light_shadowing(LightData ld, vec3 W, float vis) { #if !defined(VOLUMETRICS) || defined(VOLUME_SHADOW) - /* shadowing */ if (ld.l_shadowid >= 0.0 && vis > 0.001) { - if (ld.l_type == SUN) { vis *= sample_cascade_shadow(int(ld.l_shadowid), W); } else { vis *= sample_cube_shadow(int(ld.l_shadowid), W); } + } +#endif + return vis; +} -# ifndef VOLUMETRICS +#ifndef VOLUMETRICS +float light_contact_shadows( + LightData ld, vec3 P, vec3 vP, float tracing_depth, vec3 vNg, float rand_x, float vis) +{ + if (ld.l_shadowid >= 0.0 && vis > 0.001) { ShadowData sd = shadows_data[int(ld.l_shadowid)]; /* Only compute if not already in shadow. */ - if (use_contact_shadows && sd.sh_contact_dist > 0.0 && vis > 1e-8) { + if (sd.sh_contact_dist > 0.0) { /* Contact Shadows. */ vec3 ray_ori, ray_dir; float trace_distance; @@ -287,54 +284,34 @@ float light_shadowing(LightData ld, ray_dir = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * trace_distance; } else { - ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - W; + ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P; float len = length(ray_dir); trace_distance = min(sd.sh_contact_dist, len); ray_dir *= trace_distance / len; } ray_dir = transform_direction(ViewMatrix, ray_dir); - ray_ori = vec3(viewPosition.xy, tracing_depth) + true_normal * sd.sh_contact_offset; + ray_ori = vec3(vP.xy, tracing_depth) + vNg * sd.sh_contact_offset; vec3 hit_pos = raycast( -1, ray_ori, ray_dir, sd.sh_contact_thickness, rand_x, 0.1, 0.001, false); if (hit_pos.z > 0.0) { hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z); - float hit_dist = distance(viewPosition, hit_pos); + float hit_dist = distance(vP, hit_pos); float dist_ratio = hit_dist / trace_distance; - return vis * saturate(dist_ratio * 3.0 - 2.0); + return saturate(dist_ratio * 3.0 - 2.0); } } -# endif /* VOLUMETRICS */ } -#endif - - return vis; + return 1.0; } +#endif /* VOLUMETRICS */ -float light_visibility(LightData ld, - vec3 W, -#ifndef VOLUMETRICS - vec3 viewPosition, - float tracing_depth, - vec3 true_normal, - float rand_x, - const bool use_contact_shadows, -#endif - vec4 l_vector) +float light_visibility(LightData ld, vec3 W, vec4 l_vector) { float l_atten = light_attenuation(ld, l_vector); - return light_shadowing(ld, - W, -#ifndef VOLUMETRICS - viewPosition, - tracing_depth, - true_normal, - rand_x, - use_contact_shadows, -#endif - l_atten); + return light_shadowing(ld, W, l_atten); } float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl index e0b9d4a60db..19eecdb5b79 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl @@ -39,7 +39,7 @@ void main() vec3 viewPosition = get_view_space_from_depth(uvs, depth); vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition); - vec3 true_normal = normalize(cross(dFdx(viewPosition), dFdy(viewPosition))); + vec3 true_normal = safe_normalize(cross(dFdx(viewPosition), dFdy(viewPosition))); for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) { LightData ld = lights_data[i]; @@ -48,8 +48,10 @@ void main() l_vector.xyz = ld.l_position - worldPosition; l_vector.w = length(l_vector.xyz); - float l_vis = light_shadowing( - ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, true, 1.0); + float l_vis = light_shadowing(ld, worldPosition, 1.0); + + l_vis *= light_contact_shadows( + ld, worldPosition, viewPosition, tracing_depth, true_normal, rand.x, 1.0); accum_light += l_vis; } diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index b93a3a23eff..e80dc1761f0 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -13,7 +13,15 @@ uniform float refractionDepth; vec3 worldNormal; \ vec3 viewNormal; -#ifdef GPU_GEOMETRY_SHADER +#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE) +/* SSR will set these global variables itself. + * Also make false positive compiler warnings disapear by setting values. */ +vec3 worldPosition = vec3(0); +vec3 viewPosition = vec3(0); +vec3 worldNormal = vec3(0); +vec3 viewNormal = vec3(0); + +#elif defined(GPU_GEOMETRY_SHADER) in ShaderStageInterface{SURFACE_INTERFACE} dataIn[]; out ShaderStageInterface{SURFACE_INTERFACE} dataOut; @@ -24,7 +32,7 @@ out ShaderStageInterface{SURFACE_INTERFACE} dataOut; dataOut.worldNormal = dataIn[vert].worldNormal; \ dataOut.viewNormal = dataIn[vert].viewNormal; -#else +#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/ IN_OUT ShaderStageInterface{SURFACE_INTERFACE}; diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index a82e0b5a5e9..84d93cc3965 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -72,6 +72,9 @@ float sum(vec4 v) { return dot(vec4(1.0), v); } float avg(vec2 v) { return dot(vec2(1.0 / 2.0), v); } float avg(vec3 v) { return dot(vec3(1.0 / 3.0), v); } float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); } + +float sqr(float v) { return v * v; } + /* clang-format on */ #define saturate(a) clamp(a, 0.0, 1.0) @@ -93,6 +96,15 @@ float len_squared(vec3 a) return dot(a, a); } +vec3 safe_normalize(vec3 v) +{ + float len = length(v); + if (isnan(len) || len == 0.0) { + return vec3(1.0, 0.0, 0.0); + } + return v / len; +} + /** \} */ /* ---------------------------------------------------------------------- */ |