screenSpaceReflection.fragment.fx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Screen Space Reflection Post-Process based on the tutorial
  2. // http://imanolfotia.com/blog/update/2017/03/11/ScreenSpaceReflections.html
  3. uniform sampler2D textureSampler;
  4. uniform sampler2D normalSampler;
  5. uniform sampler2D positionSampler;
  6. uniform sampler2D reflectivitySampler;
  7. uniform mat4 view;
  8. uniform mat4 projection;
  9. uniform float step;
  10. uniform float strength;
  11. uniform float threshold;
  12. uniform float roughnessFactor;
  13. uniform float reflectionSpecularFalloffExponent;
  14. // Varyings
  15. varying vec2 vUV;
  16. // Structs
  17. struct ReflectionInfo {
  18. vec3 color;
  19. vec4 coords;
  20. };
  21. /**
  22. * According to specular, see https://en.wikipedia.org/wiki/Schlick%27s_approximation
  23. */
  24. vec3 fresnelSchlick(float cosTheta, vec3 F0)
  25. {
  26. return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
  27. }
  28. /**
  29. * Once the pixel's coordinates has been found, let's adjust (smooth) a little bit
  30. * by sampling multiple reflection pixels.
  31. */
  32. ReflectionInfo smoothReflectionInfo(vec3 dir, vec3 hitCoord)
  33. {
  34. ReflectionInfo info;
  35. info.color = vec3(0.0);
  36. vec4 projectedCoord;
  37. float sampledDepth;
  38. for(int i = 0; i < SMOOTH_STEPS; i++)
  39. {
  40. projectedCoord = projection * vec4(hitCoord, 1.0);
  41. projectedCoord.xy /= projectedCoord.w;
  42. projectedCoord.xy = 0.5 * projectedCoord.xy + vec2(0.5);
  43. sampledDepth = (view * texture2D(positionSampler, projectedCoord.xy)).z;
  44. float depth = sampledDepth - hitCoord.z;
  45. dir *= 0.5;
  46. if(depth > 0.0)
  47. hitCoord -= dir;
  48. else
  49. hitCoord += dir;
  50. info.color += texture2D(textureSampler, projectedCoord.xy).rgb;
  51. }
  52. projectedCoord = projection * vec4(hitCoord, 1.0);
  53. projectedCoord.xy /= projectedCoord.w;
  54. projectedCoord.xy = 0.5 * projectedCoord.xy + vec2(0.5);
  55. // Merge colors
  56. info.coords = vec4(projectedCoord.xy, sampledDepth, 1.0);
  57. info.color += texture2D(textureSampler, projectedCoord.xy).rgb;
  58. info.color /= float(SMOOTH_STEPS + 1);
  59. return info;
  60. }
  61. /**
  62. * Tests the given world position (hitCoord) according to the given reflection vector (dir)
  63. * until it finds a collision (means that depth is enough close to say "it's the pixel to sample!").
  64. */
  65. ReflectionInfo getReflectionInfo(vec3 dir, vec3 hitCoord)
  66. {
  67. ReflectionInfo info;
  68. vec4 projectedCoord;
  69. float sampledDepth;
  70. dir *= step;
  71. for(int i = 0; i < REFLECTION_SAMPLES; i++)
  72. {
  73. hitCoord += dir;
  74. projectedCoord = projection * vec4(hitCoord, 1.0);
  75. projectedCoord.xy /= projectedCoord.w;
  76. projectedCoord.xy = 0.5 * projectedCoord.xy + vec2(0.5);
  77. sampledDepth = (view * texture2D(positionSampler, projectedCoord.xy)).z;
  78. float depth = sampledDepth - hitCoord.z;
  79. if(((depth - dir.z) < threshold) && depth <= 0.0)
  80. {
  81. #ifdef ENABLE_SMOOTH_REFLECTIONS
  82. return smoothReflectionInfo(dir, hitCoord);
  83. #else
  84. info.color = texture2D(textureSampler, projectedCoord.xy).rgb;
  85. info.coords = vec4(projectedCoord.xy, sampledDepth, 0.0);
  86. return info;
  87. #endif
  88. }
  89. }
  90. info.color = texture2D(textureSampler, projectedCoord.xy).rgb;
  91. info.coords = vec4(projectedCoord.xy, sampledDepth, 0.0);
  92. return info;
  93. }
  94. vec3 hash(vec3 a)
  95. {
  96. a = fract(a * 0.8);
  97. a += dot(a, a.yxz + 19.19);
  98. return fract((a.xxy + a.yxx) * a.zyx);
  99. }
  100. void main()
  101. {
  102. #ifdef SSR_SUPPORTED
  103. // Intensity
  104. vec4 albedoFull = texture2D(textureSampler, vUV);
  105. vec3 albedo = albedoFull.rgb;
  106. float spec = texture2D(reflectivitySampler, vUV).r;
  107. if (spec == 0.0) {
  108. gl_FragColor = albedoFull;
  109. return;
  110. }
  111. // Get coordinates of the pixel to pick according to the pixel's position and normal.
  112. #ifdef PREPASS_LAYOUT
  113. vec3 normal = (texture2D(normalSampler, vUV)).gba;
  114. #else
  115. vec3 normal = (texture2D(normalSampler, vUV)).xyz;
  116. #endif
  117. vec3 position = (view * texture2D(positionSampler, vUV)).xyz;
  118. vec3 reflected = normalize(reflect(normalize(position), normalize(normal)));
  119. float roughness = 1.0 - texture2D(reflectivitySampler, vUV).a;
  120. vec3 jitt = mix(vec3(0.0), hash(position), roughness) * roughnessFactor;
  121. ReflectionInfo info = getReflectionInfo(jitt + reflected, position);
  122. // ReflectionInfo info = getReflectionInfo(reflected, position); // For debug: no roughness
  123. vec2 dCoords = smoothstep(0.2, 0.6, abs(vec2(0.5, 0.5) - info.coords.xy));
  124. float screenEdgefactor = clamp(1.0 - (dCoords.x + dCoords.y), 0.0, 1.0);
  125. // Fresnel
  126. vec3 F0 = vec3(0.04);
  127. F0 = mix(F0, albedo, spec);
  128. vec3 fresnel = fresnelSchlick(max(dot(normalize(normal), normalize(position)), 0.0), F0);
  129. // Apply
  130. float reflectionMultiplier = clamp(pow(spec * strength, reflectionSpecularFalloffExponent) * screenEdgefactor * reflected.z, 0.0, 0.9);
  131. float albedoMultiplier = 1.0 - reflectionMultiplier;
  132. vec3 SSR = info.color * fresnel;
  133. gl_FragColor = vec4((albedo * albedoMultiplier) + (SSR * reflectionMultiplier), albedoFull.a);
  134. #else
  135. gl_FragColor = texture2D(textureSampler, vUV);
  136. #endif
  137. }