screen_space_reflection.glsl 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #[compute]
  2. #version 450
  3. #VERSION_DEFINES
  4. layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
  5. layout(rgba16f, set = 0, binding = 0) uniform restrict readonly image2D source_diffuse;
  6. layout(r32f, set = 0, binding = 1) uniform restrict readonly image2D source_depth;
  7. layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_image;
  8. #ifdef MODE_ROUGH
  9. layout(r8, set = 1, binding = 1) uniform restrict writeonly image2D blur_radius_image;
  10. #endif
  11. layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness;
  12. layout(set = 3, binding = 0) uniform sampler2D source_metallic;
  13. layout(push_constant, std430) uniform Params {
  14. vec4 proj_info;
  15. ivec2 screen_size;
  16. float camera_z_near;
  17. float camera_z_far;
  18. int num_steps;
  19. float depth_tolerance;
  20. float distance_fade;
  21. float curve_fade_in;
  22. bool orthogonal;
  23. float filter_mipmap_levels;
  24. bool use_half_res;
  25. uint view_index;
  26. }
  27. params;
  28. #include "screen_space_reflection_inc.glsl"
  29. vec2 view_to_screen(vec3 view_pos, out float w) {
  30. vec4 projected = scene_data.projection[params.view_index] * vec4(view_pos, 1.0);
  31. projected.xyz /= projected.w;
  32. projected.xy = projected.xy * 0.5 + 0.5;
  33. w = projected.w;
  34. return projected.xy;
  35. }
  36. #define M_PI 3.14159265359
  37. void main() {
  38. // Pixel being shaded
  39. ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
  40. if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing
  41. return;
  42. }
  43. vec2 pixel_size = 1.0 / vec2(params.screen_size);
  44. vec2 uv = vec2(ssC.xy) * pixel_size;
  45. uv += pixel_size * 0.5;
  46. float base_depth = imageLoad(source_depth, ssC).r;
  47. // World space point being shaded
  48. vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth);
  49. vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
  50. vec3 normal = normalize(normal_roughness.xyz * 2.0 - 1.0);
  51. float roughness = normal_roughness.w;
  52. if (roughness > 0.5) {
  53. roughness = 1.0 - roughness;
  54. }
  55. roughness /= (127.0 / 255.0);
  56. // The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828.
  57. if (roughness > 0.6) {
  58. // Do not compute SSR for rough materials to improve performance at the cost of
  59. // subtle artifacting.
  60. #ifdef MODE_ROUGH
  61. imageStore(blur_radius_image, ssC, vec4(0.0));
  62. #endif
  63. imageStore(ssr_image, ssC, vec4(0.0));
  64. return;
  65. }
  66. normal = normalize(normal);
  67. normal.y = -normal.y; //because this code reads flipped
  68. vec3 view_dir;
  69. if (sc_multiview) {
  70. view_dir = normalize(vertex + scene_data.eye_offset[params.view_index].xyz);
  71. } else {
  72. view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : normalize(vertex);
  73. }
  74. vec3 ray_dir = normalize(reflect(view_dir, normal));
  75. if (dot(ray_dir, normal) < 0.001) {
  76. imageStore(ssr_image, ssC, vec4(0.0));
  77. return;
  78. }
  79. ////////////////
  80. // make ray length and clip it against the near plane (don't want to trace beyond visible)
  81. float ray_len = (vertex.z + ray_dir.z * params.camera_z_far) > -params.camera_z_near ? (-params.camera_z_near - vertex.z) / ray_dir.z : params.camera_z_far;
  82. vec3 ray_end = vertex + ray_dir * ray_len;
  83. float w_begin;
  84. vec2 vp_line_begin = view_to_screen(vertex, w_begin);
  85. float w_end;
  86. vec2 vp_line_end = view_to_screen(ray_end, w_end);
  87. vec2 vp_line_dir = vp_line_end - vp_line_begin;
  88. // we need to interpolate w along the ray, to generate perspective correct reflections
  89. w_begin = 1.0 / w_begin;
  90. w_end = 1.0 / w_end;
  91. float z_begin = vertex.z * w_begin;
  92. float z_end = ray_end.z * w_end;
  93. vec2 line_begin = vp_line_begin / pixel_size;
  94. vec2 line_dir = vp_line_dir / pixel_size;
  95. float z_dir = z_end - z_begin;
  96. float w_dir = w_end - w_begin;
  97. // clip the line to the viewport edges
  98. float scale_max_x = min(1.0, 0.99 * (1.0 - vp_line_begin.x) / max(1e-5, vp_line_dir.x));
  99. float scale_max_y = min(1.0, 0.99 * (1.0 - vp_line_begin.y) / max(1e-5, vp_line_dir.y));
  100. float scale_min_x = min(1.0, 0.99 * vp_line_begin.x / max(1e-5, -vp_line_dir.x));
  101. float scale_min_y = min(1.0, 0.99 * vp_line_begin.y / max(1e-5, -vp_line_dir.y));
  102. float line_clip = min(scale_max_x, scale_max_y) * min(scale_min_x, scale_min_y);
  103. line_dir *= line_clip;
  104. z_dir *= line_clip;
  105. w_dir *= line_clip;
  106. // clip z and w advance to line advance
  107. vec2 line_advance = normalize(line_dir); // down to pixel
  108. float step_size = 1.0 / length(line_dir);
  109. float z_advance = z_dir * step_size; // adapt z advance to line advance
  110. float w_advance = w_dir * step_size; // adapt w advance to line advance
  111. // make line advance faster if direction is closer to pixel edges (this avoids sampling the same pixel twice)
  112. float advance_angle_adj = 1.0 / max(abs(line_advance.x), abs(line_advance.y));
  113. line_advance *= advance_angle_adj; // adapt z advance to line advance
  114. z_advance *= advance_angle_adj;
  115. w_advance *= advance_angle_adj;
  116. vec2 pos = line_begin;
  117. float z = z_begin;
  118. float w = w_begin;
  119. float z_from = z / w;
  120. float z_to = z_from;
  121. float depth;
  122. vec2 prev_pos = pos;
  123. if (ivec2(pos + line_advance - 0.5) == ssC) {
  124. // It is possible for rounding to cause our first pixel to check to be the pixel we're reflecting.
  125. // Make sure we skip it
  126. pos += line_advance;
  127. z += z_advance;
  128. w += w_advance;
  129. }
  130. bool found = false;
  131. float steps_taken = 0.0;
  132. for (int i = 0; i < params.num_steps; i++) {
  133. pos += line_advance;
  134. z += z_advance;
  135. w += w_advance;
  136. // convert to linear depth
  137. ivec2 test_pos = ivec2(pos - 0.5);
  138. depth = imageLoad(source_depth, test_pos).r;
  139. if (sc_multiview) {
  140. depth = depth * 2.0 - 1.0;
  141. depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
  142. depth = -depth;
  143. }
  144. z_from = z_to;
  145. z_to = z / w;
  146. if (depth > z_to) {
  147. // Test if our ray is hitting the "right" side of the surface, if not we're likely self reflecting and should skip.
  148. vec4 test_normal_roughness = imageLoad(source_normal_roughness, test_pos);
  149. vec3 test_normal = test_normal_roughness.xyz * 2.0 - 1.0;
  150. test_normal = normalize(test_normal);
  151. test_normal.y = -test_normal.y; // Because this code reads flipped.
  152. if (dot(ray_dir, test_normal) < 0.001) {
  153. // if depth was surpassed
  154. if (depth <= max(z_to, z_from) + params.depth_tolerance && -depth < params.camera_z_far * 0.95) {
  155. // check the depth tolerance and far clip
  156. // check that normal is valid
  157. found = true;
  158. }
  159. break;
  160. }
  161. }
  162. steps_taken += 1.0;
  163. prev_pos = pos;
  164. }
  165. if (found) {
  166. float margin_blend = 1.0;
  167. vec2 final_pos = pos;
  168. vec2 margin = vec2((params.screen_size.x + params.screen_size.y) * 0.05); // make a uniform margin
  169. if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, params.screen_size)))) {
  170. // clip at the screen edges
  171. imageStore(ssr_image, ssC, vec4(0.0));
  172. return;
  173. }
  174. {
  175. //blend fading out towards inner margin
  176. // 0.5 = midpoint of reflection
  177. vec2 margin_grad = mix(params.screen_size - pos, pos, lessThan(pos, params.screen_size * 0.5));
  178. margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);
  179. //margin_blend = 1.0;
  180. }
  181. // Fade In / Fade Out
  182. float grad = (steps_taken + 1.0) / float(params.num_steps);
  183. float initial_fade = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in);
  184. float fade = pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade) * initial_fade;
  185. // Ensure that precision errors do not introduce any fade. Even if it is just slightly below 1.0,
  186. // strong specular light can leak through the reflection.
  187. if (fade > 0.999) {
  188. fade = 1.0;
  189. }
  190. // This is an ad-hoc term to fade out the SSR as roughness increases. Values used
  191. // are meant to match the visual appearance of a ReflectionProbe.
  192. float roughness_fade = smoothstep(0.4, 0.7, 1.0 - roughness);
  193. // Schlick term.
  194. float metallic = texelFetch(source_metallic, ssC << 1, 0).w;
  195. // F0 is the reflectance of normally incident light (perpendicular to the surface).
  196. // Dielectric materials have a widely accepted default value of 0.04. We assume that metals reflect all light, so their F0 is 1.0.
  197. float f0 = mix(0.04, 1.0, metallic);
  198. float m = clamp(1.0 - dot(normal, -view_dir), 0.0, 1.0);
  199. float m2 = m * m;
  200. m = m2 * m2 * m; // pow(m,5)
  201. float fresnel_term = f0 + (1.0 - f0) * m; // Fresnel Schlick term.
  202. // The alpha value of final_color controls the blending with specular light in specular_merge.glsl.
  203. // Note that the Fresnel term is multiplied with the RGB color instead of being a part of the alpha value.
  204. // There is a key difference:
  205. // - multiplying a term with RGB darkens the SSR light without introducing/taking away specular light.
  206. // - combining a term into the Alpha value introduces specular light at the expense of the SSR light.
  207. vec4 final_color = vec4(imageLoad(source_diffuse, ivec2(final_pos - 0.5)).rgb * fresnel_term, fade * margin_blend * roughness_fade);
  208. imageStore(ssr_image, ssC, final_color);
  209. #ifdef MODE_ROUGH
  210. // if roughness is enabled, do screen space cone tracing
  211. float blur_radius = 0.0;
  212. if (roughness > 0.001) {
  213. float cone_angle = min(roughness, 0.999) * M_PI * 0.5;
  214. float cone_len = length(final_pos - line_begin);
  215. float op_len = 2.0 * tan(cone_angle) * cone_len; // opposite side of iso triangle
  216. {
  217. // fit to sphere inside cone (sphere ends at end of cone), something like this:
  218. // ___
  219. // \O/
  220. // V
  221. //
  222. // as it avoids bleeding from beyond the reflection as much as possible. As a plus
  223. // it also makes the rough reflection more elongated.
  224. float a = op_len;
  225. float h = cone_len;
  226. float a2 = a * a;
  227. float fh2 = 4.0f * h * h;
  228. blur_radius = (a * (sqrt(a2 + fh2) - a)) / (4.0f * h);
  229. }
  230. }
  231. imageStore(blur_radius_image, ssC, vec4(blur_radius / 255.0)); //stored in r8
  232. #endif // MODE_ROUGH
  233. } else {
  234. #ifdef MODE_ROUGH
  235. imageStore(blur_radius_image, ssC, vec4(0.0));
  236. #endif
  237. imageStore(ssr_image, ssC, vec4(0.0));
  238. }
  239. }