@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.
- package/.biome/plugins/README.md +21 -0
- package/.biome/plugins/no-anchor-element.grit +12 -0
- package/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/.changeset/README.md +17 -0
- package/.changeset/config.json +11 -0
- package/.editorconfig +40 -0
- package/.env.example +81 -0
- package/.gitattributes +19 -0
- package/.github/workflows/canary.yml +80 -0
- package/.github/workflows/ci.yml +37 -0
- package/.github/workflows/release.yml +56 -0
- package/.tldrignore +84 -0
- package/.vscode/extensions.json +20 -0
- package/.vscode/settings.json +105 -0
- package/README.md +119 -0
- package/biome.json +249 -0
- package/bun.lock +1224 -0
- package/next.config.ts +131 -0
- package/package.json +73 -0
- package/packages/shader-lab-react/CHANGELOG.md +9 -0
- package/packages/shader-lab-react/README.md +119 -0
- package/packages/shader-lab-react/assets/patterns/bars/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/4.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/5.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/6.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/4.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/4.svg +4 -0
- package/packages/shader-lab-react/assets/patterns/shapes/5.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/6.svg +4 -0
- package/packages/shader-lab-react/assets/textures/blue-noise.png +0 -0
- package/packages/shader-lab-react/package.json +36 -0
- package/packages/shader-lab-react/scripts/fix-esm-specifiers.mjs +57 -0
- package/packages/shader-lab-react/scripts/prepare-dist.mjs +4 -0
- package/packages/shader-lab-react/src/ambient/three-tsl.d.ts +146 -0
- package/packages/shader-lab-react/src/ambient/three-webgpu.d.ts +51 -0
- package/packages/shader-lab-react/src/easings.ts +4 -0
- package/packages/shader-lab-react/src/index.ts +35 -0
- package/packages/shader-lab-react/src/lib/editor/custom-shader/shared.ts +2 -0
- package/packages/shader-lab-react/src/renderer/ascii-atlas.ts +83 -0
- package/packages/shader-lab-react/src/renderer/ascii-pass.ts +416 -0
- package/packages/shader-lab-react/src/renderer/asset-url.ts +3 -0
- package/packages/shader-lab-react/src/renderer/blend-modes.ts +229 -0
- package/packages/shader-lab-react/src/renderer/contracts.ts +54 -0
- package/packages/shader-lab-react/src/renderer/create-webgpu-renderer.ts +48 -0
- package/packages/shader-lab-react/src/renderer/crt-pass.ts +1040 -0
- package/packages/shader-lab-react/src/renderer/custom-shader-pass.ts +108 -0
- package/packages/shader-lab-react/src/renderer/custom-shader-runtime.ts +309 -0
- package/packages/shader-lab-react/src/renderer/dither-textures.ts +99 -0
- package/packages/shader-lab-react/src/renderer/dithering-pass.ts +322 -0
- package/packages/shader-lab-react/src/renderer/gradient-pass.ts +521 -0
- package/packages/shader-lab-react/src/renderer/halftone-pass.ts +932 -0
- package/packages/shader-lab-react/src/renderer/ink-pass.ts +802 -0
- package/packages/shader-lab-react/src/renderer/live-pass.ts +194 -0
- package/packages/shader-lab-react/src/renderer/media-pass.ts +187 -0
- package/packages/shader-lab-react/src/renderer/media-texture.ts +66 -0
- package/packages/shader-lab-react/src/renderer/particle-grid-pass.ts +389 -0
- package/packages/shader-lab-react/src/renderer/pass-node.ts +209 -0
- package/packages/shader-lab-react/src/renderer/pattern-atlas.ts +133 -0
- package/packages/shader-lab-react/src/renderer/pattern-pass.ts +552 -0
- package/packages/shader-lab-react/src/renderer/pipeline-manager.ts +369 -0
- package/packages/shader-lab-react/src/renderer/pixel-sorting-pass.ts +277 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/color/tonemapping.ts +87 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/cosine-palette.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/common.ts +31 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +36 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +36 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/fbm.ts +13 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +96 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/ridge-noise.ts +24 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +79 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +89 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/turbulence.ts +56 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/value-noise-3d.ts +32 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +60 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +24 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/atan2.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-conj.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-cos.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-div.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-log.ts +7 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mobius.ts +12 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mul.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-pow.ts +16 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sin.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sqrt.ts +18 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-tan.ts +12 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-to-polar.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/hyperbolic.ts +20 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/index.ts +48 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/rotate.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-box-2d.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-diamond.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-rhombus.ts +27 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-sphere.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smax.ts +7 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smin.ts +7 -0
- package/packages/shader-lab-react/src/renderer/text-pass.ts +176 -0
- package/packages/shader-lab-react/src/runtime-clock.ts +42 -0
- package/packages/shader-lab-react/src/runtime-frame.ts +29 -0
- package/packages/shader-lab-react/src/shader-lab-composition.tsx +163 -0
- package/packages/shader-lab-react/src/timeline.ts +283 -0
- package/packages/shader-lab-react/src/types/editor.ts +5 -0
- package/packages/shader-lab-react/src/types.ts +141 -0
- package/packages/shader-lab-react/tsconfig.build.json +8 -0
- package/packages/shader-lab-react/tsconfig.json +21 -0
- package/postcss.config.mjs +5 -0
- package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf-atlas.png +0 -0
- package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf.json +1412 -0
- package/public/assets/patterns/bars/1.svg +3 -0
- package/public/assets/patterns/bars/2.svg +3 -0
- package/public/assets/patterns/bars/3.svg +3 -0
- package/public/assets/patterns/bars/4.svg +3 -0
- package/public/assets/patterns/bars/5.svg +3 -0
- package/public/assets/patterns/bars/6.svg +3 -0
- package/public/assets/patterns/candles/1.svg +3 -0
- package/public/assets/patterns/candles/2.svg +3 -0
- package/public/assets/patterns/candles/3.svg +3 -0
- package/public/assets/patterns/candles/4.svg +3 -0
- package/public/assets/patterns/shapes/1.svg +3 -0
- package/public/assets/patterns/shapes/2.svg +3 -0
- package/public/assets/patterns/shapes/3.svg +3 -0
- package/public/assets/patterns/shapes/4.svg +4 -0
- package/public/assets/patterns/shapes/5.svg +3 -0
- package/public/assets/patterns/shapes/6.svg +4 -0
- package/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/public/textures/blue-noise.png +0 -0
- package/public/textures/crt-mask.png +0 -0
- package/src/app/design/page.tsx +398 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +280 -0
- package/src/app/layout.tsx +89 -0
- package/src/app/page.tsx +20 -0
- package/src/app/robots.ts +13 -0
- package/src/app/sitemap.ts +13 -0
- package/src/components/editor/editor-canvas-viewport.tsx +116 -0
- package/src/components/editor/editor-export-dialog.tsx +1177 -0
- package/src/components/editor/editor-timeline-overlay.tsx +983 -0
- package/src/components/editor/editor-topbar.tsx +287 -0
- package/src/components/editor/layer-sidebar.tsx +738 -0
- package/src/components/editor/properties-sidebar-content.tsx +574 -0
- package/src/components/editor/properties-sidebar-fields.tsx +389 -0
- package/src/components/editor/properties-sidebar-utils.ts +178 -0
- package/src/components/editor/properties-sidebar.tsx +421 -0
- package/src/components/ui/button/index.tsx +57 -0
- package/src/components/ui/color-picker/index.tsx +358 -0
- package/src/components/ui/glass-panel/index.tsx +45 -0
- package/src/components/ui/icon-button/index.tsx +46 -0
- package/src/components/ui/select/index.tsx +136 -0
- package/src/components/ui/slider/index.tsx +192 -0
- package/src/components/ui/toggle/index.tsx +34 -0
- package/src/components/ui/typography/index.tsx +61 -0
- package/src/components/ui/xy-pad/index.tsx +160 -0
- package/src/features/editor/components/editor-export-dialog.module.css +271 -0
- package/src/hooks/use-editor-renderer.ts +182 -0
- package/src/lib/app.ts +6 -0
- package/src/lib/cn.ts +7 -0
- package/src/lib/easings.ts +240 -0
- package/src/lib/editor/config/layer-registry.ts +2434 -0
- package/src/lib/editor/custom-shader/shared.ts +28 -0
- package/src/lib/editor/export.ts +420 -0
- package/src/lib/editor/history.ts +71 -0
- package/src/lib/editor/layers.ts +76 -0
- package/src/lib/editor/parameter-schema.ts +75 -0
- package/src/lib/editor/project-file.ts +145 -0
- package/src/lib/editor/shader-export-snippet.ts +37 -0
- package/src/lib/editor/shader-export.ts +315 -0
- package/src/lib/editor/timeline/evaluate.ts +252 -0
- package/src/lib/editor/view-transform.ts +58 -0
- package/src/lib/fonts.ts +28 -0
- package/src/renderer/ascii-atlas.ts +83 -0
- package/src/renderer/ascii-pass.ts +416 -0
- package/src/renderer/blend-modes.ts +229 -0
- package/src/renderer/contracts.ts +161 -0
- package/src/renderer/create-webgpu-renderer.ts +48 -0
- package/src/renderer/crt-pass.ts +1040 -0
- package/src/renderer/custom-shader-pass.ts +117 -0
- package/src/renderer/custom-shader-runtime.ts +309 -0
- package/src/renderer/dither-textures.ts +99 -0
- package/src/renderer/dithering-pass.ts +322 -0
- package/src/renderer/gradient-pass.ts +520 -0
- package/src/renderer/halftone-pass.ts +932 -0
- package/src/renderer/ink-pass.ts +683 -0
- package/src/renderer/live-pass.ts +194 -0
- package/src/renderer/media-pass.ts +187 -0
- package/src/renderer/media-texture.ts +66 -0
- package/src/renderer/particle-grid-pass.ts +389 -0
- package/src/renderer/pass-node-factory.ts +33 -0
- package/src/renderer/pass-node.ts +209 -0
- package/src/renderer/pattern-atlas.ts +97 -0
- package/src/renderer/pattern-pass.ts +552 -0
- package/src/renderer/pipeline-manager.ts +343 -0
- package/src/renderer/pixel-sorting-pass.ts +277 -0
- package/src/renderer/project-clock.ts +57 -0
- package/src/renderer/shaders/tsl/color/tonemapping.ts +86 -0
- package/src/renderer/shaders/tsl/cosine-palette.ts +8 -0
- package/src/renderer/shaders/tsl/noise/common.ts +30 -0
- package/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +35 -0
- package/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +35 -0
- package/src/renderer/shaders/tsl/noise/fbm.ts +12 -0
- package/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +97 -0
- package/src/renderer/shaders/tsl/noise/ridge-noise.ts +23 -0
- package/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +78 -0
- package/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +88 -0
- package/src/renderer/shaders/tsl/noise/turbulence.ts +55 -0
- package/src/renderer/shaders/tsl/noise/value-noise-3d.ts +31 -0
- package/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +59 -0
- package/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +14 -0
- package/src/renderer/shaders/tsl/patterns/bloom.ts +10 -0
- package/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +23 -0
- package/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +8 -0
- package/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +10 -0
- package/src/renderer/shaders/tsl/utils/atan2.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-conj.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-cos.ts +9 -0
- package/src/renderer/shaders/tsl/utils/complex-div.ts +10 -0
- package/src/renderer/shaders/tsl/utils/complex-log.ts +6 -0
- package/src/renderer/shaders/tsl/utils/complex-mobius.ts +11 -0
- package/src/renderer/shaders/tsl/utils/complex-mul.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-pow.ts +15 -0
- package/src/renderer/shaders/tsl/utils/complex-sin.ts +9 -0
- package/src/renderer/shaders/tsl/utils/complex-sqrt.ts +17 -0
- package/src/renderer/shaders/tsl/utils/complex-tan.ts +11 -0
- package/src/renderer/shaders/tsl/utils/complex-to-polar.ts +9 -0
- package/src/renderer/shaders/tsl/utils/hyperbolic.ts +19 -0
- package/src/renderer/shaders/tsl/utils/index.ts +47 -0
- package/src/renderer/shaders/tsl/utils/rotate.ts +14 -0
- package/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +14 -0
- package/src/renderer/shaders/tsl/utils/sd-box-2d.ts +5 -0
- package/src/renderer/shaders/tsl/utils/sd-diamond.ts +5 -0
- package/src/renderer/shaders/tsl/utils/sd-rhombus.ts +26 -0
- package/src/renderer/shaders/tsl/utils/sd-sphere.ts +5 -0
- package/src/renderer/shaders/tsl/utils/smax.ts +7 -0
- package/src/renderer/shaders/tsl/utils/smin.ts +6 -0
- package/src/renderer/text-pass.ts +176 -0
- package/src/store/asset-store.ts +193 -0
- package/src/store/editor-store.ts +223 -0
- package/src/store/history-store.ts +172 -0
- package/src/store/index.ts +31 -0
- package/src/store/layer-store.ts +675 -0
- package/src/store/timeline-store.ts +572 -0
- package/src/types/assets.d.ts +6 -0
- package/src/types/css.d.ts +21 -0
- package/src/types/editor.ts +357 -0
- package/src/types/react.d.ts +15 -0
- package/src/types/three-tsl.d.ts +146 -0
- package/src/types/three-webgpu.d.ts +51 -0
- package/tsconfig.json +49 -0
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
import { bloom } from "three/examples/jsm/tsl/display/BloomNode.js"
|
|
2
|
+
import {
|
|
3
|
+
abs,
|
|
4
|
+
clamp,
|
|
5
|
+
cos,
|
|
6
|
+
float,
|
|
7
|
+
floor,
|
|
8
|
+
max,
|
|
9
|
+
min,
|
|
10
|
+
mix,
|
|
11
|
+
screenSize,
|
|
12
|
+
select,
|
|
13
|
+
sin,
|
|
14
|
+
smoothstep,
|
|
15
|
+
type TSLNode,
|
|
16
|
+
texture as tslTexture,
|
|
17
|
+
uniform,
|
|
18
|
+
uv,
|
|
19
|
+
vec2,
|
|
20
|
+
vec3,
|
|
21
|
+
vec4,
|
|
22
|
+
} from "three/tsl"
|
|
23
|
+
import * as THREE from "three/webgpu"
|
|
24
|
+
import { PassNode } from "./pass-node"
|
|
25
|
+
import { grainTexturePattern } from "./shaders/tsl/patterns/grain-texture-pattern"
|
|
26
|
+
import type { LayerParameterValues } from "../types/editor"
|
|
27
|
+
|
|
28
|
+
type Node = TSLNode
|
|
29
|
+
type HalftoneColorMode = "cmyk" | "custom" | "duotone" | "monochrome" | "source"
|
|
30
|
+
type CmykBlendMode = "overprint" | "subtractive"
|
|
31
|
+
|
|
32
|
+
function clamp01(value: number): number {
|
|
33
|
+
return Math.max(0, Math.min(1, value))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hexToRgb(hex: string): [number, number, number] {
|
|
37
|
+
const normalized = hex.trim().replace("#", "")
|
|
38
|
+
const value =
|
|
39
|
+
normalized.length === 3
|
|
40
|
+
? normalized
|
|
41
|
+
.split("")
|
|
42
|
+
.map((entry) => `${entry}${entry}`)
|
|
43
|
+
.join("")
|
|
44
|
+
: normalized.padEnd(6, "0").slice(0, 6)
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
Number.parseInt(value.slice(0, 2), 16) / 255,
|
|
48
|
+
Number.parseInt(value.slice(2, 4), 16) / 255,
|
|
49
|
+
Number.parseInt(value.slice(4, 6), 16) / 255,
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class HalftonePass extends PassNode {
|
|
54
|
+
private bloomEnabled = false
|
|
55
|
+
private bloomNode: ReturnType<typeof bloom> | null = null
|
|
56
|
+
private colorMode: HalftoneColorMode = "cmyk"
|
|
57
|
+
private cmykBlendMode: CmykBlendMode = "subtractive"
|
|
58
|
+
|
|
59
|
+
private readonly spacingUniform: Node
|
|
60
|
+
private readonly dotSizeUniform: Node
|
|
61
|
+
private readonly dotMinUniform: Node
|
|
62
|
+
private readonly shapeUniform: Node
|
|
63
|
+
private readonly angleUniform: Node
|
|
64
|
+
private readonly contrastUniform: Node
|
|
65
|
+
private readonly softnessUniform: Node
|
|
66
|
+
private readonly invertUniform: Node
|
|
67
|
+
|
|
68
|
+
private readonly inkRedUniform: Node
|
|
69
|
+
private readonly inkGreenUniform: Node
|
|
70
|
+
private readonly inkBlueUniform: Node
|
|
71
|
+
|
|
72
|
+
private readonly duotoneLightUniform: Node
|
|
73
|
+
private readonly duotoneDarkUniform: Node
|
|
74
|
+
|
|
75
|
+
private readonly customBgColorUniform: Node
|
|
76
|
+
private readonly customColorCountUniform: Node
|
|
77
|
+
private readonly customLuminanceBiasUniform: Node
|
|
78
|
+
private readonly customColor1Uniform: Node
|
|
79
|
+
private readonly customColor2Uniform: Node
|
|
80
|
+
private readonly customColor3Uniform: Node
|
|
81
|
+
private readonly customColor4Uniform: Node
|
|
82
|
+
private readonly bloomIntensityUniform: Node
|
|
83
|
+
private readonly bloomRadiusUniform: Node
|
|
84
|
+
private readonly bloomSoftnessUniform: Node
|
|
85
|
+
private readonly bloomThresholdUniform: Node
|
|
86
|
+
|
|
87
|
+
private readonly cyanAngleUniform: Node
|
|
88
|
+
private readonly magentaAngleUniform: Node
|
|
89
|
+
private readonly yellowAngleUniform: Node
|
|
90
|
+
private readonly keyAngleUniform: Node
|
|
91
|
+
|
|
92
|
+
private readonly paperRedUniform: Node
|
|
93
|
+
private readonly paperGreenUniform: Node
|
|
94
|
+
private readonly paperBlueUniform: Node
|
|
95
|
+
private readonly paperGrainUniform: Node
|
|
96
|
+
private readonly gcrUniform: Node
|
|
97
|
+
private readonly registrationUniform: Node
|
|
98
|
+
|
|
99
|
+
private readonly inkCyanUniform: Node
|
|
100
|
+
private readonly inkMagentaUniform: Node
|
|
101
|
+
private readonly inkYellowUniform: Node
|
|
102
|
+
private readonly inkKeyUniform: Node
|
|
103
|
+
|
|
104
|
+
private readonly dotGainUniform: Node
|
|
105
|
+
private readonly dotMorphUniform: Node
|
|
106
|
+
|
|
107
|
+
private sampleNodes: Node[] = []
|
|
108
|
+
|
|
109
|
+
constructor(layerId: string) {
|
|
110
|
+
super(layerId)
|
|
111
|
+
|
|
112
|
+
this.spacingUniform = uniform(5)
|
|
113
|
+
this.dotSizeUniform = uniform(1.0)
|
|
114
|
+
this.dotMinUniform = uniform(0.0)
|
|
115
|
+
this.shapeUniform = uniform(0)
|
|
116
|
+
this.angleUniform = uniform((28 * Math.PI) / 180)
|
|
117
|
+
this.contrastUniform = uniform(1.0)
|
|
118
|
+
this.softnessUniform = uniform(0.25)
|
|
119
|
+
this.invertUniform = uniform(0)
|
|
120
|
+
|
|
121
|
+
const [inkR, inkG, inkB] = hexToRgb("#0d1014")
|
|
122
|
+
this.inkRedUniform = uniform(inkR)
|
|
123
|
+
this.inkGreenUniform = uniform(inkG)
|
|
124
|
+
this.inkBlueUniform = uniform(inkB)
|
|
125
|
+
|
|
126
|
+
this.duotoneLightUniform = uniform(new THREE.Vector3(0.96, 0.96, 0.94))
|
|
127
|
+
this.duotoneDarkUniform = uniform(new THREE.Vector3(0.11, 0.11, 0.11))
|
|
128
|
+
|
|
129
|
+
const [customBgR, customBgG, customBgB] = hexToRgb("#F5F5F0")
|
|
130
|
+
this.customBgColorUniform = uniform(
|
|
131
|
+
new THREE.Vector3(customBgR, customBgG, customBgB)
|
|
132
|
+
)
|
|
133
|
+
this.customColorCountUniform = uniform(4)
|
|
134
|
+
this.customLuminanceBiasUniform = uniform(0)
|
|
135
|
+
|
|
136
|
+
const [custom1R, custom1G, custom1B] = hexToRgb("#161616")
|
|
137
|
+
this.customColor1Uniform = uniform(
|
|
138
|
+
new THREE.Vector3(custom1R, custom1G, custom1B)
|
|
139
|
+
)
|
|
140
|
+
const [custom2R, custom2G, custom2B] = hexToRgb("#595959")
|
|
141
|
+
this.customColor2Uniform = uniform(
|
|
142
|
+
new THREE.Vector3(custom2R, custom2G, custom2B)
|
|
143
|
+
)
|
|
144
|
+
const [custom3R, custom3G, custom3B] = hexToRgb("#A0A0A0")
|
|
145
|
+
this.customColor3Uniform = uniform(
|
|
146
|
+
new THREE.Vector3(custom3R, custom3G, custom3B)
|
|
147
|
+
)
|
|
148
|
+
const [custom4R, custom4G, custom4B] = hexToRgb("#E8E8E8")
|
|
149
|
+
this.customColor4Uniform = uniform(
|
|
150
|
+
new THREE.Vector3(custom4R, custom4G, custom4B)
|
|
151
|
+
)
|
|
152
|
+
this.bloomIntensityUniform = uniform(1.25)
|
|
153
|
+
this.bloomRadiusUniform = uniform(6)
|
|
154
|
+
this.bloomSoftnessUniform = uniform(0.35)
|
|
155
|
+
this.bloomThresholdUniform = uniform(0.6)
|
|
156
|
+
|
|
157
|
+
this.cyanAngleUniform = uniform((15 * Math.PI) / 180)
|
|
158
|
+
this.magentaAngleUniform = uniform((75 * Math.PI) / 180)
|
|
159
|
+
this.yellowAngleUniform = uniform(0)
|
|
160
|
+
this.keyAngleUniform = uniform((45 * Math.PI) / 180)
|
|
161
|
+
|
|
162
|
+
const [paperR, paperG, paperB] = hexToRgb("#F5F5F0")
|
|
163
|
+
this.paperRedUniform = uniform(paperR)
|
|
164
|
+
this.paperGreenUniform = uniform(paperG)
|
|
165
|
+
this.paperBlueUniform = uniform(paperB)
|
|
166
|
+
this.paperGrainUniform = uniform(0.15)
|
|
167
|
+
this.gcrUniform = uniform(0.5)
|
|
168
|
+
this.registrationUniform = uniform(0)
|
|
169
|
+
|
|
170
|
+
const [cyanR, cyanG, cyanB] = hexToRgb("#00AEEF")
|
|
171
|
+
this.inkCyanUniform = uniform(new THREE.Vector3(cyanR, cyanG, cyanB))
|
|
172
|
+
const [magR, magG, magB] = hexToRgb("#EC008C")
|
|
173
|
+
this.inkMagentaUniform = uniform(new THREE.Vector3(magR, magG, magB))
|
|
174
|
+
const [yelR, yelG, yelB] = hexToRgb("#FFF200")
|
|
175
|
+
this.inkYellowUniform = uniform(new THREE.Vector3(yelR, yelG, yelB))
|
|
176
|
+
const [keyR, keyG, keyB] = hexToRgb("#1a1a1a")
|
|
177
|
+
this.inkKeyUniform = uniform(new THREE.Vector3(keyR, keyG, keyB))
|
|
178
|
+
|
|
179
|
+
this.dotGainUniform = uniform(0)
|
|
180
|
+
this.dotMorphUniform = uniform(0)
|
|
181
|
+
|
|
182
|
+
this.rebuildEffectNode()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
override render(
|
|
186
|
+
renderer: THREE.WebGPURenderer,
|
|
187
|
+
inputTexture: THREE.Texture,
|
|
188
|
+
outputTarget: THREE.WebGLRenderTarget,
|
|
189
|
+
time: number,
|
|
190
|
+
delta: number
|
|
191
|
+
): void {
|
|
192
|
+
for (const node of this.sampleNodes) {
|
|
193
|
+
node.value = inputTexture
|
|
194
|
+
}
|
|
195
|
+
super.render(renderer, inputTexture, outputTarget, time, delta)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
override updateParams(params: LayerParameterValues): void {
|
|
199
|
+
this.spacingUniform.value =
|
|
200
|
+
typeof params.spacing === "number" ? Math.max(2, params.spacing) : 12
|
|
201
|
+
this.dotSizeUniform.value =
|
|
202
|
+
typeof params.dotSize === "number" ? params.dotSize : 0.8
|
|
203
|
+
this.dotMinUniform.value =
|
|
204
|
+
typeof params.dotMin === "number" ? params.dotMin : 0
|
|
205
|
+
this.contrastUniform.value =
|
|
206
|
+
typeof params.contrast === "number" ? params.contrast : 1
|
|
207
|
+
this.softnessUniform.value =
|
|
208
|
+
typeof params.softness === "number" ? params.softness : 0.25
|
|
209
|
+
this.invertUniform.value = params.invertLuma === true ? 1 : 0
|
|
210
|
+
this.angleUniform.value =
|
|
211
|
+
typeof params.angle === "number" ? (params.angle * Math.PI) / 180 : 0
|
|
212
|
+
|
|
213
|
+
const shapeMap: Record<string, number> = {
|
|
214
|
+
circle: 0,
|
|
215
|
+
square: 1,
|
|
216
|
+
diamond: 2,
|
|
217
|
+
line: 3,
|
|
218
|
+
}
|
|
219
|
+
this.shapeUniform.value = shapeMap[params.shape as string] ?? 0
|
|
220
|
+
|
|
221
|
+
if (typeof params.ink === "string") {
|
|
222
|
+
const [r, g, b] = hexToRgb(params.ink)
|
|
223
|
+
this.inkRedUniform.value = r
|
|
224
|
+
this.inkGreenUniform.value = g
|
|
225
|
+
this.inkBlueUniform.value = b
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (typeof params.duotoneLight === "string") {
|
|
229
|
+
const [r, g, b] = hexToRgb(params.duotoneLight)
|
|
230
|
+
;(this.duotoneLightUniform.value as THREE.Vector3).set(r, g, b)
|
|
231
|
+
}
|
|
232
|
+
if (typeof params.duotoneDark === "string") {
|
|
233
|
+
const [r, g, b] = hexToRgb(params.duotoneDark)
|
|
234
|
+
;(this.duotoneDarkUniform.value as THREE.Vector3).set(r, g, b)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (typeof params.customBgColor === "string") {
|
|
238
|
+
const [r, g, b] = hexToRgb(params.customBgColor)
|
|
239
|
+
;(this.customBgColorUniform.value as THREE.Vector3).set(r, g, b)
|
|
240
|
+
}
|
|
241
|
+
if (typeof params.customColorCount === "number") {
|
|
242
|
+
this.customColorCountUniform.value = Math.min(
|
|
243
|
+
4,
|
|
244
|
+
Math.max(2, Math.round(params.customColorCount))
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
if (typeof params.customLuminanceBias === "number") {
|
|
248
|
+
this.customLuminanceBiasUniform.value = Math.min(
|
|
249
|
+
1,
|
|
250
|
+
Math.max(-1, params.customLuminanceBias)
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
if (typeof params.customColor1 === "string") {
|
|
254
|
+
const [r, g, b] = hexToRgb(params.customColor1)
|
|
255
|
+
;(this.customColor1Uniform.value as THREE.Vector3).set(r, g, b)
|
|
256
|
+
}
|
|
257
|
+
if (typeof params.customColor2 === "string") {
|
|
258
|
+
const [r, g, b] = hexToRgb(params.customColor2)
|
|
259
|
+
;(this.customColor2Uniform.value as THREE.Vector3).set(r, g, b)
|
|
260
|
+
}
|
|
261
|
+
if (typeof params.customColor3 === "string") {
|
|
262
|
+
const [r, g, b] = hexToRgb(params.customColor3)
|
|
263
|
+
;(this.customColor3Uniform.value as THREE.Vector3).set(r, g, b)
|
|
264
|
+
}
|
|
265
|
+
if (typeof params.customColor4 === "string") {
|
|
266
|
+
const [r, g, b] = hexToRgb(params.customColor4)
|
|
267
|
+
;(this.customColor4Uniform.value as THREE.Vector3).set(r, g, b)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (typeof params.cyanAngle === "number") {
|
|
271
|
+
this.cyanAngleUniform.value = (params.cyanAngle * Math.PI) / 180
|
|
272
|
+
}
|
|
273
|
+
if (typeof params.magentaAngle === "number") {
|
|
274
|
+
this.magentaAngleUniform.value = (params.magentaAngle * Math.PI) / 180
|
|
275
|
+
}
|
|
276
|
+
if (typeof params.yellowAngle === "number") {
|
|
277
|
+
this.yellowAngleUniform.value = (params.yellowAngle * Math.PI) / 180
|
|
278
|
+
}
|
|
279
|
+
if (typeof params.keyAngle === "number") {
|
|
280
|
+
this.keyAngleUniform.value = (params.keyAngle * Math.PI) / 180
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (typeof params.paperColor === "string") {
|
|
284
|
+
const [r, g, b] = hexToRgb(params.paperColor)
|
|
285
|
+
this.paperRedUniform.value = r
|
|
286
|
+
this.paperGreenUniform.value = g
|
|
287
|
+
this.paperBlueUniform.value = b
|
|
288
|
+
}
|
|
289
|
+
if (typeof params.paperGrain === "number") {
|
|
290
|
+
this.paperGrainUniform.value = params.paperGrain
|
|
291
|
+
}
|
|
292
|
+
if (typeof params.gcr === "number") {
|
|
293
|
+
this.gcrUniform.value = params.gcr
|
|
294
|
+
}
|
|
295
|
+
if (typeof params.registration === "number") {
|
|
296
|
+
this.registrationUniform.value = params.registration
|
|
297
|
+
}
|
|
298
|
+
if (typeof params.dotGain === "number") {
|
|
299
|
+
this.dotGainUniform.value = params.dotGain
|
|
300
|
+
}
|
|
301
|
+
if (typeof params.dotMorph === "number") {
|
|
302
|
+
this.dotMorphUniform.value = params.dotMorph
|
|
303
|
+
}
|
|
304
|
+
const nextBloomIntensity =
|
|
305
|
+
typeof params.bloomIntensity === "number"
|
|
306
|
+
? Math.max(0, params.bloomIntensity)
|
|
307
|
+
: 1.25
|
|
308
|
+
const nextBloomThreshold =
|
|
309
|
+
typeof params.bloomThreshold === "number"
|
|
310
|
+
? clamp01(params.bloomThreshold)
|
|
311
|
+
: 0.6
|
|
312
|
+
const nextBloomRadius =
|
|
313
|
+
typeof params.bloomRadius === "number"
|
|
314
|
+
? Math.max(0, params.bloomRadius)
|
|
315
|
+
: 6
|
|
316
|
+
const nextBloomSoftness =
|
|
317
|
+
typeof params.bloomSoftness === "number"
|
|
318
|
+
? clamp01(params.bloomSoftness)
|
|
319
|
+
: 0.35
|
|
320
|
+
|
|
321
|
+
this.bloomIntensityUniform.value = nextBloomIntensity
|
|
322
|
+
this.bloomRadiusUniform.value = nextBloomRadius
|
|
323
|
+
this.bloomSoftnessUniform.value = nextBloomSoftness
|
|
324
|
+
this.bloomThresholdUniform.value = nextBloomThreshold
|
|
325
|
+
|
|
326
|
+
if (typeof params.inkCyan === "string") {
|
|
327
|
+
const [r, g, b] = hexToRgb(params.inkCyan)
|
|
328
|
+
;(this.inkCyanUniform.value as THREE.Vector3).set(r, g, b)
|
|
329
|
+
}
|
|
330
|
+
if (typeof params.inkMagenta === "string") {
|
|
331
|
+
const [r, g, b] = hexToRgb(params.inkMagenta)
|
|
332
|
+
;(this.inkMagentaUniform.value as THREE.Vector3).set(r, g, b)
|
|
333
|
+
}
|
|
334
|
+
if (typeof params.inkYellow === "string") {
|
|
335
|
+
const [r, g, b] = hexToRgb(params.inkYellow)
|
|
336
|
+
;(this.inkYellowUniform.value as THREE.Vector3).set(r, g, b)
|
|
337
|
+
}
|
|
338
|
+
if (typeof params.inkKey === "string") {
|
|
339
|
+
const [r, g, b] = hexToRgb(params.inkKey)
|
|
340
|
+
;(this.inkKeyUniform.value as THREE.Vector3).set(r, g, b)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let needsRebuild = false
|
|
344
|
+
|
|
345
|
+
const nextColorMode = parseColorMode(params.colorMode)
|
|
346
|
+
if (nextColorMode !== this.colorMode) {
|
|
347
|
+
this.colorMode = nextColorMode
|
|
348
|
+
needsRebuild = true
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const resolvedBloomEnabled =
|
|
352
|
+
nextColorMode === "custom" && params.bloomEnabled === true
|
|
353
|
+
if (resolvedBloomEnabled !== this.bloomEnabled) {
|
|
354
|
+
this.bloomEnabled = resolvedBloomEnabled
|
|
355
|
+
needsRebuild = true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const nextCmykBlend = parseCmykBlend(params.cmykBlend)
|
|
359
|
+
if (nextCmykBlend !== this.cmykBlendMode) {
|
|
360
|
+
this.cmykBlendMode = nextCmykBlend
|
|
361
|
+
needsRebuild = true
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (needsRebuild) {
|
|
365
|
+
this.rebuildEffectNode()
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (this.bloomNode) {
|
|
370
|
+
this.bloomNode.strength.value = nextBloomIntensity
|
|
371
|
+
this.bloomNode.radius.value = this.normalizeBloomRadius(nextBloomRadius)
|
|
372
|
+
this.bloomNode.threshold.value = nextBloomThreshold
|
|
373
|
+
this.bloomNode.smoothWidth.value =
|
|
374
|
+
this.normalizeBloomSoftness(nextBloomSoftness)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
override dispose(): void {
|
|
379
|
+
this.disposeBloomNode()
|
|
380
|
+
super.dispose()
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
protected override buildEffectNode(): Node {
|
|
384
|
+
if (!this.spacingUniform) return this.inputNode
|
|
385
|
+
|
|
386
|
+
this.disposeBloomNode()
|
|
387
|
+
this.bloomNode = null
|
|
388
|
+
this.sampleNodes = []
|
|
389
|
+
|
|
390
|
+
const renderTargetUv = vec2(uv().x, float(1).sub(uv().y))
|
|
391
|
+
const pixCoord = renderTargetUv.mul(screenSize)
|
|
392
|
+
|
|
393
|
+
if (this.colorMode === "cmyk") {
|
|
394
|
+
return this.buildCmykNode(pixCoord, renderTargetUv)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return this.buildSingleChannelNode(pixCoord, renderTargetUv)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private buildSingleChannelNode(pixCoord: Node, _renderTargetUv: Node): Node {
|
|
401
|
+
const grid = this.buildHalftoneGrid(
|
|
402
|
+
pixCoord,
|
|
403
|
+
this.angleUniform,
|
|
404
|
+
(sample) => {
|
|
405
|
+
return float(sample.r)
|
|
406
|
+
.mul(0.2126)
|
|
407
|
+
.add(float(sample.g).mul(0.7152))
|
|
408
|
+
.add(float(sample.b).mul(0.0722))
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
const darkVec = vec3(
|
|
413
|
+
float(this.duotoneDarkUniform.x),
|
|
414
|
+
float(this.duotoneDarkUniform.y),
|
|
415
|
+
float(this.duotoneDarkUniform.z)
|
|
416
|
+
)
|
|
417
|
+
const lightVec = vec3(
|
|
418
|
+
float(this.duotoneLightUniform.x),
|
|
419
|
+
float(this.duotoneLightUniform.y),
|
|
420
|
+
float(this.duotoneLightUniform.z)
|
|
421
|
+
)
|
|
422
|
+
const customBgVec = vec3(
|
|
423
|
+
float(this.customBgColorUniform.x),
|
|
424
|
+
float(this.customBgColorUniform.y),
|
|
425
|
+
float(this.customBgColorUniform.z)
|
|
426
|
+
)
|
|
427
|
+
const customColor1Vec = vec3(
|
|
428
|
+
float(this.customColor1Uniform.x),
|
|
429
|
+
float(this.customColor1Uniform.y),
|
|
430
|
+
float(this.customColor1Uniform.z)
|
|
431
|
+
)
|
|
432
|
+
const customColor2Vec = vec3(
|
|
433
|
+
float(this.customColor2Uniform.x),
|
|
434
|
+
float(this.customColor2Uniform.y),
|
|
435
|
+
float(this.customColor2Uniform.z)
|
|
436
|
+
)
|
|
437
|
+
const customColor3Vec = vec3(
|
|
438
|
+
float(this.customColor3Uniform.x),
|
|
439
|
+
float(this.customColor3Uniform.y),
|
|
440
|
+
float(this.customColor3Uniform.z)
|
|
441
|
+
)
|
|
442
|
+
const customColor4Vec = vec3(
|
|
443
|
+
float(this.customColor4Uniform.x),
|
|
444
|
+
float(this.customColor4Uniform.y),
|
|
445
|
+
float(this.customColor4Uniform.z)
|
|
446
|
+
)
|
|
447
|
+
const customColorCount = clamp(
|
|
448
|
+
float(this.customColorCountUniform),
|
|
449
|
+
float(2),
|
|
450
|
+
float(4)
|
|
451
|
+
)
|
|
452
|
+
const customLuminance = clamp(
|
|
453
|
+
grid.luma.add(float(this.customLuminanceBiasUniform).mul(0.35)),
|
|
454
|
+
float(0),
|
|
455
|
+
float(1)
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
let dotColor: Node
|
|
459
|
+
let bgColor: Node
|
|
460
|
+
let emissiveColor: Node = vec3(float(0), float(0), float(0))
|
|
461
|
+
|
|
462
|
+
switch (this.colorMode) {
|
|
463
|
+
case "monochrome": {
|
|
464
|
+
const inkVec = vec3(
|
|
465
|
+
this.inkRedUniform,
|
|
466
|
+
this.inkGreenUniform,
|
|
467
|
+
this.inkBlueUniform
|
|
468
|
+
)
|
|
469
|
+
dotColor = vec3(grid.luma, grid.luma, grid.luma).mul(inkVec)
|
|
470
|
+
bgColor = vec3(0, 0, 0)
|
|
471
|
+
break
|
|
472
|
+
}
|
|
473
|
+
case "duotone":
|
|
474
|
+
dotColor = mix(darkVec, lightVec, grid.luma)
|
|
475
|
+
bgColor = darkVec
|
|
476
|
+
break
|
|
477
|
+
case "custom":
|
|
478
|
+
dotColor = select(
|
|
479
|
+
customColorCount.lessThan(float(2.5)),
|
|
480
|
+
select(
|
|
481
|
+
customLuminance.lessThan(float(0.5)),
|
|
482
|
+
customColor1Vec,
|
|
483
|
+
customColor2Vec
|
|
484
|
+
),
|
|
485
|
+
select(
|
|
486
|
+
customColorCount.lessThan(float(3.5)),
|
|
487
|
+
select(
|
|
488
|
+
customLuminance.lessThan(float(1 / 3)),
|
|
489
|
+
customColor1Vec,
|
|
490
|
+
select(
|
|
491
|
+
customLuminance.lessThan(float(2 / 3)),
|
|
492
|
+
customColor2Vec,
|
|
493
|
+
customColor3Vec
|
|
494
|
+
)
|
|
495
|
+
),
|
|
496
|
+
select(
|
|
497
|
+
customLuminance.lessThan(float(0.25)),
|
|
498
|
+
customColor1Vec,
|
|
499
|
+
select(
|
|
500
|
+
customLuminance.lessThan(float(0.5)),
|
|
501
|
+
customColor2Vec,
|
|
502
|
+
select(
|
|
503
|
+
customLuminance.lessThan(float(0.75)),
|
|
504
|
+
customColor3Vec,
|
|
505
|
+
customColor4Vec
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
)
|
|
509
|
+
)
|
|
510
|
+
)
|
|
511
|
+
bgColor = customBgVec
|
|
512
|
+
emissiveColor = dotColor.mul(grid.coverage)
|
|
513
|
+
break
|
|
514
|
+
default:
|
|
515
|
+
dotColor = grid.color
|
|
516
|
+
bgColor = vec3(1, 1, 1)
|
|
517
|
+
break
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const baseColor = mix(bgColor, dotColor, grid.coverage)
|
|
521
|
+
|
|
522
|
+
if (!(this.colorMode === "custom" && this.bloomEnabled)) {
|
|
523
|
+
return vec4(baseColor, float(1))
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const bloomInput = vec4(emissiveColor, float(1))
|
|
527
|
+
this.bloomNode = bloom(
|
|
528
|
+
bloomInput,
|
|
529
|
+
this.bloomIntensityUniform.value as number,
|
|
530
|
+
this.normalizeBloomRadius(this.bloomRadiusUniform.value as number),
|
|
531
|
+
this.bloomThresholdUniform.value as number
|
|
532
|
+
)
|
|
533
|
+
this.bloomNode.smoothWidth.value = this.normalizeBloomSoftness(
|
|
534
|
+
this.bloomSoftnessUniform.value as number
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
return vec4(
|
|
538
|
+
clamp(
|
|
539
|
+
baseColor.add(this.getBloomTextureNode().rgb),
|
|
540
|
+
vec3(float(0), float(0), float(0)),
|
|
541
|
+
vec3(float(1), float(1), float(1))
|
|
542
|
+
),
|
|
543
|
+
float(1)
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private buildCmykNode(pixCoord: Node, renderTargetUv: Node): Node {
|
|
548
|
+
const gcrAmount = this.gcrUniform
|
|
549
|
+
|
|
550
|
+
const extractCyan = (sample: Node) => {
|
|
551
|
+
const maxK = float(1).sub(
|
|
552
|
+
max(max(float(sample.r), float(sample.g)), float(sample.b))
|
|
553
|
+
)
|
|
554
|
+
const k = maxK.mul(gcrAmount)
|
|
555
|
+
const oneMinusK = max(float(1).sub(k), float(0.001))
|
|
556
|
+
return clamp(
|
|
557
|
+
float(1).sub(float(sample.r)).sub(k).div(oneMinusK),
|
|
558
|
+
float(0),
|
|
559
|
+
float(1)
|
|
560
|
+
)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const extractMagenta = (sample: Node) => {
|
|
564
|
+
const maxK = float(1).sub(
|
|
565
|
+
max(max(float(sample.r), float(sample.g)), float(sample.b))
|
|
566
|
+
)
|
|
567
|
+
const k = maxK.mul(gcrAmount)
|
|
568
|
+
const oneMinusK = max(float(1).sub(k), float(0.001))
|
|
569
|
+
return clamp(
|
|
570
|
+
float(1).sub(float(sample.g)).sub(k).div(oneMinusK),
|
|
571
|
+
float(0),
|
|
572
|
+
float(1)
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const extractYellow = (sample: Node) => {
|
|
577
|
+
const maxK = float(1).sub(
|
|
578
|
+
max(max(float(sample.r), float(sample.g)), float(sample.b))
|
|
579
|
+
)
|
|
580
|
+
const k = maxK.mul(gcrAmount)
|
|
581
|
+
const oneMinusK = max(float(1).sub(k), float(0.001))
|
|
582
|
+
return clamp(
|
|
583
|
+
float(1).sub(float(sample.b)).sub(k).div(oneMinusK),
|
|
584
|
+
float(0),
|
|
585
|
+
float(1)
|
|
586
|
+
)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const extractKey = (sample: Node) => {
|
|
590
|
+
const maxK = float(1).sub(
|
|
591
|
+
max(max(float(sample.r), float(sample.g)), float(sample.b))
|
|
592
|
+
)
|
|
593
|
+
return maxK.mul(gcrAmount)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const reg = float(this.registrationUniform)
|
|
597
|
+
const cyanCoord = pixCoord.add(vec2(reg, float(0)))
|
|
598
|
+
const magentaCoord = pixCoord.add(vec2(reg.mul(-0.5), reg.mul(0.866)))
|
|
599
|
+
const yellowCoord = pixCoord.add(vec2(reg.mul(-0.5), reg.mul(-0.866)))
|
|
600
|
+
|
|
601
|
+
const cyanGrid = this.buildHalftoneGrid(
|
|
602
|
+
cyanCoord,
|
|
603
|
+
this.cyanAngleUniform,
|
|
604
|
+
extractCyan
|
|
605
|
+
)
|
|
606
|
+
const magentaGrid = this.buildHalftoneGrid(
|
|
607
|
+
magentaCoord,
|
|
608
|
+
this.magentaAngleUniform,
|
|
609
|
+
extractMagenta
|
|
610
|
+
)
|
|
611
|
+
const yellowGrid = this.buildHalftoneGrid(
|
|
612
|
+
yellowCoord,
|
|
613
|
+
this.yellowAngleUniform,
|
|
614
|
+
extractYellow
|
|
615
|
+
)
|
|
616
|
+
const keyGrid = this.buildHalftoneGrid(
|
|
617
|
+
pixCoord,
|
|
618
|
+
this.keyAngleUniform,
|
|
619
|
+
extractKey
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
const grain = grainTexturePattern(renderTargetUv.mul(screenSize))
|
|
623
|
+
const grainOffset = grain.sub(0.5).mul(this.paperGrainUniform)
|
|
624
|
+
const paperR = clamp(
|
|
625
|
+
float(this.paperRedUniform).add(grainOffset),
|
|
626
|
+
float(0),
|
|
627
|
+
float(1)
|
|
628
|
+
)
|
|
629
|
+
const paperG = clamp(
|
|
630
|
+
float(this.paperGreenUniform).add(grainOffset),
|
|
631
|
+
float(0),
|
|
632
|
+
float(1)
|
|
633
|
+
)
|
|
634
|
+
const paperB = clamp(
|
|
635
|
+
float(this.paperBlueUniform).add(grainOffset),
|
|
636
|
+
float(0),
|
|
637
|
+
float(1)
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
const inkC = vec3(
|
|
641
|
+
float(this.inkCyanUniform.x),
|
|
642
|
+
float(this.inkCyanUniform.y),
|
|
643
|
+
float(this.inkCyanUniform.z)
|
|
644
|
+
)
|
|
645
|
+
const inkM = vec3(
|
|
646
|
+
float(this.inkMagentaUniform.x),
|
|
647
|
+
float(this.inkMagentaUniform.y),
|
|
648
|
+
float(this.inkMagentaUniform.z)
|
|
649
|
+
)
|
|
650
|
+
const inkY = vec3(
|
|
651
|
+
float(this.inkYellowUniform.x),
|
|
652
|
+
float(this.inkYellowUniform.y),
|
|
653
|
+
float(this.inkYellowUniform.z)
|
|
654
|
+
)
|
|
655
|
+
const inkK = vec3(
|
|
656
|
+
float(this.inkKeyUniform.x),
|
|
657
|
+
float(this.inkKeyUniform.y),
|
|
658
|
+
float(this.inkKeyUniform.z)
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
const transC = mix(vec3(1, 1, 1), inkC, cyanGrid.coverage)
|
|
662
|
+
const transM = mix(vec3(1, 1, 1), inkM, magentaGrid.coverage)
|
|
663
|
+
const transY = mix(vec3(1, 1, 1), inkY, yellowGrid.coverage)
|
|
664
|
+
const transK = mix(vec3(1, 1, 1), inkK, keyGrid.coverage)
|
|
665
|
+
|
|
666
|
+
const paper = vec3(paperR, paperG, paperB)
|
|
667
|
+
let finalColor: Node
|
|
668
|
+
|
|
669
|
+
if (this.cmykBlendMode === "overprint") {
|
|
670
|
+
finalColor = vec3(
|
|
671
|
+
clamp(
|
|
672
|
+
paper.x.mul(transC.x).mul(transM.x).mul(transY.x).mul(transK.x),
|
|
673
|
+
float(0),
|
|
674
|
+
float(1)
|
|
675
|
+
),
|
|
676
|
+
clamp(
|
|
677
|
+
paper.y.mul(transC.y).mul(transM.y).mul(transY.y).mul(transK.y),
|
|
678
|
+
float(0),
|
|
679
|
+
float(1)
|
|
680
|
+
),
|
|
681
|
+
clamp(
|
|
682
|
+
paper.z.mul(transC.z).mul(transM.z).mul(transY.z).mul(transK.z),
|
|
683
|
+
float(0),
|
|
684
|
+
float(1)
|
|
685
|
+
)
|
|
686
|
+
)
|
|
687
|
+
} else {
|
|
688
|
+
const layerC = vec3(
|
|
689
|
+
paper.x.mul(transC.x),
|
|
690
|
+
paper.y.mul(transC.y),
|
|
691
|
+
paper.z.mul(transC.z)
|
|
692
|
+
)
|
|
693
|
+
const layerM = vec3(
|
|
694
|
+
paper.x.mul(transM.x),
|
|
695
|
+
paper.y.mul(transM.y),
|
|
696
|
+
paper.z.mul(transM.z)
|
|
697
|
+
)
|
|
698
|
+
const layerY = vec3(
|
|
699
|
+
paper.x.mul(transY.x),
|
|
700
|
+
paper.y.mul(transY.y),
|
|
701
|
+
paper.z.mul(transY.z)
|
|
702
|
+
)
|
|
703
|
+
const layerK = vec3(
|
|
704
|
+
paper.x.mul(transK.x),
|
|
705
|
+
paper.y.mul(transK.y),
|
|
706
|
+
paper.z.mul(transK.z)
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
finalColor = vec3(
|
|
710
|
+
clamp(
|
|
711
|
+
min(min(layerC.x, layerM.x), min(layerY.x, layerK.x)),
|
|
712
|
+
float(0),
|
|
713
|
+
float(1)
|
|
714
|
+
),
|
|
715
|
+
clamp(
|
|
716
|
+
min(min(layerC.y, layerM.y), min(layerY.y, layerK.y)),
|
|
717
|
+
float(0),
|
|
718
|
+
float(1)
|
|
719
|
+
),
|
|
720
|
+
clamp(
|
|
721
|
+
min(min(layerC.z, layerM.z), min(layerY.z, layerK.z)),
|
|
722
|
+
float(0),
|
|
723
|
+
float(1)
|
|
724
|
+
)
|
|
725
|
+
)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return vec4(finalColor, float(1))
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
private buildHalftoneGrid(
|
|
732
|
+
pixCoord: Node,
|
|
733
|
+
angleRadians: Node,
|
|
734
|
+
channelExtractor: (sample: Node) => Node
|
|
735
|
+
): { color: Node; coverage: Node; luma: Node } {
|
|
736
|
+
const cosA = float(cos(angleRadians))
|
|
737
|
+
const sinA = float(sin(angleRadians))
|
|
738
|
+
|
|
739
|
+
const rotX = cosA.mul(pixCoord.x).add(sinA.mul(pixCoord.y))
|
|
740
|
+
const rotY = cosA.mul(pixCoord.y).sub(sinA.mul(pixCoord.x))
|
|
741
|
+
|
|
742
|
+
const ccrX = floor(float(rotX.div(this.spacingUniform)).add(0.5)).mul(
|
|
743
|
+
this.spacingUniform
|
|
744
|
+
)
|
|
745
|
+
const ccrY = floor(float(rotY.div(this.spacingUniform)).add(0.5)).mul(
|
|
746
|
+
this.spacingUniform
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
const aa = max(
|
|
750
|
+
float(0.5),
|
|
751
|
+
float(this.softnessUniform).mul(this.spacingUniform).mul(0.3)
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
let accCov: Node = float(0)
|
|
755
|
+
let accR: Node = float(0)
|
|
756
|
+
let accG: Node = float(0)
|
|
757
|
+
let accB: Node = float(0)
|
|
758
|
+
let accLuma: Node = float(0)
|
|
759
|
+
|
|
760
|
+
let accField: Node = float(0)
|
|
761
|
+
let accWeightR: Node = float(0)
|
|
762
|
+
let accWeightG: Node = float(0)
|
|
763
|
+
let accWeightB: Node = float(0)
|
|
764
|
+
let accWeightLuma: Node = float(0)
|
|
765
|
+
|
|
766
|
+
const morph = clamp(float(this.dotMorphUniform), float(0), float(1))
|
|
767
|
+
const fieldReach = this.spacingUniform.mul(morph).mul(0.6)
|
|
768
|
+
|
|
769
|
+
for (let dj = -1; dj <= 1; dj++) {
|
|
770
|
+
for (let di = -1; di <= 1; di++) {
|
|
771
|
+
const cellRX =
|
|
772
|
+
di === 0 ? ccrX : float(ccrX.add(this.spacingUniform.mul(float(di))))
|
|
773
|
+
const cellRY =
|
|
774
|
+
dj === 0 ? ccrY : float(ccrY.add(this.spacingUniform.mul(float(dj))))
|
|
775
|
+
|
|
776
|
+
const cellSX = cosA.mul(cellRX).sub(sinA.mul(cellRY))
|
|
777
|
+
const cellSY = sinA.mul(cellRX).add(cosA.mul(cellRY))
|
|
778
|
+
const cellUV = vec2(cellSX, cellSY).div(screenSize)
|
|
779
|
+
|
|
780
|
+
const sNode = tslTexture(new THREE.Texture(), cellUV)
|
|
781
|
+
this.sampleNodes.push(sNode)
|
|
782
|
+
|
|
783
|
+
const channelValue = channelExtractor(sNode)
|
|
784
|
+
const clampedValue = clamp(
|
|
785
|
+
channelValue.mul(this.contrastUniform),
|
|
786
|
+
float(0),
|
|
787
|
+
float(1)
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
const effectiveValue = select(
|
|
791
|
+
this.invertUniform.greaterThan(float(0.5)),
|
|
792
|
+
float(1).sub(clampedValue),
|
|
793
|
+
clampedValue
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
const radius = float(this.dotMinUniform)
|
|
797
|
+
.add(effectiveValue.mul(this.dotSizeUniform))
|
|
798
|
+
.add(this.dotGainUniform)
|
|
799
|
+
.mul(this.spacingUniform)
|
|
800
|
+
.mul(0.5)
|
|
801
|
+
|
|
802
|
+
const dx = rotX.sub(cellRX)
|
|
803
|
+
const dy = rotY.sub(cellRY)
|
|
804
|
+
|
|
805
|
+
const dCircle = vec2(dx, dy).length()
|
|
806
|
+
const dSquare = max(abs(dx), abs(dy))
|
|
807
|
+
const dDiamond = abs(dx).add(abs(dy))
|
|
808
|
+
const dLine = abs(dy)
|
|
809
|
+
|
|
810
|
+
const dist = select(
|
|
811
|
+
this.shapeUniform.lessThan(float(0.5)),
|
|
812
|
+
dCircle,
|
|
813
|
+
select(
|
|
814
|
+
this.shapeUniform.lessThan(float(1.5)),
|
|
815
|
+
dSquare,
|
|
816
|
+
select(this.shapeUniform.lessThan(float(2.5)), dDiamond, dLine)
|
|
817
|
+
)
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
const cellCov = smoothstep(radius.add(aa), radius.sub(aa), dist)
|
|
821
|
+
|
|
822
|
+
const isNew = cellCov.greaterThan(accCov)
|
|
823
|
+
const maxR = select(isNew, float(sNode.r), accR)
|
|
824
|
+
const maxG = select(isNew, float(sNode.g), accG)
|
|
825
|
+
const maxB = select(isNew, float(sNode.b), accB)
|
|
826
|
+
const maxLuma = select(isNew, effectiveValue, accLuma)
|
|
827
|
+
const maxCov = max(cellCov, accCov)
|
|
828
|
+
|
|
829
|
+
const fieldRadius = max(radius.add(fieldReach), float(0.001))
|
|
830
|
+
const cellField = clamp(
|
|
831
|
+
float(1).sub(dist.div(fieldRadius)),
|
|
832
|
+
float(0),
|
|
833
|
+
float(1)
|
|
834
|
+
)
|
|
835
|
+
const cellFieldSq = cellField.mul(cellField)
|
|
836
|
+
|
|
837
|
+
accWeightR = accWeightR.add(float(sNode.r).mul(cellFieldSq))
|
|
838
|
+
accWeightG = accWeightG.add(float(sNode.g).mul(cellFieldSq))
|
|
839
|
+
accWeightB = accWeightB.add(float(sNode.b).mul(cellFieldSq))
|
|
840
|
+
accWeightLuma = accWeightLuma.add(effectiveValue.mul(cellFieldSq))
|
|
841
|
+
accField = accField.add(cellFieldSq)
|
|
842
|
+
|
|
843
|
+
accR = maxR
|
|
844
|
+
accG = maxG
|
|
845
|
+
accB = maxB
|
|
846
|
+
accLuma = maxLuma
|
|
847
|
+
accCov = maxCov
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const metaEdge = max(float(0.01), aa.div(this.spacingUniform).mul(0.5))
|
|
852
|
+
const metaCov = smoothstep(
|
|
853
|
+
float(0.5).sub(metaEdge),
|
|
854
|
+
float(0.5).add(metaEdge),
|
|
855
|
+
accField
|
|
856
|
+
)
|
|
857
|
+
const fieldWeight = max(accField, float(0.001))
|
|
858
|
+
|
|
859
|
+
const finalCov = mix(accCov, metaCov, morph)
|
|
860
|
+
const finalR = mix(accR, accWeightR.div(fieldWeight), morph)
|
|
861
|
+
const finalG = mix(accG, accWeightG.div(fieldWeight), morph)
|
|
862
|
+
const finalB = mix(accB, accWeightB.div(fieldWeight), morph)
|
|
863
|
+
const finalLuma = mix(accLuma, accWeightLuma.div(fieldWeight), morph)
|
|
864
|
+
|
|
865
|
+
return {
|
|
866
|
+
color: vec3(finalR, finalG, finalB),
|
|
867
|
+
coverage: finalCov,
|
|
868
|
+
luma: finalLuma,
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
private normalizeBloomRadius(value: number): number {
|
|
873
|
+
return clamp01(value / 24)
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
private normalizeBloomSoftness(value: number): number {
|
|
877
|
+
return Math.max(0.001, value * 0.25)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
private disposeBloomNode(): void {
|
|
881
|
+
;(this.bloomNode as { dispose?: () => void } | null)?.dispose?.()
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
private getBloomTextureNode(): Node {
|
|
885
|
+
const bloomNode = this.bloomNode as
|
|
886
|
+
| ({
|
|
887
|
+
getTexture?: () => Node
|
|
888
|
+
getTextureNode?: () => Node
|
|
889
|
+
} & object)
|
|
890
|
+
| null
|
|
891
|
+
|
|
892
|
+
if (!bloomNode) {
|
|
893
|
+
throw new Error("Bloom node is not initialized")
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
if (
|
|
897
|
+
"getTextureNode" in bloomNode &&
|
|
898
|
+
typeof bloomNode.getTextureNode === "function"
|
|
899
|
+
) {
|
|
900
|
+
return bloomNode.getTextureNode()
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (
|
|
904
|
+
"getTexture" in bloomNode &&
|
|
905
|
+
typeof bloomNode.getTexture === "function"
|
|
906
|
+
) {
|
|
907
|
+
return bloomNode.getTexture()
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
throw new Error("Bloom node does not expose a texture getter")
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function parseColorMode(value: unknown): HalftoneColorMode {
|
|
915
|
+
if (
|
|
916
|
+
value === "source" ||
|
|
917
|
+
value === "monochrome" ||
|
|
918
|
+
value === "duotone" ||
|
|
919
|
+
value === "custom" ||
|
|
920
|
+
value === "cmyk"
|
|
921
|
+
) {
|
|
922
|
+
return value
|
|
923
|
+
}
|
|
924
|
+
return "source"
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function parseCmykBlend(value: unknown): CmykBlendMode {
|
|
928
|
+
if (value === "subtractive" || value === "overprint") {
|
|
929
|
+
return value
|
|
930
|
+
}
|
|
931
|
+
return "subtractive"
|
|
932
|
+
}
|