screen_space_reflection.glsl 8.7 KB

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