diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 6f226e8cf87..2ab47c2575d 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -265,8 +265,8 @@ shader_node_categories = [ NodeItem("ShaderNodeBsdfPrincipled", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfGlossy", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfTransparent", poll=object_cycles_shader_nodes_poll), - NodeItem("ShaderNodeBsdfRefraction", poll=object_cycles_shader_nodes_poll), - NodeItem("ShaderNodeBsdfGlass", poll=object_cycles_shader_nodes_poll), + NodeItem("ShaderNodeBsdfRefraction", poll=object_eevee_cycles_shader_nodes_poll), + NodeItem("ShaderNodeBsdfGlass", poll=object_eevee_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfTranslucent", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfAnisotropic", poll=object_cycles_shader_nodes_poll), NodeItem("ShaderNodeBsdfVelvet", poll=object_cycles_shader_nodes_poll), 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 61ccfe665fc..f5ddbd0679c 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -294,6 +294,69 @@ float specular_occlusion(float NV, float AO, float roughness) return saturate(pow(NV + AO, roughness) - 1.0 + AO); } +/* --- Refraction utils --- */ + +float ior_from_f0(float f0) +{ + float f = sqrt(f0); + return (-f - 1.0) / (f - 1.0); +} + +float f0_from_ior(float eta) +{ + float A = (eta - 1.0) / (eta + 1.0); + return A * A; +} + +vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) +{ + /* TODO: This a bad approximation. Better approximation should fit + * the refracted vector and roughness into the best prefiltered reflection + * lobe. */ + /* Correct the IOR for ior < 1.0 to not see the abrupt delimitation or the TIR */ + ior = (ior < 1.0) ? mix(ior, 1.0, roughness) : ior; + float eta = 1.0 / ior; + + float NV = dot(N, -V); + + /* Custom Refraction. */ + float k = 1.0 - eta * eta * (1.0 - NV * NV); + k = max(0.0, k); /* Only this changes. */ + vec3 R = eta * -V - (eta * NV + sqrt(k)) * N; + + return R; +} + +float get_btdf_lut(sampler2DArray btdf_lut_tex, float NV, float roughness, float ior) +{ + const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; + + vec3 coords; + /* Try to compensate for the low resolution and interpolation error. */ + coords.x = (ior > 1.0) + ? (0.9 + lut_scale_bias_texel_size.z) + (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) + : (0.9 + lut_scale_bias_texel_size.z) * ior * ior; + coords.y = 1.0 - NV; + coords.xy *= lut_scale_bias_texel_size.x; + coords.xy += lut_scale_bias_texel_size.y; + + const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */ + const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */ + + float mip = roughness * lut_lvl_scale; + float mip_floor = floor(mip); + + coords.z = lut_lvl_ofs + mip_floor + 1.0; + float btdf_high = textureLod(btdf_lut_tex, coords, 0.0).r; + + coords.z -= 1.0; + float btdf_low = textureLod(btdf_lut_tex, coords, 0.0).r; + + float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z); + + return btdf; +} + /* ---- Encode / Decode Normal buffer data ---- */ /* From http://aras-p.info/texts/CompactNormalStorage.html * Using Method #4: Spheremap Transform */ @@ -314,6 +377,32 @@ vec3 normal_decode(vec2 enc, vec3 view) return n; } +/* Fresnel monochromatic, perfect mirror */ +float F_eta(float eta, float cos_theta) +{ + /* compute fresnel reflectance without explicitly computing + * the refracted direction */ + float c = abs(cos_theta); + float g = eta * eta - 1.0 + c * c; + float result; + + if (g > 0.0) { + g = sqrt(g); + vec2 g_c = vec2(g) + vec2(c, -c); + float A = g_c.y / g_c.x; + A *= A; + g_c *= c; + float B = (g_c.y - 1.0) / (g_c.x + 1.0); + B *= B; + result = 0.5 * A * (1.0 + B); + } + else { + result = 1.0; /* TIR (no refracted component) */ + } + + return result; +} + /* Fresnel */ vec3 F_schlick(vec3 f0, float cos_theta) { diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 71c327940ae..9e5b0472513 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -544,3 +544,192 @@ vec3 eevee_surface_glossy_lit(vec3 N, vec3 f0, float roughness, float ao, int ss return out_light; } + +/* ----------- Transmission ----------- */ + +vec3 eevee_surface_refraction(vec3 N, vec3 f0, float roughness, float ior, int ssr_id, out vec3 ssr_spec) +{ + /* Zero length vectors cause issues, see: T51979. */ +#if 0 + N = normalize(N); +#else + { + float len = length(N); + if (isnan(len)) { + return vec3(0.0); + } + N /= len; + } +#endif + vec3 V = cameraVec; + ior = (gl_FrontFacing) ? ior : 1.0 / ior; + + roughness = clamp(roughness, 1e-8, 0.9999); + float roughnessSquared = roughness * roughness; + + /* ---------------- SCENE LAMPS LIGHTING ----------------- */ + + /* No support for now. Supporting LTCs mean having a 3D LUT. + * We could support point lights easily though. */ + + /* ---------------- SPECULAR ENVIRONMENT LIGHTING ----------------- */ + + /* Accumulate light from all sources until accumulator is full. Then apply Occlusion and BRDF. */ + vec4 trans_accum = vec4(0.0); + + /* Specular probes */ + vec3 spec_dir = get_specular_refraction_dominant_dir(N, V, roughness, ior); + + /* Starts at 1 because 0 is world probe */ + for (int i = 1; i < MAX_PROBE && i < probe_count && trans_accum.a < 0.999; ++i) { + CubeData cd = probes_data[i]; + + float fade = probe_attenuation_cube(cd, worldPosition); + + if (fade > 0.0) { + vec3 spec = probe_evaluate_cube(float(i), cd, worldPosition, spec_dir, roughnessSquared); + accumulate_light(spec, fade, trans_accum); + } + } + + /* World Specular */ + if (trans_accum.a < 0.999) { + vec3 spec = probe_evaluate_world_spec(spec_dir, roughnessSquared); + accumulate_light(spec, 1.0, trans_accum); + } + + float btdf = get_btdf_lut(utilTex, dot(N, V), roughness, ior); + + return trans_accum.rgb * btdf; +} + +vec3 eevee_surface_glass(vec3 N, vec3 transmission_col, float roughness, float ior, int ssr_id, out vec3 ssr_spec) +{ + /* Zero length vectors cause issues, see: T51979. */ +#if 0 + N = normalize(N); +#else + { + float len = length(N); + if (isnan(len)) { + return vec3(0.0); + } + N /= len; + } +#endif + vec3 V = cameraVec; + ior = (gl_FrontFacing) ? ior : 1.0 / ior; + + if (!specToggle) return vec3(0.0); + + roughness = clamp(roughness, 1e-8, 0.9999); + float roughnessSquared = roughness * roughness; + + /* ---------------- SCENE LAMPS LIGHTING ----------------- */ + +#ifdef HAIR_SHADER + vec3 norm_view = cross(V, N); + norm_view = normalize(cross(norm_view, N)); /* Normal facing view */ +#endif + + vec3 spec = vec3(0.0); + for (int i = 0; i < MAX_LIGHT && i < light_count; ++i) { + LightData ld = lights_data[i]; + + 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); + + vec3 l_color_vis = ld.l_color * light_visibility(ld, worldPosition, l_vector); + +#ifdef HAIR_SHADER + vec3 norm_lamp, view_vec; + float occlu_trans, occlu; + light_hair_common(ld, N, V, l_vector, norm_view, occlu_trans, occlu, norm_lamp, view_vec); + + spec += l_color_vis * light_specular(ld, N, view_vec, l_vector, roughnessSquared, vec3(1.0)) * occlu; +#else + spec += l_color_vis * light_specular(ld, N, V, l_vector, roughnessSquared, vec3(1.0)); +#endif + } + + /* Accumulate outgoing radiance */ + vec3 out_light = spec; + +#ifdef HAIR_SHADER + N = -norm_view; +#endif + + + /* ---------------- SPECULAR ENVIRONMENT LIGHTING ----------------- */ + + /* Accumulate light from all sources until accumulator is full. Then apply Occlusion and BRDF. */ + vec4 spec_accum = vec4(0.0); + + /* Planar Reflections */ + if (!(ssrToggle && ssr_id == outputSsrId)) { + for (int i = 0; i < MAX_PLANAR && i < planar_count && spec_accum.a < 0.999 && roughness < 0.1; ++i) { + PlanarData pd = planars_data[i]; + + float fade = probe_attenuation_planar(pd, worldPosition, N, roughness); + + if (fade > 0.0) { + vec3 spec = probe_evaluate_planar(float(i), pd, worldPosition, N, V, roughness, fade); + accumulate_light(spec, fade, spec_accum); + } + } + } + + /* Specular probes */ + vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared); + vec3 refr_dir = get_specular_refraction_dominant_dir(N, V, roughness, ior); + vec4 trans_accum = vec4(0.0); + + /* Starts at 1 because 0 is world probe */ + for (int i = 1; i < MAX_PROBE && i < probe_count && spec_accum.a < 0.999 && trans_accum.a < 0.999; ++i) { + CubeData cd = probes_data[i]; + + float fade = probe_attenuation_cube(cd, worldPosition); + + if (fade > 0.0) { + if (!(ssrToggle && ssr_id == outputSsrId)) { + vec3 spec = probe_evaluate_cube(float(i), cd, worldPosition, spec_dir, roughness); + accumulate_light(spec, fade, spec_accum); + + spec = probe_evaluate_cube(float(i), cd, worldPosition, refr_dir, roughnessSquared); + accumulate_light(spec, fade, trans_accum); + } + } + } + + /* World Specular */ + 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); + + spec = probe_evaluate_world_spec(refr_dir, roughnessSquared); + accumulate_light(spec, 1.0, trans_accum); + } + } + + /* Ambient Occlusion */ + /* TODO : when AO will be cheaper */ + float final_ao = 1.0; + + float NV = dot(N, V); + /* Get Brdf intensity */ + vec2 uv = lut_coords(NV, roughness); + vec2 brdf_lut = texture(utilTex, vec3(uv, 1.0)).rg; + + float fresnel = F_eta(ior, NV); + + ssr_spec = vec3(fresnel) * F_ibl(vec3(1.0), brdf_lut) * specular_occlusion(NV, final_ao, roughness); + out_light += spec_accum.rgb * ssr_spec; + + float btdf = get_btdf_lut(utilTex, NV, roughness, ior); + + out_light += vec3(1.0 - fresnel) * transmission_col * trans_accum.rgb * btdf; + + return out_light; +} diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 416907c8453..a4e0f85b132 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2722,7 +2722,15 @@ void node_bsdf_anisotropic( void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, out Closure result) { +#ifdef EEVEE_ENGINE + vec3 ssr_spec; + roughness = sqrt(roughness); + vec3 L = eevee_surface_glass(N, vec3(1.0), roughness, ior, int(-2), ssr_spec); + vec3 vN = normalize(mat3(ViewMatrix) * N); + result = Closure(L * color.rgb, 1.0, vec4(ssr_spec * color.rgb, roughness), normal_encode(vN, viewCameraVec), int(-2)); +#else node_bsdf_diffuse(color, 0.0, N, result); +#endif } void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result) @@ -2881,8 +2889,9 @@ void node_bsdf_principled_clearcoat(vec4 base_color, float subsurface, vec3 subs } result = Closure(surface_color.rgb / surface_color.a, 1.0); #else - + vec3 L_trans = (transmission <= 0.0) ? vec3(0.0) : eevee_surface_glass(N, base_color.rgb, roughness, ior, int(-2), ssr_spec); vec3 L = eevee_surface_clearcoat_lit(N, diffuse, f0, roughness, CN, clearcoat, clearcoat_roughness, 1.0, int(ssr_id), ssr_spec); + L = mix(L, L_trans, transmission); vec3 vN = normalize(mat3(ViewMatrix) * N); result = Closure(L, 1.0, vec4(ssr_spec, roughness), normal_encode(vN, viewCameraVec), int(ssr_id)); #endif @@ -2896,7 +2905,7 @@ void node_bsdf_principled_clearcoat(vec4 base_color, float subsurface, vec3 subs void node_bsdf_translucent(vec4 color, vec3 N, out Closure result) { - node_bsdf_diffuse(color, 0.0, N, result); + node_bsdf_diffuse(color, 0.0, -N, result); } void node_bsdf_transparent(vec4 color, out Closure result) @@ -2918,6 +2927,19 @@ void node_subsurface_scattering( node_bsdf_diffuse(color, 0.0, N, result); } +void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result) +{ +#ifdef EEVEE_ENGINE + vec3 ssr_spec; + roughness = sqrt(roughness); + vec3 L = eevee_surface_refraction(N, vec3(1.0), roughness, ior, int(-2), ssr_spec); + vec3 vN = normalize(mat3(ViewMatrix) * N); + result = Closure(L * color.rgb, 1.0, vec4(ssr_spec * color.rgb, roughness), normal_encode(vN, viewCameraVec), int(-2)); +#else + node_bsdf_diffuse(color, 0.0, N, result); +#endif /* EEVEE_ENGINE */ +} + /* Unsupported for now */ #ifndef EEVEE_ENGINE void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv, vec3 tangent, out Closure result) @@ -2925,11 +2947,6 @@ void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv result = Closure(color.rgb, color.a); } -void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result) -{ - node_bsdf_diffuse(color, 0.0, N, result); -} - void node_ambient_occlusion(vec4 color, out Closure result) { result = Closure(color.rgb, color.a);