@basementstudio/shader-lab 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/.biome/plugins/README.md +21 -0
  2. package/.biome/plugins/no-anchor-element.grit +12 -0
  3. package/.biome/plugins/no-relative-parent-imports.grit +10 -0
  4. package/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  5. package/.changeset/README.md +17 -0
  6. package/.changeset/config.json +11 -0
  7. package/.editorconfig +40 -0
  8. package/.env.example +81 -0
  9. package/.gitattributes +19 -0
  10. package/.github/workflows/canary.yml +80 -0
  11. package/.github/workflows/ci.yml +37 -0
  12. package/.github/workflows/release.yml +56 -0
  13. package/.tldrignore +84 -0
  14. package/.vscode/extensions.json +20 -0
  15. package/.vscode/settings.json +105 -0
  16. package/README.md +119 -0
  17. package/biome.json +249 -0
  18. package/bun.lock +1224 -0
  19. package/next.config.ts +131 -0
  20. package/package.json +73 -0
  21. package/packages/shader-lab-react/CHANGELOG.md +9 -0
  22. package/packages/shader-lab-react/README.md +119 -0
  23. package/packages/shader-lab-react/assets/patterns/bars/1.svg +3 -0
  24. package/packages/shader-lab-react/assets/patterns/bars/2.svg +3 -0
  25. package/packages/shader-lab-react/assets/patterns/bars/3.svg +3 -0
  26. package/packages/shader-lab-react/assets/patterns/bars/4.svg +3 -0
  27. package/packages/shader-lab-react/assets/patterns/bars/5.svg +3 -0
  28. package/packages/shader-lab-react/assets/patterns/bars/6.svg +3 -0
  29. package/packages/shader-lab-react/assets/patterns/candles/1.svg +3 -0
  30. package/packages/shader-lab-react/assets/patterns/candles/2.svg +3 -0
  31. package/packages/shader-lab-react/assets/patterns/candles/3.svg +3 -0
  32. package/packages/shader-lab-react/assets/patterns/candles/4.svg +3 -0
  33. package/packages/shader-lab-react/assets/patterns/shapes/1.svg +3 -0
  34. package/packages/shader-lab-react/assets/patterns/shapes/2.svg +3 -0
  35. package/packages/shader-lab-react/assets/patterns/shapes/3.svg +3 -0
  36. package/packages/shader-lab-react/assets/patterns/shapes/4.svg +4 -0
  37. package/packages/shader-lab-react/assets/patterns/shapes/5.svg +3 -0
  38. package/packages/shader-lab-react/assets/patterns/shapes/6.svg +4 -0
  39. package/packages/shader-lab-react/assets/textures/blue-noise.png +0 -0
  40. package/packages/shader-lab-react/package.json +36 -0
  41. package/packages/shader-lab-react/scripts/fix-esm-specifiers.mjs +57 -0
  42. package/packages/shader-lab-react/scripts/prepare-dist.mjs +4 -0
  43. package/packages/shader-lab-react/src/ambient/three-tsl.d.ts +146 -0
  44. package/packages/shader-lab-react/src/ambient/three-webgpu.d.ts +51 -0
  45. package/packages/shader-lab-react/src/easings.ts +4 -0
  46. package/packages/shader-lab-react/src/index.ts +35 -0
  47. package/packages/shader-lab-react/src/lib/editor/custom-shader/shared.ts +2 -0
  48. package/packages/shader-lab-react/src/renderer/ascii-atlas.ts +83 -0
  49. package/packages/shader-lab-react/src/renderer/ascii-pass.ts +416 -0
  50. package/packages/shader-lab-react/src/renderer/asset-url.ts +3 -0
  51. package/packages/shader-lab-react/src/renderer/blend-modes.ts +229 -0
  52. package/packages/shader-lab-react/src/renderer/contracts.ts +54 -0
  53. package/packages/shader-lab-react/src/renderer/create-webgpu-renderer.ts +48 -0
  54. package/packages/shader-lab-react/src/renderer/crt-pass.ts +1040 -0
  55. package/packages/shader-lab-react/src/renderer/custom-shader-pass.ts +108 -0
  56. package/packages/shader-lab-react/src/renderer/custom-shader-runtime.ts +309 -0
  57. package/packages/shader-lab-react/src/renderer/dither-textures.ts +99 -0
  58. package/packages/shader-lab-react/src/renderer/dithering-pass.ts +322 -0
  59. package/packages/shader-lab-react/src/renderer/gradient-pass.ts +521 -0
  60. package/packages/shader-lab-react/src/renderer/halftone-pass.ts +932 -0
  61. package/packages/shader-lab-react/src/renderer/ink-pass.ts +802 -0
  62. package/packages/shader-lab-react/src/renderer/live-pass.ts +194 -0
  63. package/packages/shader-lab-react/src/renderer/media-pass.ts +187 -0
  64. package/packages/shader-lab-react/src/renderer/media-texture.ts +66 -0
  65. package/packages/shader-lab-react/src/renderer/particle-grid-pass.ts +389 -0
  66. package/packages/shader-lab-react/src/renderer/pass-node.ts +209 -0
  67. package/packages/shader-lab-react/src/renderer/pattern-atlas.ts +133 -0
  68. package/packages/shader-lab-react/src/renderer/pattern-pass.ts +552 -0
  69. package/packages/shader-lab-react/src/renderer/pipeline-manager.ts +369 -0
  70. package/packages/shader-lab-react/src/renderer/pixel-sorting-pass.ts +277 -0
  71. package/packages/shader-lab-react/src/renderer/shaders/tsl/color/tonemapping.ts +87 -0
  72. package/packages/shader-lab-react/src/renderer/shaders/tsl/cosine-palette.ts +9 -0
  73. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/common.ts +31 -0
  74. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +36 -0
  75. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +36 -0
  76. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/fbm.ts +13 -0
  77. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +96 -0
  78. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/ridge-noise.ts +24 -0
  79. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +79 -0
  80. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +89 -0
  81. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/turbulence.ts +56 -0
  82. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/value-noise-3d.ts +32 -0
  83. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +60 -0
  84. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +15 -0
  85. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom.ts +11 -0
  86. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +24 -0
  87. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +9 -0
  88. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +11 -0
  89. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/atan2.ts +9 -0
  90. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-conj.ts +9 -0
  91. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-cos.ts +10 -0
  92. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-div.ts +11 -0
  93. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-log.ts +7 -0
  94. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mobius.ts +12 -0
  95. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mul.ts +9 -0
  96. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-pow.ts +16 -0
  97. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sin.ts +10 -0
  98. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sqrt.ts +18 -0
  99. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-tan.ts +12 -0
  100. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-to-polar.ts +10 -0
  101. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/hyperbolic.ts +20 -0
  102. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/index.ts +48 -0
  103. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/rotate.ts +15 -0
  104. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +15 -0
  105. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-box-2d.ts +6 -0
  106. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-diamond.ts +6 -0
  107. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-rhombus.ts +27 -0
  108. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-sphere.ts +6 -0
  109. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  110. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smin.ts +7 -0
  111. package/packages/shader-lab-react/src/renderer/text-pass.ts +176 -0
  112. package/packages/shader-lab-react/src/runtime-clock.ts +42 -0
  113. package/packages/shader-lab-react/src/runtime-frame.ts +29 -0
  114. package/packages/shader-lab-react/src/shader-lab-composition.tsx +163 -0
  115. package/packages/shader-lab-react/src/timeline.ts +283 -0
  116. package/packages/shader-lab-react/src/types/editor.ts +5 -0
  117. package/packages/shader-lab-react/src/types.ts +141 -0
  118. package/packages/shader-lab-react/tsconfig.build.json +8 -0
  119. package/packages/shader-lab-react/tsconfig.json +21 -0
  120. package/postcss.config.mjs +5 -0
  121. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf-atlas.png +0 -0
  122. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf.json +1412 -0
  123. package/public/assets/patterns/bars/1.svg +3 -0
  124. package/public/assets/patterns/bars/2.svg +3 -0
  125. package/public/assets/patterns/bars/3.svg +3 -0
  126. package/public/assets/patterns/bars/4.svg +3 -0
  127. package/public/assets/patterns/bars/5.svg +3 -0
  128. package/public/assets/patterns/bars/6.svg +3 -0
  129. package/public/assets/patterns/candles/1.svg +3 -0
  130. package/public/assets/patterns/candles/2.svg +3 -0
  131. package/public/assets/patterns/candles/3.svg +3 -0
  132. package/public/assets/patterns/candles/4.svg +3 -0
  133. package/public/assets/patterns/shapes/1.svg +3 -0
  134. package/public/assets/patterns/shapes/2.svg +3 -0
  135. package/public/assets/patterns/shapes/3.svg +3 -0
  136. package/public/assets/patterns/shapes/4.svg +4 -0
  137. package/public/assets/patterns/shapes/5.svg +3 -0
  138. package/public/assets/patterns/shapes/6.svg +4 -0
  139. package/public/fonts/geist/Geist-Mono.woff2 +0 -0
  140. package/public/textures/blue-noise.png +0 -0
  141. package/public/textures/crt-mask.png +0 -0
  142. package/src/app/design/page.tsx +398 -0
  143. package/src/app/favicon.ico +0 -0
  144. package/src/app/globals.css +280 -0
  145. package/src/app/layout.tsx +89 -0
  146. package/src/app/page.tsx +20 -0
  147. package/src/app/robots.ts +13 -0
  148. package/src/app/sitemap.ts +13 -0
  149. package/src/components/editor/editor-canvas-viewport.tsx +116 -0
  150. package/src/components/editor/editor-export-dialog.tsx +1177 -0
  151. package/src/components/editor/editor-timeline-overlay.tsx +983 -0
  152. package/src/components/editor/editor-topbar.tsx +287 -0
  153. package/src/components/editor/layer-sidebar.tsx +738 -0
  154. package/src/components/editor/properties-sidebar-content.tsx +574 -0
  155. package/src/components/editor/properties-sidebar-fields.tsx +389 -0
  156. package/src/components/editor/properties-sidebar-utils.ts +178 -0
  157. package/src/components/editor/properties-sidebar.tsx +421 -0
  158. package/src/components/ui/button/index.tsx +57 -0
  159. package/src/components/ui/color-picker/index.tsx +358 -0
  160. package/src/components/ui/glass-panel/index.tsx +45 -0
  161. package/src/components/ui/icon-button/index.tsx +46 -0
  162. package/src/components/ui/select/index.tsx +136 -0
  163. package/src/components/ui/slider/index.tsx +192 -0
  164. package/src/components/ui/toggle/index.tsx +34 -0
  165. package/src/components/ui/typography/index.tsx +61 -0
  166. package/src/components/ui/xy-pad/index.tsx +160 -0
  167. package/src/features/editor/components/editor-export-dialog.module.css +271 -0
  168. package/src/hooks/use-editor-renderer.ts +182 -0
  169. package/src/lib/app.ts +6 -0
  170. package/src/lib/cn.ts +7 -0
  171. package/src/lib/easings.ts +240 -0
  172. package/src/lib/editor/config/layer-registry.ts +2434 -0
  173. package/src/lib/editor/custom-shader/shared.ts +28 -0
  174. package/src/lib/editor/export.ts +420 -0
  175. package/src/lib/editor/history.ts +71 -0
  176. package/src/lib/editor/layers.ts +76 -0
  177. package/src/lib/editor/parameter-schema.ts +75 -0
  178. package/src/lib/editor/project-file.ts +145 -0
  179. package/src/lib/editor/shader-export-snippet.ts +37 -0
  180. package/src/lib/editor/shader-export.ts +315 -0
  181. package/src/lib/editor/timeline/evaluate.ts +252 -0
  182. package/src/lib/editor/view-transform.ts +58 -0
  183. package/src/lib/fonts.ts +28 -0
  184. package/src/renderer/ascii-atlas.ts +83 -0
  185. package/src/renderer/ascii-pass.ts +416 -0
  186. package/src/renderer/blend-modes.ts +229 -0
  187. package/src/renderer/contracts.ts +161 -0
  188. package/src/renderer/create-webgpu-renderer.ts +48 -0
  189. package/src/renderer/crt-pass.ts +1040 -0
  190. package/src/renderer/custom-shader-pass.ts +117 -0
  191. package/src/renderer/custom-shader-runtime.ts +309 -0
  192. package/src/renderer/dither-textures.ts +99 -0
  193. package/src/renderer/dithering-pass.ts +322 -0
  194. package/src/renderer/gradient-pass.ts +520 -0
  195. package/src/renderer/halftone-pass.ts +932 -0
  196. package/src/renderer/ink-pass.ts +683 -0
  197. package/src/renderer/live-pass.ts +194 -0
  198. package/src/renderer/media-pass.ts +187 -0
  199. package/src/renderer/media-texture.ts +66 -0
  200. package/src/renderer/particle-grid-pass.ts +389 -0
  201. package/src/renderer/pass-node-factory.ts +33 -0
  202. package/src/renderer/pass-node.ts +209 -0
  203. package/src/renderer/pattern-atlas.ts +97 -0
  204. package/src/renderer/pattern-pass.ts +552 -0
  205. package/src/renderer/pipeline-manager.ts +343 -0
  206. package/src/renderer/pixel-sorting-pass.ts +277 -0
  207. package/src/renderer/project-clock.ts +57 -0
  208. package/src/renderer/shaders/tsl/color/tonemapping.ts +86 -0
  209. package/src/renderer/shaders/tsl/cosine-palette.ts +8 -0
  210. package/src/renderer/shaders/tsl/noise/common.ts +30 -0
  211. package/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +35 -0
  212. package/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +35 -0
  213. package/src/renderer/shaders/tsl/noise/fbm.ts +12 -0
  214. package/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +97 -0
  215. package/src/renderer/shaders/tsl/noise/ridge-noise.ts +23 -0
  216. package/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +78 -0
  217. package/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +88 -0
  218. package/src/renderer/shaders/tsl/noise/turbulence.ts +55 -0
  219. package/src/renderer/shaders/tsl/noise/value-noise-3d.ts +31 -0
  220. package/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +59 -0
  221. package/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +14 -0
  222. package/src/renderer/shaders/tsl/patterns/bloom.ts +10 -0
  223. package/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +23 -0
  224. package/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +8 -0
  225. package/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +10 -0
  226. package/src/renderer/shaders/tsl/utils/atan2.ts +8 -0
  227. package/src/renderer/shaders/tsl/utils/complex-conj.ts +8 -0
  228. package/src/renderer/shaders/tsl/utils/complex-cos.ts +9 -0
  229. package/src/renderer/shaders/tsl/utils/complex-div.ts +10 -0
  230. package/src/renderer/shaders/tsl/utils/complex-log.ts +6 -0
  231. package/src/renderer/shaders/tsl/utils/complex-mobius.ts +11 -0
  232. package/src/renderer/shaders/tsl/utils/complex-mul.ts +8 -0
  233. package/src/renderer/shaders/tsl/utils/complex-pow.ts +15 -0
  234. package/src/renderer/shaders/tsl/utils/complex-sin.ts +9 -0
  235. package/src/renderer/shaders/tsl/utils/complex-sqrt.ts +17 -0
  236. package/src/renderer/shaders/tsl/utils/complex-tan.ts +11 -0
  237. package/src/renderer/shaders/tsl/utils/complex-to-polar.ts +9 -0
  238. package/src/renderer/shaders/tsl/utils/hyperbolic.ts +19 -0
  239. package/src/renderer/shaders/tsl/utils/index.ts +47 -0
  240. package/src/renderer/shaders/tsl/utils/rotate.ts +14 -0
  241. package/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +14 -0
  242. package/src/renderer/shaders/tsl/utils/sd-box-2d.ts +5 -0
  243. package/src/renderer/shaders/tsl/utils/sd-diamond.ts +5 -0
  244. package/src/renderer/shaders/tsl/utils/sd-rhombus.ts +26 -0
  245. package/src/renderer/shaders/tsl/utils/sd-sphere.ts +5 -0
  246. package/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  247. package/src/renderer/shaders/tsl/utils/smin.ts +6 -0
  248. package/src/renderer/text-pass.ts +176 -0
  249. package/src/store/asset-store.ts +193 -0
  250. package/src/store/editor-store.ts +223 -0
  251. package/src/store/history-store.ts +172 -0
  252. package/src/store/index.ts +31 -0
  253. package/src/store/layer-store.ts +675 -0
  254. package/src/store/timeline-store.ts +572 -0
  255. package/src/types/assets.d.ts +6 -0
  256. package/src/types/css.d.ts +21 -0
  257. package/src/types/editor.ts +357 -0
  258. package/src/types/react.d.ts +15 -0
  259. package/src/types/three-tsl.d.ts +146 -0
  260. package/src/types/three-webgpu.d.ts +51 -0
  261. package/tsconfig.json +49 -0
