From 1c22b551d0c13ad90bdc6145e6fa7db763038523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 3 Mar 2021 13:14:24 +0100 Subject: EEVEE: Add ensure_valid_reflection to glossy closures This is ported from Cycles and fixes issues with bump/normal mapping giving weird reflections/lighting. Fixes T81070 Specular light should be limited to normal pointing toward the camera Fixes T78501 Normal mapping making specular artifact --- .../engines/eevee/shaders/bsdf_common_lib.glsl | 54 ++++++++++++++++++++++ .../eevee/shaders/closure_eval_glossy_lib.glsl | 5 ++ 2 files changed, 59 insertions(+) (limited to 'source/blender/draw') 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 bc2895ef3df..5f0fde134d1 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -135,6 +135,60 @@ void accumulate_light(vec3 light, float fac, inout vec4 accum) accum += vec4(light, 1.0) * min(fac, (1.0 - accum.a)); } +/* Same thing as Cycles without the comments to make it shorter. */ +vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N) +{ + vec3 R = -reflect(I, N); + + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9 * dot(Ng, I), 0.025); + if (dot(Ng, R) >= threshold) { + return N; + } + + float NdotNg = dot(N, Ng); + vec3 X = normalize(N - NdotNg * Ng); + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = sqrt(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + float fac = 0.5 / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5)); + bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5)); + + vec2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2)); + vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2)); + + float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5); + valid2 = (R2 >= 1e-5); + if (valid1 && valid2) { + N_new = (R1 < R2) ? N1 : N2; + } + else { + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2)); + } + else { + return Ng; + } + return N_new.x * X + N_new.y * Ng; +} + /* ----------- Cone angle Approximation --------- */ /* Return a fitted cone angle given the input roughness */ diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl index 5c10a7f451f..00d265a48b0 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl @@ -3,6 +3,7 @@ #pragma BLENDER_REQUIRE(lights_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) #pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) struct ClosureInputGlossy { vec3 N; /** Shading normal. */ @@ -39,6 +40,10 @@ ClosureEvalGlossy closure_Glossy_eval_init(inout ClosureInputGlossy cl_in, cl_in.roughness = clamp(cl_in.roughness, 1e-8, 0.9999); cl_out.radiance = vec3(0.0); +#ifndef STEP_RESOLVE /* SSR */ + cl_in.N = ensure_valid_reflection(cl_common.Ng, cl_common.V, cl_in.N); +#endif + float NV = dot(cl_in.N, cl_common.V); vec2 lut_uv = lut_coords(NV, cl_in.roughness); -- cgit v1.2.3