LUTPass.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. ( function () {
  2. const LUTShader = {
  3. defines: {
  4. USE_3DTEXTURE: 1
  5. },
  6. uniforms: {
  7. lut3d: {
  8. value: null
  9. },
  10. lut: {
  11. value: null
  12. },
  13. lutSize: {
  14. value: 0
  15. },
  16. tDiffuse: {
  17. value: null
  18. },
  19. intensity: {
  20. value: 1.0
  21. }
  22. },
  23. vertexShader:
  24. /* glsl */
  25. `
  26. varying vec2 vUv;
  27. void main() {
  28. vUv = uv;
  29. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  30. }
  31. `,
  32. fragmentShader:
  33. /* glsl */
  34. `
  35. uniform float lutSize;
  36. #if USE_3DTEXTURE
  37. precision highp sampler3D;
  38. uniform sampler3D lut3d;
  39. #else
  40. uniform sampler2D lut;
  41. vec3 lutLookup( sampler2D tex, float size, vec3 rgb ) {
  42. float sliceHeight = 1.0 / size;
  43. float yPixelHeight = 1.0 / ( size * size );
  44. // Get the slices on either side of the sample
  45. float slice = rgb.b * size;
  46. float interp = fract( slice );
  47. float slice0 = slice - interp;
  48. float centeredInterp = interp - 0.5;
  49. float slice1 = slice0 + sign( centeredInterp );
  50. // Pull y sample in by half a pixel in each direction to avoid color
  51. // bleeding from adjacent slices.
  52. float greenOffset = clamp( rgb.g * sliceHeight, yPixelHeight * 0.5, sliceHeight - yPixelHeight * 0.5 );
  53. vec2 uv0 = vec2(
  54. rgb.r,
  55. slice0 * sliceHeight + greenOffset
  56. );
  57. vec2 uv1 = vec2(
  58. rgb.r,
  59. slice1 * sliceHeight + greenOffset
  60. );
  61. vec3 sample0 = texture2D( tex, uv0 ).rgb;
  62. vec3 sample1 = texture2D( tex, uv1 ).rgb;
  63. return mix( sample0, sample1, abs( centeredInterp ) );
  64. }
  65. #endif
  66. varying vec2 vUv;
  67. uniform float intensity;
  68. uniform sampler2D tDiffuse;
  69. void main() {
  70. vec4 val = texture2D( tDiffuse, vUv );
  71. vec4 lutVal;
  72. // pull the sample in by half a pixel so the sample begins
  73. // at the center of the edge pixels.
  74. float pixelWidth = 1.0 / lutSize;
  75. float halfPixelWidth = 0.5 / lutSize;
  76. vec3 uvw = vec3( halfPixelWidth ) + val.rgb * ( 1.0 - pixelWidth );
  77. #if USE_3DTEXTURE
  78. lutVal = vec4( texture( lut3d, uvw ).rgb, val.a );
  79. #else
  80. lutVal = vec4( lutLookup( lut, lutSize, uvw ), val.a );
  81. #endif
  82. gl_FragColor = vec4( mix( val, lutVal, intensity ) );
  83. }
  84. `
  85. };
  86. class LUTPass extends THREE.ShaderPass {
  87. set lut( v ) {
  88. const material = this.material;
  89. if ( v !== this.lut ) {
  90. material.uniforms.lut3d.value = null;
  91. material.uniforms.lut.value = null;
  92. if ( v ) {
  93. const is3dTextureDefine = v.isDataTexture3D ? 1 : 0;
  94. if ( is3dTextureDefine !== material.defines.USE_3DTEXTURE ) {
  95. material.defines.USE_3DTEXTURE = is3dTextureDefine;
  96. material.needsUpdate = true;
  97. }
  98. material.uniforms.lutSize.value = v.image.width;
  99. if ( v.isDataTexture3D ) {
  100. material.uniforms.lut3d.value = v;
  101. } else {
  102. material.uniforms.lut.value = v;
  103. }
  104. }
  105. }
  106. }
  107. get lut() {
  108. return this.material.uniforms.lut.value || this.material.uniforms.lut3d.value;
  109. }
  110. set intensity( v ) {
  111. this.material.uniforms.intensity.value = v;
  112. }
  113. get intensity() {
  114. return this.material.uniforms.intensity.value;
  115. }
  116. constructor( options = {} ) {
  117. super( LUTShader );
  118. this.lut = options.lut || null;
  119. this.intensity = 'intensity' in options ? options.intensity : 1;
  120. }
  121. }
  122. THREE.LUTPass = LUTPass;
  123. } )();