@@ -0,0 +1,322 @@
1
+ import * as THREE from "three/webgpu"
2
+ import {
3
+ abs,
4
+ clamp,
5
+ float,
6
+ floor,
7
+ fract,
8
+ max,
9
+ mix,
10
+ smoothstep,
11
+ texture as tslTexture,
12
+ type TSLNode,
13
+ uniform,
14
+ uv,
15
+ vec2,
16
+ vec3,
17
+ vec4,
18
+ } from "three/tsl"
19
+ import { buildDitherTextures, type DitherTextures } from "@/renderer/dither-textures"
20
+ import { PassNode } from "@/renderer/pass-node"
21
+ import type { LayerParameterValues } from "@/types/editor"
22
+
23
+ type Node = TSLNode
24
+ type DitherColorMode = "duo-tone" | "monochrome" | "source"
25
+
26
+ function hexToRgb(hex: string): [number, number, number] {
27
+ const normalized = hex.trim().replace("#", "")
28
+ const value =
29
+ normalized.length === 3
30
+ ? normalized
31
+ .split("")
32
+ .map((entry) => `${entry}${entry}`)
33
+ .join("")
34
+ : normalized.padEnd(6, "0").slice(0, 6)
35
+
36
+ return [
37
+ Number.parseInt(value.slice(0, 2), 16) / 255,
38
+ Number.parseInt(value.slice(2, 4), 16) / 255,
39
+ Number.parseInt(value.slice(4, 6), 16) / 255,
40
+ ]
41
+ }
42
+
43
+ export class DitheringPass extends PassNode {
44
+ private colorMode: DitherColorMode = "source"
45
+ private isAnimated = false
46
+
47
+ private readonly colorBlueUniform: Node
48
+ private readonly colorGreenUniform: Node
49
+ private readonly colorRedUniform: Node
50
+ private readonly highlightBlueUniform: Node
51
+ private readonly highlightGreenUniform: Node
52
+ private readonly highlightRedUniform: Node
53
+ private readonly levelsUniform: Node
54
+ private readonly logicalHeightUniform: Node
55
+ private readonly logicalWidthUniform: Node
56
+ private readonly matrixSizeUniform: Node
57
+ private readonly pixelSizeUniform: Node
58
+ private readonly shadowBlueUniform: Node
59
+ private readonly shadowGreenUniform: Node
60
+ private readonly shadowRedUniform: Node
61
+ private readonly spreadUniform: Node
62
+ private readonly textures: DitherTextures
63
+
64
+ // Effects uniforms
65
+ private readonly dotScaleUniform: Node
66
+ private readonly animateDitherUniform: Node
67
+ private readonly ditherSpeedUniform: Node
68
+ private readonly timeUniform: Node
69
+ private readonly chromaticSplitUniform: Node
70
+
71
+ private currentTexture: THREE.DataTexture
72
+ private ditherNode: Node | null = null
73
+ private ditherNodeG: Node | null = null
74
+ private ditherNodeB: Node | null = null
75
+ private readonly placeholder: THREE.Texture
76
+ private sourceTextureNode: Node | null = null
77
+
78
+ constructor(layerId: string) {
79
+ super(layerId)
80
+ this.textures = buildDitherTextures()
81
+ this.placeholder = new THREE.Texture()
82
+ this.currentTexture = this.textures.bayer4
83
+ this.levelsUniform = uniform(4)
84
+ this.logicalWidthUniform = uniform(1)
85
+ this.logicalHeightUniform = uniform(1)
86
+ this.matrixSizeUniform = uniform(4)
87
+ this.pixelSizeUniform = uniform(1)
88
+ this.spreadUniform = uniform(0.5)
89
+ this.colorRedUniform = uniform(0.96)
90
+ this.colorGreenUniform = uniform(0.96)
91
+ this.colorBlueUniform = uniform(0.94)
92
+ this.shadowRedUniform = uniform(0.06)
93
+ this.shadowGreenUniform = uniform(0.06)
94
+ this.shadowBlueUniform = uniform(0.06)
95
+ this.highlightRedUniform = uniform(0.96)
96
+ this.highlightGreenUniform = uniform(0.95)
97
+ this.highlightBlueUniform = uniform(0.91)
98
+
99
+ // Effects uniforms
100
+ this.dotScaleUniform = uniform(1.0)
101
+ this.animateDitherUniform = uniform(0.0)
102
+ this.ditherSpeedUniform = uniform(1.0)
103
+ this.timeUniform = uniform(0.0)
104
+ this.chromaticSplitUniform = uniform(0.0)
105
+
106
+ this.rebuildEffectNode()
107
+ }
108
+
109
+ override render(
110
+ renderer: THREE.WebGPURenderer,
111
+ inputTexture: THREE.Texture,
112
+ outputTarget: THREE.WebGLRenderTarget,
113
+ time: number,
114
+ delta: number,
115
+ ): void {
116
+ if (this.sourceTextureNode) {
117
+ this.sourceTextureNode.value = inputTexture
118
+ }
119
+
120
+ if (this.ditherNode) {
121
+ this.ditherNode.value = this.currentTexture
122
+ }
123
+ if (this.ditherNodeG) {
124
+ this.ditherNodeG.value = this.currentTexture
125
+ }
126
+ if (this.ditherNodeB) {
127
+ this.ditherNodeB.value = this.currentTexture
128
+ }
129
+
130
+ super.render(renderer, inputTexture, outputTarget, time, delta)
131
+ }
132
+
133
+ protected override beforeRender(time: number, _delta: number): void {
134
+ this.timeUniform.value = time
135
+ }
136
+
137
+ override needsContinuousRender(): boolean {
138
+ return this.isAnimated
139
+ }
140
+
141
+ override updateParams(params: LayerParameterValues): void {
142
+ const nextColorMode: DitherColorMode =
143
+ params.colorMode === "monochrome" ||
144
+ params.colorMode === "duo-tone"
145
+ ? params.colorMode
146
+ : "source"
147
+
148
+ const [red, green, blue] = hexToRgb(typeof params.monoColor === "string" ? params.monoColor : "#f5f5f0")
149
+ const [shadowRed, shadowGreen, shadowBlue] = hexToRgb(
150
+ typeof params.shadowColor === "string" ? params.shadowColor : "#101010",
151
+ )
152
+ const [highlightRed, highlightGreen, highlightBlue] = hexToRgb(
153
+ typeof params.highlightColor === "string" ? params.highlightColor : "#f5f2e8",
154
+ )
155
+
156
+ this.colorRedUniform.value = red
157
+ this.colorGreenUniform.value = green
158
+ this.colorBlueUniform.value = blue
159
+ this.shadowRedUniform.value = shadowRed
160
+ this.shadowGreenUniform.value = shadowGreen
161
+ this.shadowBlueUniform.value = shadowBlue
162
+ this.highlightRedUniform.value = highlightRed
163
+ this.highlightGreenUniform.value = highlightGreen
164
+ this.highlightBlueUniform.value = highlightBlue
165
+ this.levelsUniform.value =
166
+ typeof params.levels === "number" ? Math.max(2, params.levels) : 4
167
+ this.pixelSizeUniform.value =
168
+ typeof params.pixelSize === "number" ? Math.max(1, Math.round(params.pixelSize)) : 1
169
+ this.spreadUniform.value =
170
+ typeof params.spread === "number"
171
+ ? Math.max(0, Math.min(1, params.spread))
172
+ : 0.5
173
+
174
+ switch (params.algorithm) {
175
+ case "bayer-2x2":
176
+ this.currentTexture = this.textures.bayer2
177
+ this.matrixSizeUniform.value = 2
178
+ break
179
+ case "bayer-8x8":
180
+ this.currentTexture = this.textures.bayer8
181
+ this.matrixSizeUniform.value = 8
182
+ break
183
+ case "noise":
184
+ this.currentTexture = this.textures.noise
185
+ this.matrixSizeUniform.value = 64
186
+ break
187
+ default:
188
+ this.currentTexture = this.textures.bayer4
189
+ this.matrixSizeUniform.value = 4
190
+ break
191
+ }
192
+
193
+ // Effects params
194
+ this.dotScaleUniform.value =
195
+ typeof params.dotScale === "number" ? params.dotScale : 1.0
196
+ this.isAnimated = params.animateDither === true
197
+ this.animateDitherUniform.value = this.isAnimated ? 1.0 : 0.0
198
+ this.ditherSpeedUniform.value =
199
+ typeof params.ditherSpeed === "number" ? params.ditherSpeed : 1.0
200
+ this.chromaticSplitUniform.value = params.chromaticSplit === true ? 1.0 : 0.0
201
+
202
+ if (nextColorMode !== this.colorMode) {
203
+ this.colorMode = nextColorMode
204
+ this.rebuildEffectNode()
205
+ }
206
+ }
207
+
208
+ override dispose(): void {
209
+ this.placeholder.dispose()
210
+ this.textures.bayer2.dispose()
211
+ this.textures.bayer4.dispose()
212
+ this.textures.bayer8.dispose()
213
+ this.textures.noise.dispose()
214
+ super.dispose()
215
+ }
216
+
217
+ override updateLogicalSize(width: number, height: number): void {
218
+ this.logicalWidthUniform.value = Math.max(1, width)
219
+ this.logicalHeightUniform.value = Math.max(1, height)
220
+ }
221
+
222
+ protected override buildEffectNode(): Node {
223
+ if (!(this.levelsUniform && this.matrixSizeUniform)) {
224
+ return this.inputNode
225
+ }
226
+
227
+ const pixelSize = max(this.pixelSizeUniform, float(1))
228
+ const renderTargetUv = vec2(uv().x, float(1).sub(uv().y))
229
+ const logicalWidth = max(this.logicalWidthUniform.div(pixelSize), float(1))
230
+ const logicalHeight = max(this.logicalHeightUniform.div(pixelSize), float(1))
231
+ const logicalDims = vec2(logicalWidth, logicalHeight)
232
+
233
+ // Cell grid
234
+ const cellCoordinates = vec2(
235
+ floor(renderTargetUv.x.mul(logicalWidth)),
236
+ floor(renderTargetUv.y.mul(logicalHeight)),
237
+ )
238
+ const snappedUv = vec2(
239
+ cellCoordinates.x.add(0.5).div(logicalWidth),
240
+ cellCoordinates.y.add(0.5).div(logicalHeight),
241
+ )
242
+
243
+ // Animated Dither: shift ditherUv by time * speed when animate is on
244
+ const timeOffset = this.timeUniform.mul(this.ditherSpeedUniform).mul(this.animateDitherUniform)
245
+ const ditherUv = cellCoordinates.div(this.matrixSizeUniform).add(timeOffset)
246
+
247
+ // Chromatic Split: sample dither at 3 offset UVs when enabled
248
+ const splitOffset = this.chromaticSplitUniform.div(this.matrixSizeUniform)
249
+ const ditherUvR = ditherUv
250
+ const ditherUvG = ditherUv.add(vec2(splitOffset, float(0)))
251
+ const ditherUvB = ditherUv.add(vec2(float(0), splitOffset))
252
+
253
+ this.sourceTextureNode = tslTexture(this.placeholder, snappedUv)
254
+ this.ditherNode = tslTexture(this.currentTexture, ditherUvR)
255
+ this.ditherNodeG = tslTexture(this.currentTexture, ditherUvG)
256
+ this.ditherNodeB = tslTexture(this.currentTexture, ditherUvB)
257
+
258
+ const src = this.sourceTextureNode
259
+ const thresholdR = float(this.ditherNode.r).sub(float(0.5))
260
+ const thresholdG = float(this.ditherNodeG.r).sub(float(0.5))
261
+ const thresholdB = float(this.ditherNodeB.r).sub(float(0.5))
262
+ const levelsMinusOne = max(this.levelsUniform.sub(float(1)), float(1))
263
+
264
+ // Per-channel quantization with independent thresholds
265
+ const adjustedR = float(src.r).add(thresholdR.mul(this.spreadUniform))
266
+ const adjustedG = float(src.g).add(thresholdG.mul(this.spreadUniform))
267
+ const adjustedB = float(src.b).add(thresholdB.mul(this.spreadUniform))
268
+ const quantizedColor = clamp(
269
+ vec3(
270
+ floor(adjustedR.mul(levelsMinusOne).add(0.5)).div(levelsMinusOne),
271
+ floor(adjustedG.mul(levelsMinusOne).add(0.5)).div(levelsMinusOne),
272
+ floor(adjustedB.mul(levelsMinusOne).add(0.5)).div(levelsMinusOne),
273
+ ),
274
+ vec3(float(0), float(0), float(0)),
275
+ vec3(float(1), float(1), float(1)),
276
+ )
277
+
278
+ // Color mode
279
+ const quantizedLuma = float(quantizedColor.x)
280
+ .mul(float(0.2126))
281
+ .add(float(quantizedColor.y).mul(float(0.7152)))
282
+ .add(float(quantizedColor.z).mul(float(0.0722)))
283
+
284
+ const monoTint = vec3(
285
+ this.colorRedUniform,
286
+ this.colorGreenUniform,
287
+ this.colorBlueUniform,
288
+ )
289
+ const shadowTint = vec3(
290
+ this.shadowRedUniform,
291
+ this.shadowGreenUniform,
292
+ this.shadowBlueUniform,
293
+ )
294
+ const highlightTint = vec3(
295
+ this.highlightRedUniform,
296
+ this.highlightGreenUniform,
297
+ this.highlightBlueUniform,
298
+ )
299
+
300
+ let colorResult: Node
301
+ switch (this.colorMode) {
302
+ case "monochrome":
303
+ colorResult = vec3(quantizedLuma, quantizedLuma, quantizedLuma).mul(monoTint)
304
+ break
305
+ case "duo-tone":
306
+ colorResult = mix(shadowTint, highlightTint, quantizedLuma)
307
+ break
308
+ default:
309
+ colorResult = quantizedColor
310
+ break
311
+ }
312
+
313
+ // Dot Scale: mask within each cell (square shape only)
314
+ const cellFrac = fract(renderTargetUv.mul(logicalDims))
315
+ const centered = cellFrac.sub(vec2(0.5, 0.5))
316
+ const dist = max(abs(centered.x), abs(centered.y))
317
+ const halfSize = float(0.5).mul(this.dotScaleUniform)
318
+ const mask = smoothstep(halfSize, halfSize.sub(float(0.01)), dist)
319
+
320
+ return vec4(vec3(colorResult).mul(mask), float(1))
321
+ }
322
+ }