HalftoneShader.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * @author meatbags / xavierburrow.com, github/meatbags
  3. *
  4. * Colour halftone shader
  5. * Shape uniform (1 = circle, 2 = euclidean dot, 3 = ellipse, 4 = line, 5 = square)
  6. * Blending mode (1 = linear, 2 = add, 3 = multiply, 4 = lighter colour, 5 = darker colour)
  7. */
  8. THREE.HalftoneShader = {
  9. uniforms: {
  10. "shape": {value: 1},
  11. "tDiffuse": {value: null},
  12. "radius": {value: 5},
  13. "rC": {value: Math.PI * 0.25},
  14. "rM": {value: Math.PI * 0.33},
  15. "rY": {value: Math.PI * 0.66},
  16. "width": {value: 1},
  17. "height": {value: 1},
  18. "blending": {value: 0},
  19. "blendingMode": {value: 1},
  20. "greyscale": {value: false},
  21. "disable": {value: false}
  22. },
  23. vertexShader: `
  24. varying vec2 vUV;
  25. void main() {
  26. vUV = uv;
  27. gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  28. }`,
  29. fragmentShader: `
  30. #define SQRT2 1.41421356
  31. #define SQRT2_HALF 0.70710678
  32. #define SQRT2_MINUS_ONE 0.41421356
  33. #define SQRT2_HALF_MINUS_ONE 0.20710678
  34. uniform sampler2D tDiffuse;
  35. uniform float radius;
  36. uniform float rC;
  37. uniform float rM;
  38. uniform float rY;
  39. uniform float width;
  40. uniform float height;
  41. uniform int shape;
  42. uniform bool disable;
  43. uniform float blending;
  44. uniform int blendingMode;
  45. uniform bool greyscale;
  46. varying vec2 vUV;
  47. float blend(float a, float b, float t) {
  48. return a * (1.0 - t) + b * t;
  49. }
  50. float blendColour(float a, float b, float t) {
  51. if (blendingMode == 1) {
  52. return blend(a, b, t);
  53. } else if (blendingMode == 2) {
  54. return blend(a, min(1.0, a + b), t);
  55. } else if (blendingMode == 3) {
  56. return blend(a, max(0.0, a * b), t);
  57. } else if (blendingMode == 4) {
  58. return blend(a, max(a, b), t);
  59. } else {
  60. return blend(a, min(a, b), t);
  61. }
  62. }
  63. float hypot(float x, float y) {
  64. return sqrt(x * x + y * y);
  65. }
  66. float distanceTo(vec2 a, vec2 b) {
  67. return hypot(b.x - a.x, b.y - a.y);
  68. }
  69. float shapeDistance(vec2 p, vec2 coord, vec2 n) {
  70. float d = distanceTo(p, coord);
  71. if (shape == 3) {
  72. // ellipse
  73. if (d != 0.0) {
  74. float dp = abs((p.x - coord.x) / d * n.x + (p.y - coord.y) / d * n.y);
  75. d = (d * (1.0 - SQRT2_HALF_MINUS_ONE)) + dp * d * SQRT2_MINUS_ONE;
  76. }
  77. } else if (shape == 4) {
  78. // line
  79. float dp = (p.x - coord.x) * n.x + (p.y - coord.y) * n.y;
  80. d = hypot(n.x * dp, n.y * dp);
  81. }
  82. return d;
  83. }
  84. float shapeRadius(float r, vec2 p, vec2 coord) {
  85. float theta = atan(p.y - coord.y, p.x - coord.x);
  86. float sin_t = abs(sin(theta));
  87. float cos_t = abs(cos(theta));
  88. if (shape == 2) {
  89. // euclidean dot
  90. float square = r + ((sin_t > cos_t) ? r - sin_t * r : r - cos_t * r);
  91. if (r <= 0.5) {
  92. r = blend(r, square, r * 2.0);
  93. } else {
  94. float max_r = r + 2.0 * ((sin_t > cos_t) ? r - sin_t * r : r - cos_t * r);
  95. r = blend(square, max_r, pow(abs((r - 0.5) * 2.0), 0.4));
  96. }
  97. } else if (shape == 5) {
  98. // square
  99. r += (sin_t > cos_t) ? r - sin_t * r : r - cos_t * r;
  100. }
  101. return r;
  102. }
  103. vec2 gridReference(vec2 p, vec2 origin, vec2 n, float step, float aspect) {
  104. // get nearest grid reference (rotated grid)
  105. float dot_normal = n.x * (p.x - origin.x) + n.y * (p.y - origin.y);
  106. float dot_line = -n.y * (p.x - origin.x) + n.x * (p.y - origin.y);
  107. vec2 offset = vec2(n.x * dot_normal, n.y * dot_normal);
  108. float offset_normal = mod(hypot(offset.x, offset.y), step);
  109. float normal_scale = ((offset_normal < step * 0.5) ? -offset_normal : step - offset_normal) * ((dot_normal < 0.0) ? 1.0 : -1.0);
  110. float offset_line = mod(hypot((p.x - offset.x) - origin.x, (p.y - offset.y) - origin.y), step);
  111. float line_scale = ((offset_line < step * 0.5) ? -offset_line : step - offset_line) * ((dot_line < 0.0) ? 1.0 : -1.0);
  112. return vec2(
  113. p.x - n.x * normal_scale + n.y * line_scale,
  114. p.y - n.y * normal_scale * aspect - n.x * line_scale
  115. );
  116. }
  117. void main() {
  118. if (!disable) {
  119. vec2 origin = vec2(0, 0);
  120. float step = radius / width;
  121. float radius_max = step;
  122. float aspect = height / width;
  123. float aa = (step < 1.0 / width) ? step * 0.5 : 1.0 / width;
  124. vec2 normC = vec2(cos(rC), sin(rC));
  125. vec2 normM = vec2(cos(rM), sin(rM));
  126. vec2 normY = vec2(cos(rY), sin(rY));
  127. // sampling
  128. vec2 coordC = gridReference(vUV, origin, normC, step, aspect);
  129. vec2 coordM = gridReference(vUV, origin, normM, step, aspect);
  130. vec2 coordY = gridReference(vUV, origin, normY, step, aspect);
  131. float distC = shapeRadius(texture2D(tDiffuse, coordC).r, vUV, coordC) * radius_max - shapeDistance(vUV, coordC, normC);
  132. float distM = shapeRadius(texture2D(tDiffuse, coordM).g, vUV, coordM) * radius_max - shapeDistance(vUV, coordM, normM);
  133. float distY = shapeRadius(texture2D(tDiffuse, coordY).b, vUV, coordY) * radius_max - shapeDistance(vUV, coordY, normY);
  134. float r = (distC > 0.0) ? clamp(0.0, 1.0, distC / aa) : 0.0;
  135. float g = (distM > 0.0) ? clamp(0.0, 1.0, distM / aa) : 0.0;
  136. float b = (distY > 0.0) ? clamp(0.0, 1.0, distY / aa) : 0.0;
  137. if (blending != 0.0) {
  138. vec4 colour = texture2D(tDiffuse, vUV);
  139. r = blendColour(r, colour.r, blending);
  140. g = blendColour(g, colour.g, blending);
  141. b = blendColour(b, colour.b, blending);
  142. }
  143. if (greyscale) {
  144. r = (r + b + g) / 3.0;
  145. g = r;
  146. b = r;
  147. }
  148. // write
  149. gl_FragColor = vec4(r, g, b, 1.0);
  150. } else {
  151. gl_FragColor = texture2D(tDiffuse, vUV);
  152. }
  153. }`
  154. };