screen_space_reflection.glsl 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /* clang-format off */
  2. [vertex]
  3. layout(location = 0) in highp vec4 vertex_attrib;
  4. /* clang-format on */
  5. layout(location = 4) in vec2 uv_in;
  6. out vec2 uv_interp;
  7. out vec2 pos_interp;
  8. void main() {
  9. uv_interp = uv_in;
  10. gl_Position = vertex_attrib;
  11. pos_interp.xy = gl_Position.xy;
  12. }
  13. /* clang-format off */
  14. [fragment]
  15. in vec2 uv_interp;
  16. /* clang-format on */
  17. in vec2 pos_interp;
  18. uniform sampler2D source_diffuse; //texunit:0
  19. uniform sampler2D source_normal_roughness; //texunit:1
  20. uniform sampler2D source_depth; //texunit:2
  21. uniform float camera_z_near;
  22. uniform float camera_z_far;
  23. uniform vec2 viewport_size;
  24. uniform vec2 pixel_size;
  25. uniform float filter_mipmap_levels;
  26. uniform mat4 inverse_projection;
  27. uniform mat4 projection;
  28. uniform int num_steps;
  29. uniform float depth_tolerance;
  30. uniform float distance_fade;
  31. uniform float curve_fade_in;
  32. layout(location = 0) out vec4 frag_color;
  33. vec2 view_to_screen(vec3 view_pos, out float w) {
  34. vec4 projected = projection * vec4(view_pos, 1.0);
  35. projected.xyz /= projected.w;
  36. projected.xy = projected.xy * 0.5 + 0.5;
  37. w = projected.w;
  38. return projected.xy;
  39. }
  40. #define M_PI 3.14159265359
  41. void main() {
  42. vec4 diffuse = texture(source_diffuse, uv_interp);
  43. vec4 normal_roughness = texture(source_normal_roughness, uv_interp);
  44. vec3 normal;
  45. normal = normal_roughness.xyz * 2.0 - 1.0;
  46. float roughness = normal_roughness.w;
  47. float depth_tex = texture(source_depth, uv_interp).r;
  48. vec4 world_pos = inverse_projection * vec4(uv_interp * 2.0 - 1.0, depth_tex * 2.0 - 1.0, 1.0);
  49. vec3 vertex = world_pos.xyz / world_pos.w;
  50. #ifdef USE_ORTHOGONAL_PROJECTION
  51. vec3 view_dir = vec3(0.0, 0.0, -1.0);
  52. #else
  53. vec3 view_dir = normalize(vertex);
  54. #endif
  55. vec3 ray_dir = normalize(reflect(view_dir, normal));
  56. if (dot(ray_dir, normal) < 0.001) {
  57. frag_color = vec4(0.0);
  58. return;
  59. }
  60. //ray_dir = normalize(view_dir - normal * dot(normal,view_dir) * 2.0);
  61. //ray_dir = normalize(vec3(1.0, 1.0, -1.0));
  62. ////////////////
  63. // make ray length and clip it against the near plane (don't want to trace beyond visible)
  64. float ray_len = (vertex.z + ray_dir.z * camera_z_far) > -camera_z_near ? (-camera_z_near - vertex.z) / ray_dir.z : camera_z_far;
  65. vec3 ray_end = vertex + ray_dir * ray_len;
  66. float w_begin;
  67. vec2 vp_line_begin = view_to_screen(vertex, w_begin);
  68. float w_end;
  69. vec2 vp_line_end = view_to_screen(ray_end, w_end);
  70. vec2 vp_line_dir = vp_line_end - vp_line_begin;
  71. // we need to interpolate w along the ray, to generate perspective correct reflections
  72. w_begin = 1.0 / w_begin;
  73. w_end = 1.0 / w_end;
  74. float z_begin = vertex.z * w_begin;
  75. float z_end = ray_end.z * w_end;
  76. vec2 line_begin = vp_line_begin / pixel_size;
  77. vec2 line_dir = vp_line_dir / pixel_size;
  78. float z_dir = z_end - z_begin;
  79. float w_dir = w_end - w_begin;
  80. // clip the line to the viewport edges
  81. float scale_max_x = min(1.0, 0.99 * (1.0 - vp_line_begin.x) / max(1e-5, vp_line_dir.x));
  82. float scale_max_y = min(1.0, 0.99 * (1.0 - vp_line_begin.y) / max(1e-5, vp_line_dir.y));
  83. float scale_min_x = min(1.0, 0.99 * vp_line_begin.x / max(1e-5, -vp_line_dir.x));
  84. float scale_min_y = min(1.0, 0.99 * vp_line_begin.y / max(1e-5, -vp_line_dir.y));
  85. float line_clip = min(scale_max_x, scale_max_y) * min(scale_min_x, scale_min_y);
  86. line_dir *= line_clip;
  87. z_dir *= line_clip;
  88. w_dir *= line_clip;
  89. // clip z and w advance to line advance
  90. vec2 line_advance = normalize(line_dir); // down to pixel
  91. float step_size = length(line_advance) / length(line_dir);
  92. float z_advance = z_dir * step_size; // adapt z advance to line advance
  93. float w_advance = w_dir * step_size; // adapt w advance to line advance
  94. // make line advance faster if direction is closer to pixel edges (this avoids sampling the same pixel twice)
  95. float advance_angle_adj = 1.0 / max(abs(line_advance.x), abs(line_advance.y));
  96. line_advance *= advance_angle_adj; // adapt z advance to line advance
  97. z_advance *= advance_angle_adj;
  98. w_advance *= advance_angle_adj;
  99. vec2 pos = line_begin;
  100. float z = z_begin;
  101. float w = w_begin;
  102. float z_from = z / w;
  103. float z_to = z_from;
  104. float depth;
  105. vec2 prev_pos = pos;
  106. bool found = false;
  107. float steps_taken = 0.0;
  108. for (int i = 0; i < num_steps; i++) {
  109. pos += line_advance;
  110. z += z_advance;
  111. w += w_advance;
  112. // convert to linear depth
  113. depth = texture(source_depth, pos * pixel_size).r * 2.0 - 1.0;
  114. #ifdef USE_ORTHOGONAL_PROJECTION
  115. depth = ((depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
  116. #else
  117. depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
  118. #endif
  119. depth = -depth;
  120. z_from = z_to;
  121. z_to = z / w;
  122. if (depth > z_to) {
  123. // if depth was surpassed
  124. if ((depth <= max(z_to, z_from) + depth_tolerance) && (-depth < camera_z_far)) {
  125. // check the depth tolerance and far clip
  126. found = true;
  127. }
  128. break;
  129. }
  130. steps_taken += 1.0;
  131. prev_pos = pos;
  132. }
  133. if (found) {
  134. float margin_blend = 1.0;
  135. vec2 margin = vec2((viewport_size.x + viewport_size.y) * 0.5 * 0.05); // make a uniform margin
  136. if (any(bvec4(lessThan(pos, vec2(0.0, 0.0)), greaterThan(pos, viewport_size * 0.5)))) {
  137. // clip at the screen edges
  138. frag_color = vec4(0.0);
  139. return;
  140. }
  141. {
  142. //blend fading out towards inner margin
  143. // 0.25 = midpoint of half-resolution reflection
  144. vec2 margin_grad = mix(viewport_size * 0.5 - pos, pos, lessThan(pos, viewport_size * 0.25));
  145. margin_blend = smoothstep(0.0, margin.x * margin.y, margin_grad.x * margin_grad.y);
  146. //margin_blend = 1.0;
  147. }
  148. vec2 final_pos;
  149. float grad = (steps_taken + 1.0) / float(num_steps);
  150. float initial_fade = curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), curve_fade_in);
  151. float fade = pow(clamp(1.0 - grad, 0.0, 1.0), distance_fade) * initial_fade;
  152. final_pos = pos;
  153. #ifdef REFLECT_ROUGHNESS
  154. vec4 final_color;
  155. // if roughness is enabled, do screen space cone tracing
  156. if (roughness > 0.001) {
  157. ///////////////////////////////////////////////////////////////////////////////////////
  158. // use a blurred version (in consecutive mipmaps) of the screen to simulate roughness
  159. float gloss = 1.0 - roughness;
  160. float cone_angle = roughness * M_PI * 0.5;
  161. vec2 cone_dir = final_pos - line_begin;
  162. float cone_len = length(cone_dir);
  163. cone_dir = normalize(cone_dir); // will be used normalized from now on
  164. float max_mipmap = filter_mipmap_levels - 1.0;
  165. float gloss_mult = gloss;
  166. float rem_alpha = 1.0;
  167. final_color = vec4(0.0);
  168. for (int i = 0; i < 7; i++) {
  169. float op_len = 2.0 * tan(cone_angle) * cone_len; // opposite side of iso triangle
  170. float radius;
  171. {
  172. // fit to sphere inside cone (sphere ends at end of cone), something like this:
  173. // ___
  174. // \O/
  175. // V
  176. //
  177. // as it avoids bleeding from beyond the reflection as much as possible. As a plus
  178. // it also makes the rough reflection more elongated.
  179. float a = op_len;
  180. float h = cone_len;
  181. float a2 = a * a;
  182. float fh2 = 4.0f * h * h;
  183. radius = (a * (sqrt(a2 + fh2) - a)) / (4.0f * h);
  184. }
  185. // find the place where screen must be sampled
  186. vec2 sample_pos = (line_begin + cone_dir * (cone_len - radius)) * pixel_size;
  187. // radius is in pixels, so it's natural that log2(radius) maps to the right mipmap for the amount of pixels
  188. float mipmap = clamp(log2(radius), 0.0, max_mipmap);
  189. //mipmap = max(mipmap - 1.0, 0.0);
  190. // do sampling
  191. vec4 sample_color;
  192. {
  193. sample_color = textureLod(source_diffuse, sample_pos, mipmap);
  194. }
  195. // multiply by gloss
  196. sample_color.rgb *= gloss_mult;
  197. sample_color.a = gloss_mult;
  198. rem_alpha -= sample_color.a;
  199. if (rem_alpha < 0.0) {
  200. sample_color.rgb *= (1.0 - abs(rem_alpha));
  201. }
  202. final_color += sample_color;
  203. if (final_color.a >= 0.95) {
  204. // This code of accumulating gloss and aborting on near one
  205. // makes sense when you think of cone tracing.
  206. // Think of it as if roughness was 0, then we could abort on the first
  207. // iteration. For lesser roughness values, we need more iterations, but
  208. // each needs to have less influence given the sphere is smaller
  209. break;
  210. }
  211. cone_len -= radius * 2.0; // go to next (smaller) circle.
  212. gloss_mult *= gloss;
  213. }
  214. } else {
  215. final_color = textureLod(source_diffuse, final_pos * pixel_size, 0.0);
  216. }
  217. frag_color = vec4(final_color.rgb, fade * margin_blend);
  218. #else
  219. frag_color = vec4(textureLod(source_diffuse, final_pos * pixel_size, 0.0).rgb, fade * margin_blend);
  220. #endif
  221. } else {
  222. frag_color = vec4(0.0, 0.0, 0.0, 0.0);
  223. }
  224. }