depthOfField.fragment.fx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. BABYLON.JS Depth-of-field GLSL Shader
  3. Author: Olivier Guyot
  4. Does depth-of-field blur, edge blur, highlights enhancing
  5. Inspired by Francois Tarlier & Martins Upitis
  6. */
  7. #ifdef GL_ES
  8. precision highp float;
  9. #endif
  10. // samplers
  11. uniform sampler2D textureSampler;
  12. uniform sampler2D depthSampler;
  13. uniform sampler2D grainSampler;
  14. // uniforms
  15. uniform float grain_amount;
  16. uniform bool pentagon;
  17. uniform float maxZ;
  18. uniform bool blur_noise;
  19. uniform float screen_width;
  20. uniform float screen_height;
  21. uniform float distortion;
  22. uniform float focus_depth;
  23. uniform float aperture;
  24. uniform float gain;
  25. uniform float threshold;
  26. uniform float edge_blur;
  27. // varyings
  28. varying vec2 vUV;
  29. // constants
  30. #define PI 3.14159265
  31. const int RING_1_SAMPLES = 4;
  32. const int RING_2_SAMPLES = 6;
  33. const int RING_3_SAMPLES = 9;
  34. const int RING_4_SAMPLES = 12;
  35. const int RING_5_SAMPLES = 16;
  36. //const int RING_6_SAMPLES = 15;
  37. const float RING_STEP_DIST = 0.4; // a new blur ring is added each time this distance is passed
  38. const float PENTAGON_ANGLE_SUB = 1.2566; // 2PI / 5
  39. const float PENTAGON_ANGLE_SUB_HALF = 0.6283; // 2PI / 10
  40. // common calculations
  41. vec2 centered_screen_pos;
  42. float radius2;
  43. float radius;
  44. // applies edge distortion on texture coords
  45. vec2 getDistortedCoords(vec2 coords) {
  46. if(distortion == 0.0) { return coords; }
  47. vec2 direction = 1.0 * normalize(centered_screen_pos);
  48. vec2 dist_coords = vec2(0.5, 0.5);
  49. dist_coords.x = 0.5 + direction.x * radius2 * 1.0;
  50. dist_coords.y = 0.5 + direction.y * radius2 * 1.0;
  51. float dist_amount = clamp(distortion*0.23, 0.0, 1.0);
  52. dist_coords = mix(coords, dist_coords, dist_amount);
  53. return dist_coords;
  54. }
  55. // picks either original screen color or highlights only
  56. vec4 getColor(vec2 coords, bool highlight) {
  57. vec4 color = texture2D(textureSampler, coords);
  58. if(highlight) {
  59. float luminance = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));
  60. float lum_threshold;
  61. if(threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
  62. else { lum_threshold = 0.5 + 0.44 * threshold; }
  63. if(luminance < lum_threshold) {
  64. color.rgb = vec3(0.0, 0.0, 0.0);
  65. color.a = 1.0;
  66. }
  67. }
  68. return color;
  69. }
  70. // returns a modifier to be applied on the radius, in order to simulate a pentagon
  71. float pentagonShape(float angle) {
  72. float a1 = mod(angle, PENTAGON_ANGLE_SUB) / PENTAGON_ANGLE_SUB - 0.5;
  73. float a2 = 0.5 - a1 * a1;
  74. return 1.35 - 0.94 * a2;
  75. }
  76. // returns original screen color after blur
  77. vec4 getBlurColor(vec2 coords, float size, bool highlight) {
  78. float w = (size/screen_width);
  79. float h = (size/screen_height);
  80. vec4 col = getColor(coords, highlight);
  81. if(size == 0.0) { return col; }
  82. float s = 1.0;
  83. float pw; // sample x relative coord
  84. float ph; // sample y relative coord
  85. float bias = 0.65; // inner/outer ring bias
  86. if(highlight) { bias = 0.95; }
  87. float sample_angle;
  88. float ratio_rings;
  89. float ring_radius;
  90. float penta; // pentagon shape modifier
  91. int ring_count;
  92. if(size >= 6.0 * RING_STEP_DIST) { ring_count = 6; }
  93. else if(size >= 5.0 * RING_STEP_DIST) { ring_count = 5; }
  94. else if(size >= 4.0 * RING_STEP_DIST) { ring_count = 4; }
  95. else if(size >= 3.0 * RING_STEP_DIST) { ring_count = 3; }
  96. else if(size >= 2.0 * RING_STEP_DIST) { ring_count = 2; }
  97. else { ring_count = 1; }
  98. // RING 1
  99. if(size > RING_STEP_DIST) {
  100. ring_radius = size / float(ring_count);
  101. ratio_rings = 1.0 / float(ring_count);
  102. for(int i = 0; i < RING_1_SAMPLES; i++) {
  103. sample_angle = PI *2.0 * float(i) / float(RING_1_SAMPLES);
  104. if(pentagon) { penta = pentagonShape(sample_angle); }
  105. else { penta = 1.0; }
  106. pw = cos( sample_angle ) * penta * ring_radius;
  107. ph = sin( sample_angle ) * penta * ring_radius;
  108. col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
  109. s += 1.0 * mix(1.0, ratio_rings, bias);
  110. }
  111. }
  112. // RING 2
  113. if(size > RING_STEP_DIST * 2.0) {
  114. ring_radius = 2.0 * size / float(ring_count);
  115. ratio_rings = 2.0 / float(ring_count);
  116. for(int i = 0; i < RING_2_SAMPLES; i++) {
  117. sample_angle = PI *2.0 * float(i) / float(RING_2_SAMPLES);
  118. if(pentagon) { penta = pentagonShape(sample_angle); }
  119. else { penta = 1.0; }
  120. pw = cos( sample_angle ) * penta * ring_radius;
  121. ph = sin( sample_angle ) * penta * ring_radius;
  122. col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
  123. s += 1.0 * mix(1.0, ratio_rings, bias);
  124. }
  125. }
  126. // RING 3
  127. if(size > RING_STEP_DIST * 3.0) {
  128. ring_radius = 3.0 * size / float(ring_count);
  129. ratio_rings = 3.0 / float(ring_count);
  130. for(int i = 0; i < RING_3_SAMPLES; i++) {
  131. sample_angle = PI *2.0 * float(i) / float(RING_3_SAMPLES);
  132. if(pentagon) { penta = pentagonShape(sample_angle); }
  133. else { penta = 1.0; }
  134. pw = cos( sample_angle ) * penta * ring_radius;
  135. ph = sin( sample_angle ) * penta * ring_radius;
  136. col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
  137. s += 1.0 * mix(1.0, ratio_rings, bias);
  138. }
  139. }
  140. // RING 4
  141. if(size > RING_STEP_DIST * 4.0) {
  142. ring_radius = 4.0 * size / float(ring_count);
  143. ratio_rings = 4.0 / float(ring_count);
  144. for(int i = 0; i < RING_4_SAMPLES; i++) {
  145. sample_angle = PI *2.0 * float(i) / float(RING_4_SAMPLES);
  146. if(pentagon) { penta = pentagonShape(sample_angle); }
  147. else { penta = 1.0; }
  148. pw = cos( sample_angle ) * penta * ring_radius;
  149. ph = sin( sample_angle ) * penta * ring_radius;
  150. col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
  151. s += 1.0 * mix(1.0, ratio_rings, bias);
  152. }
  153. }
  154. // RING 5
  155. if(size > RING_STEP_DIST * 5.0) {
  156. ring_radius = 5.0 * size / float(ring_count);
  157. ratio_rings = 5.0 / float(ring_count);
  158. for(int i = 0; i < RING_5_SAMPLES; i++) {
  159. sample_angle = PI *2.0 * float(i) / float(RING_5_SAMPLES);
  160. if(pentagon) { penta = pentagonShape(sample_angle); }
  161. else { penta = 1.0; }
  162. pw = cos( sample_angle ) * penta * ring_radius;
  163. ph = sin( sample_angle ) * penta * ring_radius;
  164. col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
  165. s += 1.0 * mix(1.0, ratio_rings, bias);
  166. }
  167. }
  168. col /= s; // scales color according to samples taken
  169. col.a = 1.0;
  170. return col;
  171. }
  172. // on-the-fly constant noise
  173. vec2 rand(vec2 co)
  174. {
  175. float noise1 = (fract(sin(dot(co ,vec2(12.9898,78.233))) * 43758.5453));
  176. float noise2 = (fract(sin(dot(co ,vec2(12.9898,78.233)*2.0)) * 43758.5453));
  177. return clamp(vec2(noise1,noise2),0.0,1.0);
  178. }
  179. void main(void)
  180. {
  181. // Common calc
  182. centered_screen_pos = vec2(vUV.x-0.5, vUV.y-0.5);
  183. radius2 = centered_screen_pos.x*centered_screen_pos.x + centered_screen_pos.y*centered_screen_pos.y;
  184. radius = sqrt(radius2);
  185. vec4 final_color;
  186. vec2 distorted_coords = getDistortedCoords(vUV);
  187. vec2 texels_coords = vec2(vUV.x * screen_width, vUV.y * screen_height); // varies from 0 to SCREEN_WIDTH or _HEIGHT
  188. // blur from depth of field effect
  189. float dof_blur_amount = 0.0;
  190. if(focus_depth != -1.0) {
  191. vec4 depth_sample = texture2D(depthSampler, distorted_coords);
  192. float depth = depth_sample.r;
  193. dof_blur_amount = abs(depth - focus_depth) * aperture * 3.5;
  194. if(dof_blur_amount < 0.05) { dof_blur_amount = 0.0; } // no blur at all
  195. else if( depth - focus_depth < 0.0 ) { dof_blur_amount *= 2.0; } // blur more when close to camera
  196. dof_blur_amount = clamp(dof_blur_amount, 0.0, 1.0);
  197. }
  198. // blur from edge blur effect
  199. float edge_blur_amount = 0.0;
  200. if(edge_blur > 0.0) {
  201. edge_blur_amount = clamp( ( radius*2.0 - 1.0 + 0.15*edge_blur ) * 1.5 , 0.0 , 1.0 ) * 1.3;
  202. }
  203. // total blur amount
  204. float blur_amount = max(edge_blur_amount, dof_blur_amount);
  205. // apply blur if necessary
  206. if(blur_amount == 0.0) {
  207. gl_FragColor = getColor(distorted_coords, false);
  208. } else {
  209. gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7, false)
  210. + gain * blur_amount*getBlurColor(distorted_coords, blur_amount * 2.75, true);
  211. if(blur_noise) {
  212. // we put a slight amount of noise in the blurred color
  213. vec2 noise = rand(distorted_coords) * 0.01 * blur_amount;
  214. vec2 blurred_coord = vec2(distorted_coords.x + noise.x, distorted_coords.y + noise.y);
  215. gl_FragColor = 0.04 * getColor(blurred_coord, false) + 0.96 * gl_FragColor;
  216. }
  217. }
  218. if(grain_amount > 0.0) {
  219. vec4 grain_color = texture2D(grainSampler, texels_coords*0.003);
  220. gl_FragColor.rgb += ( -0.5 + grain_color.rgb ) * 0.20;
  221. }
  222. }