Changeset View
Changeset View
Standalone View
Standalone View
source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl
| #pragma BLENDER_REQUIRE(common_math_lib.glsl) | #pragma BLENDER_REQUIRE(common_math_lib.glsl) | ||||
| #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) | #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) | ||||
| #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) | #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) | ||||
| #pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl) | |||||
| #pragma BLENDER_REQUIRE(closure_eval_lib.glsl) | |||||
| #pragma BLENDER_REQUIRE(raytrace_lib.glsl) | #pragma BLENDER_REQUIRE(raytrace_lib.glsl) | ||||
| #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) | #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) | ||||
| #pragma BLENDER_REQUIRE(ssr_lib.glsl) | #pragma BLENDER_REQUIRE(ssr_lib.glsl) | ||||
| /* Based on Stochastic Screen Space Reflections | /* Based on Stochastic Screen Space Reflections | ||||
| * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */ | * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */ | ||||
| #define MAX_MIP 9.0 | #define MAX_MIP 9.0 | ||||
| ▲ Show 20 Lines • Show All 152 Lines • ▼ Show 20 Lines | # endif | ||||
| /* Gives *perfect* reflection for very small roughness */ | /* Gives *perfect* reflection for very small roughness */ | ||||
| if (roughness < 0.04) { | if (roughness < 0.04) { | ||||
| rand.xzw *= 0.0; | rand.xzw *= 0.0; | ||||
| } | } | ||||
| /* Importance sampling bias */ | /* Importance sampling bias */ | ||||
| rand.x = mix(rand.x, 0.0, ssrBrdfBias); | 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 wN = transform_direction(ViewMatrixInverse, N); | ||||
| vec3 T, B; | vec3 T, B; | ||||
| make_orthonormal_basis(N, T, B); /* Generate tangent space */ | make_orthonormal_basis(N, T, B); /* Generate tangent space */ | ||||
| /* Planar Reflections */ | /* Planar Reflections */ | ||||
| for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { | for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { | ||||
| PlanarData pd = planars_data[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) { | if (fade > 0.5) { | ||||
| /* Find view vector / reflection plane intersection. */ | /* Find view vector / reflection plane intersection. */ | ||||
| /* TODO optimize, use view space for all. */ | /* 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); | tracePosition = transform_point(ViewMatrix, tracePosition); | ||||
| vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal); | vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal); | ||||
| do_planar_ssr(i, V, N, T, B, planeNormal, tracePosition, a2, rand); | do_planar_ssr(i, V, N, T, B, planeNormal, tracePosition, a2, rand); | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| Show All 11 Lines | |||||
| uniform sampler2D normalBuffer; | uniform sampler2D normalBuffer; | ||||
| uniform sampler2D specroughBuffer; | uniform sampler2D specroughBuffer; | ||||
| uniform isampler2D hitBuffer; | uniform isampler2D hitBuffer; | ||||
| uniform sampler2D pdfBuffer; | uniform sampler2D pdfBuffer; | ||||
| uniform int neighborOffset; | uniform int neighborOffset; | ||||
| in vec4 uvcoordsvar; | |||||
| const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0), | const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0), | ||||
| ivec2(1, 1), | ivec2(1, 1), | ||||
| ivec2(-2, 0), | ivec2(-2, 0), | ||||
| ivec2(0, -2), | ivec2(0, -2), | ||||
| ivec2(0, 0), | ivec2(0, 0), | ||||
| ivec2(1, -1), | ivec2(1, -1), | ||||
| ivec2(-2, 0), | ivec2(-2, 0), | ||||
| ivec2(0, 2), | ivec2(0, 2), | ||||
| ▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index) | ||||
| } | } | ||||
| else { | else { | ||||
| return textureLod(depthBuffer, hit_co, 0.0).r; | return textureLod(depthBuffer, hit_co, 0.0).r; | ||||
| } | } | ||||
| } | } | ||||
| vec3 get_hit_vector(vec3 hit_pos, | vec3 get_hit_vector(vec3 hit_pos, | ||||
| PlanarData pd, | PlanarData pd, | ||||
| vec3 worldPosition, | vec3 P, | ||||
| vec3 N, | vec3 N, | ||||
| vec3 V, | vec3 V, | ||||
| bool is_planar, | bool is_planar, | ||||
| inout vec2 hit_co, | inout vec2 hit_co, | ||||
| inout float mask) | inout float mask) | ||||
| { | { | ||||
| vec3 hit_vec; | vec3 hit_vec; | ||||
| if (is_planar) { | if (is_planar) { | ||||
| /* Reflect back the hit position to have it in non-reflected world space */ | /* 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 = hit_pos - trace_pos; | ||||
| hit_vec = reflect(hit_vec, pd.pl_normal); | hit_vec = reflect(hit_vec, pd.pl_normal); | ||||
| /* Modify here so mip texel alignment is correct. */ | /* Modify here so mip texel alignment is correct. */ | ||||
| hit_co.x = 1.0 - hit_co.x; | hit_co.x = 1.0 - hit_co.x; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Find hit position in previous frame. */ | /* Find hit position in previous frame. */ | ||||
| hit_co = get_reprojected_reflection(hit_pos, worldPosition, N); | hit_co = get_reprojected_reflection(hit_pos, P, N); | ||||
| hit_vec = hit_pos - worldPosition; | hit_vec = hit_pos - P; | ||||
| } | } | ||||
| mask = screen_border_mask(hit_co); | mask = screen_border_mask(hit_co); | ||||
| return hit_vec; | return hit_vec; | ||||
| } | } | ||||
| vec3 get_scene_color(vec2 ref_uvs, float mip, float planar_index, bool is_planar) | vec3 get_scene_color(vec2 ref_uvs, float mip, float planar_index, bool is_planar) | ||||
| { | { | ||||
| if (is_planar) { | if (is_planar) { | ||||
| return textureLod(probePlanars, vec3(ref_uvs, planar_index), min(mip, prbLodPlanarMax)).rgb; | return textureLod(probePlanars, vec3(ref_uvs, planar_index), min(mip, prbLodPlanarMax)).rgb; | ||||
| } | } | ||||
| else { | else { | ||||
| return textureLod(prevColorBuffer, ref_uvs, mip).rgb; | return textureLod(prevColorBuffer, ref_uvs, mip).rgb; | ||||
| } | } | ||||
| } | } | ||||
| vec4 get_ssr_samples(vec4 hit_pdf, | vec4 get_ssr_samples(vec4 hit_pdf, | ||||
| ivec4 hit_data[2], | ivec4 hit_data[2], | ||||
| PlanarData pd, | PlanarData pd, | ||||
| float planar_index, | float planar_index, | ||||
| vec3 worldPosition, | vec3 P, | ||||
| vec3 N, | vec3 N, | ||||
| vec3 V, | vec3 V, | ||||
| float roughnessSquared, | float roughnessSquared, | ||||
| float cone_tan, | float cone_tan, | ||||
| vec2 source_uvs, | vec2 source_uvs, | ||||
| inout float weight_acc) | inout float weight_acc) | ||||
| { | { | ||||
| bvec4 is_planar, has_hit; | bvec4 is_planar, has_hit; | ||||
| Show All 23 Lines | vec4 get_ssr_samples(vec4 hit_pdf, | ||||
| vec3 hit_pos[4]; | vec3 hit_pos[4]; | ||||
| hit_pos[0] = transform_point(ViewMatrixInverse, hit_view[0]); | hit_pos[0] = transform_point(ViewMatrixInverse, hit_view[0]); | ||||
| hit_pos[1] = transform_point(ViewMatrixInverse, hit_view[1]); | hit_pos[1] = transform_point(ViewMatrixInverse, hit_view[1]); | ||||
| hit_pos[2] = transform_point(ViewMatrixInverse, hit_view[2]); | hit_pos[2] = transform_point(ViewMatrixInverse, hit_view[2]); | ||||
| hit_pos[3] = transform_point(ViewMatrixInverse, hit_view[3]); | hit_pos[3] = transform_point(ViewMatrixInverse, hit_view[3]); | ||||
| /* Get actual hit vector and hit coordinate (from last frame). */ | /* Get actual hit vector and hit coordinate (from last frame). */ | ||||
| vec4 mask = vec4(1.0); | vec4 mask = vec4(1.0); | ||||
| hit_pos[0] = get_hit_vector( | hit_pos[0] = get_hit_vector(hit_pos[0], pd, P, N, V, is_planar.x, hit_co[0].xy, mask.x); | ||||
| 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, P, N, V, is_planar.y, hit_co[0].zw, mask.y); | ||||
| hit_pos[1] = get_hit_vector( | hit_pos[2] = get_hit_vector(hit_pos[2], pd, P, N, V, is_planar.z, hit_co[1].xy, mask.z); | ||||
| hit_pos[1], pd, worldPosition, N, V, is_planar.y, hit_co[0].zw, mask.y); | hit_pos[3] = get_hit_vector(hit_pos[3], pd, P, N, V, is_planar.w, hit_co[1].zw, mask.w); | ||||
| 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); | |||||
| vec4 hit_dist; | vec4 hit_dist; | ||||
| hit_dist.x = length(hit_pos[0]); | hit_dist.x = length(hit_pos[0]); | ||||
| hit_dist.y = length(hit_pos[1]); | hit_dist.y = length(hit_pos[1]); | ||||
| hit_dist.z = length(hit_pos[2]); | hit_dist.z = length(hit_pos[2]); | ||||
| hit_dist.w = length(hit_pos[3]); | hit_dist.w = length(hit_pos[3]); | ||||
| hit_dist = max(vec4(1e-8), hit_dist); | hit_dist = max(vec4(1e-8), hit_dist); | ||||
| ▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | vec4 get_ssr_samples(vec4 hit_pdf, | ||||
| vec4 accum; | vec4 accum; | ||||
| accum = vec4(sample[0], mask.x) * weight.x * float(has_hit.x); | accum = vec4(sample[0], mask.x) * weight.x * float(has_hit.x); | ||||
| accum += vec4(sample[1], mask.y) * weight.y * float(has_hit.y); | accum += vec4(sample[1], mask.y) * weight.y * float(has_hit.y); | ||||
| accum += vec4(sample[2], mask.z) * weight.z * float(has_hit.z); | accum += vec4(sample[2], mask.z) * weight.z * float(has_hit.z); | ||||
| accum += vec4(sample[3], mask.w) * weight.w * float(has_hit.w); | accum += vec4(sample[3], mask.w) * weight.w * float(has_hit.w); | ||||
| return accum; | return accum; | ||||
| } | } | ||||
| void main() | void raytrace_resolve(ClosureInputGlossy cl_in, | ||||
| inout ClosureEvalGlossy cl_eval, | |||||
| inout ClosureEvalCommon cl_common, | |||||
| inout ClosureOutputGlossy cl_out) | |||||
| { | { | ||||
| ivec2 fullres_texel = ivec2(gl_FragCoord.xy); | |||||
| # ifdef FULLRES | # ifdef FULLRES | ||||
| ivec2 halfres_texel = fullres_texel; | ivec2 texel = ivec2(gl_FragCoord.xy); | ||||
| # else | # else | ||||
| ivec2 halfres_texel = ivec2(gl_FragCoord.xy / 2.0); | ivec2 texel = ivec2(gl_FragCoord.xy / 2.0); | ||||
| # endif | # 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 */ | /* Using world space */ | ||||
| vec3 viewPosition = get_view_space_from_depth(uvs, depth); /* Needed for viewCameraVec */ | vec3 V = cl_common.V; | ||||
| vec3 worldPosition = transform_point(ViewMatrixInverse, viewPosition); | vec3 N = cl_in.N; | ||||
| vec3 V = cameraVec; | vec3 P = cl_common.P; | ||||
| 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 */ | float roughness = cl_in.roughness; | ||||
| if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { | float roughnessSquared = max(1e-3, sqr(roughness)); | ||||
| discard; | |||||
| } | |||||
| float roughness = speccol_roughness.a; | |||||
| float roughnessSquared = max(1e-3, roughness * roughness); | |||||
| vec4 spec_accum = vec4(0.0); | |||||
| /* Resolve SSR */ | /* Resolve SSR */ | ||||
| float cone_cos = cone_cosine(roughnessSquared); | float cone_cos = cone_cosine(roughnessSquared); | ||||
| float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos; | 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 */ | 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); | vec4 ssr_accum = vec4(0.0); | ||||
| float weight_acc = 0.0; | float weight_acc = 0.0; | ||||
| if (roughness < ssrMaxRoughness + 0.2) { | if (roughness < ssrMaxRoughness + 0.2) { | ||||
| /* TODO optimize with textureGather */ | /* TODO optimize with textureGather */ | ||||
| /* Doing these fetches early to hide latency. */ | /* Doing these fetches early to hide latency. */ | ||||
| vec4 hit_pdf; | vec4 hit_pdf; | ||||
| hit_pdf.x = texelFetch(pdfBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).r; | hit_pdf.x = texelFetch(pdfBuffer, texel + neighbors[0 + neighborOffset], 0).r; | ||||
| hit_pdf.y = texelFetch(pdfBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).r; | hit_pdf.y = texelFetch(pdfBuffer, texel + neighbors[1 + neighborOffset], 0).r; | ||||
| hit_pdf.z = texelFetch(pdfBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).r; | hit_pdf.z = texelFetch(pdfBuffer, texel + neighbors[2 + neighborOffset], 0).r; | ||||
| hit_pdf.w = texelFetch(pdfBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).r; | hit_pdf.w = texelFetch(pdfBuffer, texel + neighbors[3 + neighborOffset], 0).r; | ||||
| ivec4 hit_data[2]; | ivec4 hit_data[2]; | ||||
| hit_data[0].xy = texelFetch(hitBuffer, halfres_texel + neighbors[0 + neighborOffset], 0).rg; | hit_data[0].xy = texelFetch(hitBuffer, texel + neighbors[0 + neighborOffset], 0).rg; | ||||
| hit_data[0].zw = texelFetch(hitBuffer, halfres_texel + neighbors[1 + neighborOffset], 0).rg; | hit_data[0].zw = texelFetch(hitBuffer, texel + neighbors[1 + neighborOffset], 0).rg; | ||||
| hit_data[1].xy = texelFetch(hitBuffer, halfres_texel + neighbors[2 + neighborOffset], 0).rg; | hit_data[1].xy = texelFetch(hitBuffer, texel + neighbors[2 + neighborOffset], 0).rg; | ||||
| hit_data[1].zw = texelFetch(hitBuffer, halfres_texel + neighbors[3 + neighborOffset], 0).rg; | hit_data[1].zw = texelFetch(hitBuffer, texel + neighbors[3 + neighborOffset], 0).rg; | ||||
| /* Find Planar Reflections affecting this pixel */ | /* Find Planar Reflections affecting this pixel */ | ||||
| PlanarData pd; | PlanarData pd; | ||||
| float planar_index; | float planar_index; | ||||
| for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { | for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { | ||||
| pd = planars_data[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) { | if (fade > 0.5) { | ||||
| planar_index = float(i); | planar_index = float(i); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| ssr_accum += get_ssr_samples(hit_pdf, | ssr_accum += get_ssr_samples(hit_pdf, | ||||
| hit_data, | hit_data, | ||||
| pd, | pd, | ||||
| planar_index, | planar_index, | ||||
| worldPosition, | P, | ||||
| N, | N, | ||||
| V, | V, | ||||
| roughnessSquared, | roughnessSquared, | ||||
| cone_tan, | cone_tan, | ||||
| source_uvs, | source_uvs, | ||||
| weight_acc); | weight_acc); | ||||
| } | } | ||||
| /* Compute SSR contribution */ | /* Compute SSR contribution */ | ||||
| if (weight_acc > 0.0) { | ssr_accum *= (weight_acc == 0.0) ? 0.0 : (1.0 / weight_acc); | ||||
| ssr_accum /= weight_acc; | |||||
| /* fade between 0.5 and 1.0 roughness */ | /* fade between 0.5 and 1.0 roughness */ | ||||
| ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); | ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); | ||||
| accumulate_light(ssr_accum.rgb, ssr_accum.a, spec_accum); | |||||
| cl_eval.raytrace_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 (depth == 1.0) { | |||||
| discard; | |||||
| } | } | ||||
| /* If SSR contribution is not 1.0, blend with cubemaps */ | vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba; | ||||
| if (spec_accum.a < 1.0) { | vec3 brdf = speccol_roughness.rgb; | ||||
| fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum); | float roughness = speccol_roughness.a; | ||||
| if (max_v3(brdf) <= 0.0) { | |||||
| discard; | |||||
| } | } | ||||
| fragColor = vec4(spec_accum.rgb * speccol_roughness.rgb, 1.0); | 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 | #endif | ||||