DepthLimitedBlurShader.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {
  2. Vector2
  3. } from 'three';
  4. /**
  5. * TODO
  6. */
  7. const DepthLimitedBlurShader = {
  8. defines: {
  9. 'KERNEL_RADIUS': 4,
  10. 'DEPTH_PACKING': 1,
  11. 'PERSPECTIVE_CAMERA': 1
  12. },
  13. uniforms: {
  14. 'tDiffuse': { value: null },
  15. 'size': { value: new Vector2( 512, 512 ) },
  16. 'sampleUvOffsets': { value: [ new Vector2( 0, 0 ) ] },
  17. 'sampleWeights': { value: [ 1.0 ] },
  18. 'tDepth': { value: null },
  19. 'cameraNear': { value: 10 },
  20. 'cameraFar': { value: 1000 },
  21. 'depthCutoff': { value: 10 },
  22. },
  23. vertexShader: /* glsl */`
  24. #include <common>
  25. uniform vec2 size;
  26. varying vec2 vUv;
  27. varying vec2 vInvSize;
  28. void main() {
  29. vUv = uv;
  30. vInvSize = 1.0 / size;
  31. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  32. }`,
  33. fragmentShader: /* glsl */`
  34. #include <common>
  35. #include <packing>
  36. uniform sampler2D tDiffuse;
  37. uniform sampler2D tDepth;
  38. uniform float cameraNear;
  39. uniform float cameraFar;
  40. uniform float depthCutoff;
  41. uniform vec2 sampleUvOffsets[ KERNEL_RADIUS + 1 ];
  42. uniform float sampleWeights[ KERNEL_RADIUS + 1 ];
  43. varying vec2 vUv;
  44. varying vec2 vInvSize;
  45. float getDepth( const in vec2 screenPosition ) {
  46. #if DEPTH_PACKING == 1
  47. return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );
  48. #else
  49. return texture2D( tDepth, screenPosition ).x;
  50. #endif
  51. }
  52. float getViewZ( const in float depth ) {
  53. #if PERSPECTIVE_CAMERA == 1
  54. return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
  55. #else
  56. return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
  57. #endif
  58. }
  59. void main() {
  60. float depth = getDepth( vUv );
  61. if( depth >= ( 1.0 - EPSILON ) ) {
  62. discard;
  63. }
  64. float centerViewZ = -getViewZ( depth );
  65. bool rBreak = false, lBreak = false;
  66. float weightSum = sampleWeights[0];
  67. vec4 diffuseSum = texture2D( tDiffuse, vUv ) * weightSum;
  68. for( int i = 1; i <= KERNEL_RADIUS; i ++ ) {
  69. float sampleWeight = sampleWeights[i];
  70. vec2 sampleUvOffset = sampleUvOffsets[i] * vInvSize;
  71. vec2 sampleUv = vUv + sampleUvOffset;
  72. float viewZ = -getViewZ( getDepth( sampleUv ) );
  73. if( abs( viewZ - centerViewZ ) > depthCutoff ) rBreak = true;
  74. if( ! rBreak ) {
  75. diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;
  76. weightSum += sampleWeight;
  77. }
  78. sampleUv = vUv - sampleUvOffset;
  79. viewZ = -getViewZ( getDepth( sampleUv ) );
  80. if( abs( viewZ - centerViewZ ) > depthCutoff ) lBreak = true;
  81. if( ! lBreak ) {
  82. diffuseSum += texture2D( tDiffuse, sampleUv ) * sampleWeight;
  83. weightSum += sampleWeight;
  84. }
  85. }
  86. gl_FragColor = diffuseSum / weightSum;
  87. }`
  88. };
  89. const BlurShaderUtils = {
  90. createSampleWeights: function ( kernelRadius, stdDev ) {
  91. const weights = [];
  92. for ( let i = 0; i <= kernelRadius; i ++ ) {
  93. weights.push( gaussian( i, stdDev ) );
  94. }
  95. return weights;
  96. },
  97. createSampleOffsets: function ( kernelRadius, uvIncrement ) {
  98. const offsets = [];
  99. for ( let i = 0; i <= kernelRadius; i ++ ) {
  100. offsets.push( uvIncrement.clone().multiplyScalar( i ) );
  101. }
  102. return offsets;
  103. },
  104. configure: function ( material, kernelRadius, stdDev, uvIncrement ) {
  105. material.defines[ 'KERNEL_RADIUS' ] = kernelRadius;
  106. material.uniforms[ 'sampleUvOffsets' ].value = BlurShaderUtils.createSampleOffsets( kernelRadius, uvIncrement );
  107. material.uniforms[ 'sampleWeights' ].value = BlurShaderUtils.createSampleWeights( kernelRadius, stdDev );
  108. material.needsUpdate = true;
  109. }
  110. };
  111. function gaussian( x, stdDev ) {
  112. return Math.exp( - ( x * x ) / ( 2.0 * ( stdDev * stdDev ) ) ) / ( Math.sqrt( 2.0 * Math.PI ) * stdDev );
  113. }
  114. export { DepthLimitedBlurShader, BlurShaderUtils };