kmcom-nuxt-layers 2.2.5 → 2.2.8
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/docs/FEEDS.md +197 -0
- package/docs/LAYERS-FIXES.md +101 -0
- package/docs/MIGRATION.md +627 -0
- package/docs/feed-layer.md +374 -0
- package/docs/patch-picture-provider-type.md +52 -0
- package/docs/shaderGuide.md +2071 -0
- package/docs/types-architecture.md +234 -0
- package/layers/animations/app/components/Motion/CountUp.vue +1 -1
- package/layers/animations/app/components/Motion/Magnetic.vue +1 -1
- package/layers/animations/app/components/Motion/Marquee.vue +3 -2
- package/layers/animations/app/components/Motion/MarqueeText.vue +3 -2
- package/layers/animations/app/components/Motion/Tilt.vue +1 -1
- package/layers/animations/app/composables/useCountUp.ts +4 -1
- package/layers/animations/app/composables/useMagneticElement.ts +2 -4
- package/layers/animations/app/composables/useMarqueeCopies.ts +4 -4
- package/layers/animations/app/composables/useTiltEffect.ts +1 -1
- package/layers/animations/app/types/animations.ts +8 -0
- package/layers/animations/app/types/index.ts +1 -0
- package/layers/animations/package.json +4 -1
- package/layers/canvas/app/components/ShaderCanvas.vue +4 -4
- package/layers/canvas/app/composables/useRendererCapabilities.ts +19 -15
- package/layers/canvas/app/types/index.ts +1 -0
- package/layers/canvas/package.json +2 -1
- package/layers/canvas/tsconfig.json +2 -1
- package/layers/content/app/components/Blog/Card.vue +5 -5
- package/layers/content/app/components/Gallery/AmbientImage.vue +3 -3
- package/layers/content/app/components/Gallery/Card.vue +3 -3
- package/layers/content/app/components/Gallery/Lightbox.vue +5 -1
- package/layers/content/app/components/NuxtContent/Detail.vue +5 -1
- package/layers/content/app/components/NuxtContent/Surround.vue +5 -3
- package/layers/content/app/components/NuxtContent/Toc.vue +1 -1
- package/layers/content/app/components/Portfolio/Card.vue +5 -5
- package/layers/content/app/components/content/Figure.vue +3 -3
- package/layers/content/app/composables/useBlogPosts.ts +2 -2
- package/layers/content/app/composables/useCollectionItem.ts +1 -3
- package/layers/content/app/composables/useGalleryItems.ts +2 -2
- package/layers/content/app/types/index.ts +1 -0
- package/layers/content/package.json +2 -1
- package/layers/core/app/composables/useCache.ts +0 -1
- package/layers/core/app/composables/useErrorLog.ts +9 -11
- package/layers/core/app/plugins/error-handler.ts +36 -36
- package/layers/core/app/plugins/feature-detection.client.ts +15 -15
- package/layers/core/app/plugins/init.ts +121 -129
- package/layers/core/app/plugins/loading.client.ts +27 -27
- package/layers/core/app/plugins/scroll-guard.client.ts +26 -26
- package/layers/core/app/utils/helpers.ts +14 -12
- package/layers/core/nuxt.config.ts +1 -0
- package/layers/feeds/app/plugins/feed-head.ts +62 -63
- package/layers/feeds/package.json +2 -1
- package/layers/feeds/server/routes/feed/discovery.get.ts +1 -2
- package/layers/feeds/server/utils/content-adapter.ts +3 -2
- package/layers/forms/app/components/Form/Field.vue +4 -4
- package/layers/forms/app/types/index.ts +1 -0
- package/layers/forms/package.json +2 -1
- package/layers/forms/server/api/contact.post.ts +1 -1
- package/layers/layout/app/components/Layout/Grid/Item.vue +33 -19
- package/layers/layout/app/components/Layout/Page/Container.vue +11 -11
- package/layers/layout/app/components/Layout/Page/Header.vue +1 -1
- package/layers/layout/app/components/Layout/Page/index.vue +1 -1
- package/layers/layout/app/components/Layout/Section/Gallery.vue +6 -1
- package/layers/layout/app/components/Layout/Section/Title.vue +1 -1
- package/layers/layout/app/types/index.ts +1 -0
- package/layers/layout/package.json +2 -1
- package/layers/mailer/app/types/index.ts +1 -0
- package/layers/mailer/app/types/mailer.ts +25 -0
- package/layers/mailer/package.json +2 -1
- package/layers/motion/package.json +2 -1
- package/layers/navigation/app/components/Links/Group.vue +1 -0
- package/layers/navigation/app/components/Links/Named.vue +2 -0
- package/layers/navigation/app/types/index.ts +1 -0
- package/layers/navigation/package.json +4 -1
- package/layers/page-transitions/app/plugins/page-transitions.client.ts +9 -9
- package/layers/page-transitions/package.json +4 -1
- package/layers/routing/app/plugins/feature-flags.client.ts +9 -9
- package/layers/routing/app/types/app-config.d.ts +3 -1
- package/layers/routing/app/types/index.ts +1 -0
- package/layers/routing/package.json +2 -1
- package/layers/scripts/app/composables/useGtm.ts +1 -1
- package/layers/scripts/app/types/index.ts +1 -0
- package/layers/scripts/app/types/scripts.ts +14 -0
- package/layers/scripts/package.json +2 -1
- package/layers/scroll/app/components/Motion/ScrollScene.vue +3 -1
- package/layers/scroll/app/composables/useScrollSteps.ts +2 -9
- package/layers/scroll/app/composables/useSectionProgress.ts +2 -1
- package/layers/scroll/app/plugins/locomotive-scroll.client.ts +54 -61
- package/layers/scroll/app/types/index.ts +1 -0
- package/layers/scroll/app/types/scroll.ts +32 -0
- package/layers/scroll/package.json +3 -0
- package/layers/seo/package.json +2 -1
- package/layers/shader/app/components/Material/Fresnel.client.vue +1 -1
- package/layers/shader/app/components/Material/Image.client.vue +1 -1
- package/layers/shader/app/components/Material/Node.client.vue +7 -7
- package/layers/shader/app/components/Material/Noise.client.vue +1 -1
- package/layers/shader/app/components/Node/Color.client.vue +17 -20
- package/layers/shader/app/components/Node/Noise.client.vue +31 -34
- package/layers/shader/app/components/Pipeline/Aurora.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/BilinearGradient.client.vue +8 -11
- package/layers/shader/app/components/Pipeline/BillowNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/BrightnessContrast.client.vue +6 -2
- package/layers/shader/app/components/Pipeline/CellularNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Checkerboard.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Circle.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/Clouds.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/ColorBurnBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/ColorDodgeBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/ColourRamp.client.vue +5 -9
- package/layers/shader/app/components/Pipeline/ConicGradient.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/Cross.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/CurlNoise.client.vue +3 -7
- package/layers/shader/app/components/Pipeline/DarkenBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/DayNightCycle.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/DiagonalGradient.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/DiamondGradient.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/DifferenceBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/DomainWarpedNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Dots.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/DuoTone.client.vue +5 -9
- package/layers/shader/app/components/Pipeline/ExclusionBlend.client.vue +3 -23
- package/layers/shader/app/components/Pipeline/ExponentialFog.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/FilmBurn.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/Flame.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/FocalGradient.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/GodRays.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/GradientNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Grid.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Halation.client.vue +3 -7
- package/layers/shader/app/components/Pipeline/HardLightBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/Haze.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/Hexagon.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/LensFlare.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/LightenBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/LinearGradient4.client.vue +2 -2
- package/layers/shader/app/components/Pipeline/Marble.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/MonochromeTint.client.vue +3 -7
- package/layers/shader/app/components/Pipeline/MultiplyBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/NoisyGradient.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/NoisyGradientBlend.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/OverlayBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/Polygon.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/RaymarchTunnel.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/Rectangle.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/RidgedNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Ring.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/ScreenBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/SkyAtmosphere.client.vue +6 -9
- package/layers/shader/app/components/Pipeline/SoftLightBlend.client.vue +2 -19
- package/layers/shader/app/components/Pipeline/SplitTone.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Star.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Stripes.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Tint.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/Triangle.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/ValueNoise.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/VoronoiEdges.client.vue +4 -8
- package/layers/shader/app/components/Pipeline/Water.client.vue +5 -8
- package/layers/shader/app/components/Pipeline/WaveBendLayer.client.vue +4 -7
- package/layers/shader/app/components/Pipeline/WaveColourLayer.client.vue +3 -7
- package/layers/shader/app/components/Pipeline/Wood.client.vue +4 -8
- package/layers/shader/app/components/Preset/Aurora.client.vue +15 -21
- package/layers/shader/app/components/Preset/Flow.client.vue +2 -1
- package/layers/shader/app/components/Preset/GradientMesh.client.vue +2 -1
- package/layers/shader/app/components/Preset/Nebula.client.vue +2 -1
- package/layers/shader/app/components/Preset/Ocean.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeAurora.client.vue +30 -90
- package/layers/shader/app/components/Preset/ThemeBubble.client.vue +30 -91
- package/layers/shader/app/components/Preset/ThemeFlow.client.vue +30 -90
- package/layers/shader/app/components/Preset/ThemeGradient.client.vue +30 -91
- package/layers/shader/app/components/Preset/ThemeLavaLamp.client.vue +30 -90
- package/layers/shader/app/components/Preset/ThemePlasma.client.vue +30 -90
- package/layers/shader/app/components/Preset/ThemeWave.client.vue +30 -90
- package/layers/shader/app/components/Shader/Background.client.vue +4 -4
- package/layers/shader/app/components/Shader/Host.client.vue +31 -33
- package/layers/shader/app/components/Shader/Runtime.client.vue +15 -23
- package/layers/shader/app/composables/useAmbientMaterials.ts +53 -51
- package/layers/shader/app/composables/useShaderMixBlend.ts +26 -0
- package/layers/shader/app/composables/useThemePreset.ts +75 -0
- package/layers/shader/app/plugins/shader.client.ts +21 -21
- package/layers/shader/app/shaders/common/noise.ts +2 -7
- package/layers/shader/app/shaders/types.ts +6 -6
- package/layers/shader/app/types/tsl.ts +7 -25
- package/layers/shader/app/types/uniforms.ts +2 -1
- package/layers/shader/app/utils/tsl/color.ts +7 -1
- package/layers/shader/package.json +2 -1
- package/layers/theme/app/components/ThemePicker/Colors.vue +1 -3
- package/layers/theme/app/plugins/theme.client.ts +54 -51
- package/layers/theme/app/types/app-config.d.ts +4 -2
- package/layers/theme/app/types/index.ts +1 -0
- package/layers/theme/app/types/theme.ts +3 -18
- package/layers/theme/package.json +2 -1
- package/layers/theme/server/plugins/theme-fouc.ts +1 -1
- package/layers/transitions/package.json +4 -1
- package/layers/typography/app/components/Typography/CodeBlock.vue +2 -2
- package/layers/typography/app/components/Typography/Headline.vue +2 -2
- package/layers/typography/app/components/Typography/HeadlineScreen.vue +1 -1
- package/layers/typography/app/components/Typography/QuoteBlock.vue +4 -1
- package/layers/typography/app/components/Typography/TextStroke.vue +2 -0
- package/layers/typography/app/components/Typography/index.vue +36 -27
- package/layers/typography/app/composables/typography.ts +27 -21
- package/layers/typography/app/types/colors.ts +9 -29
- package/layers/typography/app/types/index.ts +2 -0
- package/layers/typography/package.json +4 -1
- package/layers/ui/package.json +2 -1
- package/layers/visual/app/app.config.ts +5 -2
- package/layers/visual/app/components/Accent/Blob.vue +20 -20
- package/layers/visual/app/components/Accent/Scene.vue +2 -2
- package/layers/visual/app/components/Base/Modal.vue +2 -2
- package/layers/visual/app/components/Gradient/Background.vue +2 -2
- package/layers/visual/app/components/Gradient/Text.vue +2 -2
- package/layers/visual/app/components/Media/Picture.vue +3 -1
- package/layers/visual/app/components/Progress/Bar.vue +6 -6
- package/layers/visual/app/components/Tint/Overlay.vue +14 -14
- package/layers/visual/app/composables/accent.ts +10 -8
- package/layers/visual/app/composables/gradient.ts +2 -3
- package/layers/visual/app/composables/tint.ts +7 -7
- package/layers/visual/app/types/index.ts +6 -0
- package/layers/visual/app/types/media.ts +4 -2
- package/layers/visual/app/types/tint.ts +2 -1
- package/layers/visual/package.json +4 -1
- package/package.json +5 -2
|
@@ -0,0 +1,2071 @@
|
|
|
1
|
+
# Modular Shader Block System -- v5
|
|
2
|
+
|
|
3
|
+
## Changelog from v4
|
|
4
|
+
|
|
5
|
+
- **`CosinePalette`** added as a first-class Scalar Generator -- the single most reusable block in the entire system, backbone of all phobon TSL shaders
|
|
6
|
+
- New pipeline pattern formalised: **Scalar Pipeline** (UV → float → colour)
|
|
7
|
+
- New Math Primitives: `ComplexDiv`, `ComplexLog` (complex number arithmetic in TSL)
|
|
8
|
+
- New Generators: `CosinePalette`, `RingField`, `ComplexPlaneField`, `ChebyshevNoiseField`
|
|
9
|
+
- New UV Transformers: `UVColumnOffset`, `UVFractBand`
|
|
10
|
+
- New Colour Ops: `TanhTonemap`, `SDFColourMask`
|
|
11
|
+
- Shader conversions: imaginary2, dawn2, dawn4, dawn5, flare8, flare9, genuary22
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
Primitives Raw values, uniforms, constants, samplers
|
|
18
|
+
Generators UV → colour (starting point of most pipelines)
|
|
19
|
+
Scalar Generators UV → float → colour (CosinePalette family) ← NEW
|
|
20
|
+
UV Transformers UV → UV (warp, scroll, tile, distort)
|
|
21
|
+
Ray Transformers Ray → Ray (tilt, reflect, fisheye)
|
|
22
|
+
Colour Ops colour → colour (blend, grade, shift, remap)
|
|
23
|
+
Overlays colour → colour (composited effects, post-process style)
|
|
24
|
+
|
|
25
|
+
### The Scalar Pipeline
|
|
26
|
+
|
|
27
|
+
All 7 phobon shaders share the same pipeline shape: compute a meaningful float from UV (distance, noise, SDF, imaginary component), feed it into `cosinePalette`, multiply the result by a mask float. This is a distinct pattern from the UV→colour generators and worth formalising:
|
|
28
|
+
|
|
29
|
+
UV
|
|
30
|
+
├─ ScalarSource (uv.y / length(uv) / SDF / noise → float) ← float lane
|
|
31
|
+
│ └─ CosinePalette (float → colour)
|
|
32
|
+
└─ MaskSource (SDF / noise → float) ← mask lane
|
|
33
|
+
└─ SDFColourMask (colour × mask → colour)
|
|
34
|
+
└─ Overlays
|
|
35
|
+
|
|
36
|
+
`CosinePalette` is the bridge between the scalar lane and the colour lane. Any float can drive it -- that's what makes it so broadly composable.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## System Layers
|
|
41
|
+
|
|
42
|
+
### Core Infrastructure
|
|
43
|
+
|
|
44
|
+
- `ShaderPipeline` -- fragment compositor, owns material.colorNode, reduces fragment stages
|
|
45
|
+
- `VertexPipeline` -- vertex compositor, owns material.positionNode, reduces vertex stages
|
|
46
|
+
- `BackdropPass` -- renders scene to a RenderTarget, exposes as texture for blur/glass effects
|
|
47
|
+
- `useShaderStage(fn, order, stage?)` -- registers a block into the correct pipeline
|
|
48
|
+
- `useCSSColourUniform(varName)` -- CSS var → TSL uniform, theme-reactive via inject
|
|
49
|
+
- `useCSSFloatUniform(varName)` -- CSS numeric var → float uniform
|
|
50
|
+
- `useThemeProvider` -- provides `{ mode, palette, contrast }` to the component tree
|
|
51
|
+
- `colour.js` -- resolveColour() handles hex, rgb, hsl, hsla, oklch → THREE.Color
|
|
52
|
+
- `useWaveLayerDefaults(index, total)` -- generates offset noise params for wave stacking
|
|
53
|
+
|
|
54
|
+
### Stage Routing
|
|
55
|
+
|
|
56
|
+
// useShaderStage.js -- stage: 'fragment' | 'vertex' | 'ray'
|
|
57
|
+
export function useShaderStage(stageFn, order = 0, stage = 'fragment') {
|
|
58
|
+
const { register, unregister } = inject('shaderPipeline')
|
|
59
|
+
onMounted(() => register(stageFn, order, stage))
|
|
60
|
+
onUnmounted(() => unregister(stageFn))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ShaderPipeline reduces three targets
|
|
64
|
+
material.colorNode = fragmentStages.reduce((n, { fn }) => fn(n), vec4(0, 0, 0, 1))
|
|
65
|
+
material.positionNode = vertexStages.reduce((n, { fn }) => fn(n), positionLocal)
|
|
66
|
+
// Ray pipeline: screen coord → normalised ray → consumed by terminal generator
|
|
67
|
+
const rayNode = rayStages.reduce((n, { fn }) => fn(n), screenRayNode)
|
|
68
|
+
|
|
69
|
+
### Ray Pipeline
|
|
70
|
+
|
|
71
|
+
Ray-based generators (SkyAtmosphere, RaymarchTunnel) consume a 3D ray direction rather than a 2D UV. The Ray Pipeline converts screen coordinates to a ray, applies 3D transforms (tilt, fisheye, reflection), then feeds into a terminal generator. Ray transformer blocks use `stage: 'ray'`.
|
|
72
|
+
|
|
73
|
+
**Terminal blocks** -- blocks marked `terminal: true` always sit at `order: 0` and consume the ray output directly. UV transformers have no effect on them. The pipeline compositor warns if a UV transformer is registered before a terminal block.
|
|
74
|
+
|
|
75
|
+
### Debug / DX
|
|
76
|
+
|
|
77
|
+
- `ShaderDebugger` -- visualises output of any named stage mid-pipeline
|
|
78
|
+
- `ShaderInspector` -- UV coords, stage names, node graph overlay in dev mode
|
|
79
|
+
- `useShaderPerf` -- tracks GPU frame time per pipeline
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Block Registry
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## I. Primitives
|
|
88
|
+
|
|
89
|
+
> Raw values, constants, and data sources.
|
|
90
|
+
|
|
91
|
+
### Constants & Values
|
|
92
|
+
|
|
93
|
+
1. `FloatConst` -- static float value
|
|
94
|
+
2. `Vec2Const` -- static 2D vector
|
|
95
|
+
3. `Vec3Const` -- static 3D vector
|
|
96
|
+
4. `Vec4Const` -- static RGBA vector
|
|
97
|
+
5. `ColourConst` -- THREE.Color → vec3 uniform
|
|
98
|
+
6. `BoolConst` -- 0 or 1 float
|
|
99
|
+
7. `IntConst` -- integer uniform
|
|
100
|
+
8. `AngleConst` -- degrees-in, radians-out convenience wrapper
|
|
101
|
+
|
|
102
|
+
### Reactive Uniforms
|
|
103
|
+
|
|
104
|
+
9. `FloatUniform` -- JS-reactive float (prop-driven)
|
|
105
|
+
10. `ColourUniform` -- JS-reactive colour (prop-driven)
|
|
106
|
+
11. `Vec2Uniform` -- JS-reactive 2D vector
|
|
107
|
+
12. `CSSColourUniform` -- reads from a CSS custom property
|
|
108
|
+
13. `CSSFloatUniform` -- reads a numeric CSS custom property
|
|
109
|
+
14. `TimeUniform` -- exposes `time` node, speed and offset controls
|
|
110
|
+
15. `DeltaTimeUniform` -- frame delta for frame-rate-independent effects
|
|
111
|
+
16. `MouseUniform` -- normalised mouse/pointer position vec2 (NDC -1..1)
|
|
112
|
+
17. `ScrollUniform` -- normalised scroll progress float (Lenis integration)
|
|
113
|
+
18. `ViewportUniform` -- resolution vec2, updates on resize
|
|
114
|
+
19. `AspectUniform` -- viewport width/height ratio float
|
|
115
|
+
20. `DevicePixelRatioUniform` -- DPR float
|
|
116
|
+
|
|
117
|
+
### Coordinate Sources
|
|
118
|
+
|
|
119
|
+
21. `UVSource` -- base uv(), entry point for UV chains
|
|
120
|
+
22. `WorldPositionSource` -- vertex world position
|
|
121
|
+
23. `NormalSource` -- vertex/fragment normal vec3
|
|
122
|
+
24. `DepthSource` -- fragment depth float
|
|
123
|
+
25. `ScreenUVSource` -- screen-space UV (0--1 across viewport)
|
|
124
|
+
26. `TiledUVSource` -- uv() pre-tiled by a repeat vec2
|
|
125
|
+
|
|
126
|
+
### Texture Samplers
|
|
127
|
+
|
|
128
|
+
27. `TextureSampler` -- standard texture2D lookup
|
|
129
|
+
28. `VideoSampler` -- HTMLVideoElement as texture
|
|
130
|
+
29. `CanvasSampler` -- HTMLCanvasElement as texture (live 2D canvas)
|
|
131
|
+
30. `CubemapSampler` -- environment map sampler
|
|
132
|
+
31. `NoiseSampler` -- samples a pre-baked noise texture
|
|
133
|
+
32. `LUTSampler` -- 3D LUT texture for colour grading
|
|
134
|
+
33. `GradientMapSampler` -- 1D gradient texture lookup by luminance
|
|
135
|
+
|
|
136
|
+
### Math Primitives
|
|
137
|
+
|
|
138
|
+
34. `Sine` -- sin(x) node
|
|
139
|
+
35. `Cosine` -- cos(x) node
|
|
140
|
+
36. `Abs` -- abs(x) node
|
|
141
|
+
37. `Floor` -- floor(x) node
|
|
142
|
+
38. `Fract` -- fract(x) node
|
|
143
|
+
39. `Clamp01` -- clamp(x, 0, 1)
|
|
144
|
+
40. `Remap` -- remaps a value from one range to another
|
|
145
|
+
41. `OneMinus` -- 1.0 - x
|
|
146
|
+
42. `Saturate` -- alias for Clamp01
|
|
147
|
+
43. `Step` -- step(edge, x)
|
|
148
|
+
44. `Smoothstep` -- smoothstep(a, b, x)
|
|
149
|
+
45. `Lerp` -- mix(a, b, t) as an explicit primitive
|
|
150
|
+
|
|
151
|
+
### Channel Ops
|
|
152
|
+
|
|
153
|
+
46. `SplitChannels` -- exposes .r .g .b .a as individual float nodes
|
|
154
|
+
47. `MergeChannels` -- combines float nodes into a vec4
|
|
155
|
+
48. `SwizzleXY` -- returns .xy from a vec4
|
|
156
|
+
49. `SwizzleZW` -- returns .zw from a vec4
|
|
157
|
+
50. `AlphaExtract` -- isolates alpha channel as float
|
|
158
|
+
|
|
159
|
+
### Alpha / Compositing
|
|
160
|
+
|
|
161
|
+
51. `CoverageAlpha` -- ★ NEW -- transparency based on max channel coverage (used by ColorBends transparent mode). Alpha = max(r, g, b) rather than a fixed 1.0. Essential for shaders that generate shape from colour intensity.
|
|
162
|
+
52. `SunDirectionUniform` ★ NEW -- animated or mouse-driven sun elevation. Inactive: `sin(time)/8 - 0.2`. Active: `-mouse.y / res.y`. Outputs normalised vec3 via `rotX(vec3(0,0,1), angle * 5 + 0.5)`. Used by SkyAtmosphere.
|
|
163
|
+
53. `RayCameraUniform` ★ NEW -- source node for the Ray Pipeline. Converts screen fragment coord to a centred, aspect-corrected 3D direction with a fixed z depth.
|
|
164
|
+
54. `StateBufferSampler` ★ NEW -- reads vec3 values from specific texels of a small state texture written by a previous render pass. Used by HeightmapTerrain to read camera position from texel (0.5, 0.5) and rotation from texel (1.5, 0). This is the multi-pass state pattern: one pass writes simulation state as pixel values, the next pass reads it back. Takes `texture`, `texelCoord`.
|
|
165
|
+
55. `HeightmapSampler` ★ NEW -- samples a height/noise texture with LOD and a coordinate scale factor. Distinct from `NoiseSampler` in that it's specifically for terrain elevation lookup with explicit `textureLod(..., 0)` to prevent mip-blurring artefacts on terrain edges. Takes `texture`, `scale`, `offset`.
|
|
166
|
+
|
|
167
|
+
### Complex Number Math ★ NEW
|
|
168
|
+
|
|
169
|
+
56. `ComplexDiv(a, b)` -- complex number division. `vec2((a.x*b.x + a.y*b.y) / dot(b,b), (a.y*b.x - a.x*b.y) / dot(b,b))`. Used in imaginary2 for the Möbius transformation `(z-p)/(z-q)`.
|
|
170
|
+
57. `ComplexLog(z)` -- complex natural logarithm. `vec2(log(length(z)), atan(z.y, z.x))`. Produces `(ln|z|, arg(z))` -- the magnitude and angle of a complex number. Used in imaginary2 to compute the winding angle between two poles.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## IIa. Ray Transformers ★ NEW CATEGORY
|
|
175
|
+
|
|
176
|
+
> Ray → Ray. Operate in 3D ray space rather than 2D UV space. All blocks here use `stage: 'ray'` and feed into terminal ray-based generators.
|
|
177
|
+
|
|
178
|
+
1. `FisheyeRay` -- applies fisheye lens distortion to a ray direction before normalisation. `ray.z -= dot(ray.xy, ray.xy) * 0.5`. Produces the characteristic wide-angle sky dome look.
|
|
179
|
+
2. `RayTiltBasis` -- tilts the ray via an orthonormal basis (i, j, k) constructed from a target forward direction. `ray = ray.x*i + ray.y*j + ray.z*k`. Controls camera elevation. Used in SkyAtmosphere to angle the view upward.
|
|
180
|
+
3. `RaySphereReflect` -- reflects the ray off a virtual sphere at a given centre and radius. Used in SkyAtmosphere's sphere mode. The reflection creates a mirrored panoramic ball effect.
|
|
181
|
+
4. `RayRotateX` -- rotates ray around the X axis by a given angle
|
|
182
|
+
5. `RayRotateY` -- rotates ray around the Y axis
|
|
183
|
+
6. `RayRotateZ` -- rotates ray around the Z axis
|
|
184
|
+
7. `RayMouseOrbit` -- orbits ray direction based on mouse position (horizontal = yaw, vertical = pitch)
|
|
185
|
+
8. `RayAutoOrbit` -- continuously orbits ray direction over time at a given speed
|
|
186
|
+
9. `RayFromStateBuffer` ★ NEW -- reads camera position and rotation from a StateBufferSampler texture rather than from uniforms. Constructs the view ray using `rotateZ(rot.x) * rotateX(rot.y) * screenCoord`. Used by HeightmapTerrain for the multi-pass camera state pattern where position is simulated separately and written to a texture each frame.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## II. Generators
|
|
191
|
+
|
|
192
|
+
> UV → colour. The starting point of every pipeline.
|
|
193
|
+
|
|
194
|
+
### Gradients
|
|
195
|
+
|
|
196
|
+
1. `LinearGradient` -- two-colour gradient along an axis
|
|
197
|
+
2. `RadialGradient` -- circular gradient from a centre point
|
|
198
|
+
3. `ConicGradient` -- angular sweep gradient
|
|
199
|
+
4. `DiamondGradient` -- Chebyshev-distance based diamond shape
|
|
200
|
+
5. `SweepGradient` -- atan2-based full 360° sweep
|
|
201
|
+
6. `MultiStopGradient` -- n-stop gradient via 1D LUT texture
|
|
202
|
+
7. `DiagonalGradient` -- gradient along an arbitrary angle
|
|
203
|
+
8. `FocalGradient` -- radial with offset focal point
|
|
204
|
+
9. `MeshGradient` -- interpolation across a 2D grid of colour points
|
|
205
|
+
10. `NoisyGradient` -- linear gradient with per-pixel noise offset
|
|
206
|
+
|
|
207
|
+
### Wave & Bend
|
|
208
|
+
|
|
209
|
+
11. `WaveColourLayer` -- noise-driven colour band (Stripe-style). Each layer has its own `noiseFreq`, `noiseSpeed`, `noiseFlow`, `noiseSeed`, `noiseFloor`, `noiseCeil`. Stack multiple instances with offset params.
|
|
210
|
+
12. `WaveBendLayer` ★ NEW -- ColorBends-style per-colour wave. Uses `sin(s.yx * freq)`
|
|
211
|
+
- length-based intensity with optional warpStrength domain feedback loop. Different aesthetic to WaveColourLayer -- produces broader, bending colour bands rather than flowing noise ribbons. Stack one per colour.
|
|
212
|
+
|
|
213
|
+
### Solid / Flat
|
|
214
|
+
|
|
215
|
+
13. `SolidColour` -- flat colour fill
|
|
216
|
+
14. `Checkerboard` -- two-colour tile grid
|
|
217
|
+
15. `Stripes` -- repeating stripe pattern, controllable angle and width
|
|
218
|
+
16. `Dots` -- polka dot grid, smooth or hard edge
|
|
219
|
+
17. `Grid` -- line grid overlay
|
|
220
|
+
|
|
221
|
+
### Noise & Organic
|
|
222
|
+
|
|
223
|
+
18. `ValueNoise` -- classic smooth value noise
|
|
224
|
+
19. `GradientNoise` -- gradient noise (the `hash()` + smooth interpolation pattern used in Grainient and Shadertoy snippets 3 & 4)
|
|
225
|
+
20. `SimplexNoise` -- simplex noise
|
|
226
|
+
21. `FBMNoise` -- fractal Brownian motion (layered octaves)
|
|
227
|
+
22. `DomainWarpedNoise` -- FBM with domain warping
|
|
228
|
+
23. `CellularNoise` -- Worley/Voronoi noise
|
|
229
|
+
24. `VoronoiEdges` -- Voronoi cell edge lines only
|
|
230
|
+
25. `RidgedNoise` -- abs() of noise for mountain-ridge look
|
|
231
|
+
26. `BillowNoise` -- folded noise for cloud-like forms
|
|
232
|
+
27. `CurlNoise` -- divergence-free noise for fluid-like motion
|
|
233
|
+
|
|
234
|
+
### Gradient Blend (Noise-Rotated)
|
|
235
|
+
|
|
236
|
+
28. `NoisyGradientBlend` ★ NEW -- the full Grainient/Shadertoy gradient pattern as a single generator: GradientNoise → noise-driven UV rotation → sine warp → rotated two-layer colour blend. Exposes all params individually so the pipeline can still intercept at any stage, but provides a convenient self-contained starting point.
|
|
237
|
+
|
|
238
|
+
### Geometric
|
|
239
|
+
|
|
240
|
+
29. `Circle` -- smooth SDF circle
|
|
241
|
+
30. `Rectangle` -- SDF rounded rectangle
|
|
242
|
+
31. `Triangle` -- SDF equilateral or arbitrary triangle
|
|
243
|
+
32. `Hexagon` -- SDF hexagon
|
|
244
|
+
33. `Star` -- SDF n-pointed star
|
|
245
|
+
34. `Ring` -- annulus/donut SDF
|
|
246
|
+
35. `Line` -- SDF line segment
|
|
247
|
+
36. `Cross` -- SDF plus/cross shape
|
|
248
|
+
37. `Arrow` -- SDF directional arrow
|
|
249
|
+
38. `Polygon` -- SDF regular n-gon
|
|
250
|
+
|
|
251
|
+
### Patterns
|
|
252
|
+
|
|
253
|
+
39. `HexGrid` -- hexagonal tiling pattern
|
|
254
|
+
40. `TriangleGrid` -- triangular tiling
|
|
255
|
+
41. `PenroseTiling` -- quasi-periodic Penrose pattern
|
|
256
|
+
42. `TruchetTiles` -- randomised quarter-circle tile pattern
|
|
257
|
+
43. `WavyStripes` -- sine-modulated stripe generator
|
|
258
|
+
44. `Chevron` -- repeating chevron/zigzag
|
|
259
|
+
45. `Houndstooth` -- classic houndstooth textile pattern
|
|
260
|
+
46. `Tartan` -- crossed stripe pattern
|
|
261
|
+
47. `Halftone` -- dot-grid halftone screen
|
|
262
|
+
48. `CrossHatch` -- angled line cross-hatch
|
|
263
|
+
|
|
264
|
+
### Procedural Natural
|
|
265
|
+
|
|
266
|
+
49. `Marble` -- sine + FBM marble veining
|
|
267
|
+
50. `Wood` -- concentric ring wood grain
|
|
268
|
+
51. `Flame` -- animated upward noise-driven flame
|
|
269
|
+
52. `Water` -- animated layered sine water surface
|
|
270
|
+
53. `Clouds` -- FBM + remap cloud formation
|
|
271
|
+
|
|
272
|
+
### Volumetric / Raymarching
|
|
273
|
+
|
|
274
|
+
54. `RaymarchTunnel` ★ NEW -- the Shadertoy tunnel effect. Raymarching loop in a fragment shader: SDF sphere + fractional XY tiling + additive volumetric glow per step + chromatic time-offset colour. Self-contained, terminal: true. Exposes: `glowColour`, `glowFalloff`, `speed`, `tiling`, `sphereRadius`.
|
|
275
|
+
|
|
276
|
+
55. `SkyAtmosphere` ★ NEW -- physically-approximated sky dome. Consumes a ray direction from the Ray Pipeline plus a sun direction vec3. Core is an exponential atmospheric gradient with per-channel Rayleigh scattering coefficients `vec3(0.1, 0.3, 0.6)`: `exp2(-(ray.y - raySun*0.5) / scatterCoeffs)`. Darkens sky when sun is low, adds sun disc via `smoothstep(0.9995, 1.0, raySun)`. terminal: true, stage: 'ray'. Exposes: `scatterCoeffs`, `sunDiscSharpness`, `sunDiscColour`, `groundFade`.
|
|
277
|
+
|
|
278
|
+
56. `BilinearGradient` ★ NEW -- four-corner colour interpolation. The simplest possible 2D colour field: mix bottom-left/right along X, mix top-left/right along X, then mix the two results along Y. Zero noise, zero animation, zero distortion. `mix(mix(bl, br, uv.x), mix(tl, tr, uv.x), uv.y)` Exposes: `colorBL`, `colorBR`, `colorTL`, `colorTR`. Accepts CSS var props for all four corners for theme reactivity.
|
|
279
|
+
|
|
280
|
+
57. `HeightmapTerrain` ★ NEW -- DDA (Digital Differential Analysis) heightmap traversal producing a voxel-style terrain view. Consumes camera position (vec3) and ray direction (vec3) from the Ray Pipeline or StateBufferSampler.
|
|
281
|
+
|
|
282
|
+
Internally:
|
|
283
|
+
|
|
284
|
+
- Applies a hexagonal grid skew via `F = (sqrt(3)-1)/2` -- skews the XY coordinate space so a square grid behaves like a hexagonal one, giving the terrain its distinctive isometric diamond facets
|
|
285
|
+
- Marches through 2D grid cells using DDA, at each boundary comparing ray Z against the elevation function to find intersection
|
|
286
|
+
- Elevation is `cos`-based terrain shape + distance falloff + HeightmapSampler noise
|
|
287
|
+
- Shading: white/blue facets from `fract(pos.xy)` proximity to grid edges and diagonal lines; shadow tones from adjacent cell elevation difference
|
|
288
|
+
|
|
289
|
+
Exposes: `heightTexture`, `gridScale`, `terrainAmplitude`, `terrainRadius`, `facetColourA` (light), `facetColourB` (water/shadow), `marchSteps`. terminal: true, hdr: false, stage: 'ray'.
|
|
290
|
+
|
|
291
|
+
### Scalar Generators ★ NEW CATEGORY
|
|
292
|
+
|
|
293
|
+
> These take a float input and output a colour. They sit between a scalar source (uv.y, length(uv), SDF result, noise value) and the colour pipeline. The float input can come from any upstream node -- that composability is the point.
|
|
294
|
+
|
|
295
|
+
58. `CosinePalette` ★ NEW -- **the most reusable block in the system**. Iq's cosine palette formula: `a + b * cos(TAU * (c*t + d))` where a, b, c, d are vec3 parameters and t is a scalar float input. The four parameter vectors control:
|
|
296
|
+
- `a` -- colour offset (brightness centre)
|
|
297
|
+
- `b` -- colour amplitude (contrast)
|
|
298
|
+
- `c` -- frequency per channel (how fast each channel oscillates)
|
|
299
|
+
- `d` -- phase offset per channel (colour shift) Accepts CSS var props for all four vectors for full theme reactivity. Has a `frequency` multiplier prop for the `mul(0.6 * 2.0, PI)` pattern in imaginary2. Input `t` is wired from the previous pipeline stage or an explicit `scalarInput` prop.
|
|
300
|
+
|
|
301
|
+
59. `RingField` ★ NEW -- one or more ring-shaped SDF fields accumulated into a single float mask. Each ring is `abs(length(uv + offset) - radius)`. Multiple rings are additive. The flare8/flare9 pattern. Accepts an array of ring descriptors `[{ offset: vec2, radius: float, scale: float }]`. Outputs a float mask consumed downstream by `CosinePalette` and `SDFColourMask`. Also includes a built-in vignette term: `oneMinus(length(uv * vignetteScale))`.
|
|
302
|
+
|
|
303
|
+
60. `ChebyshevNoiseField` ★ NEW -- noise-distorted Chebyshev box shape. The genuary22 pattern: `1 - max(abs(uv.x * scaleX * n), abs(uv.y * scaleY * n2))` where n and n2 are two octaves of simplex noise at different frequencies. Produces a noise-eroded cross/box silhouette. Outputs a float mask.
|
|
304
|
+
|
|
305
|
+
61. `ComplexPlaneField` ★ NEW -- complex number plane visualisation. The imaginary2 effect: places two poles p and q at angle/distance from origin, computes the Möbius transformation `(z-p)/(z-q)`, takes the complex log, extracts the imaginary component (winding angle), combines with `exp(z.x)/exp(z.y)` for the radial term. Outputs a float t for `CosinePalette`. terminal: true. Exposes: `poleAngle`, `poleDistance`, `imaginaryWeight`, `radialWeight`.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## III. UV Transformers
|
|
310
|
+
|
|
311
|
+
> UV → UV. These never produce colour.
|
|
312
|
+
|
|
313
|
+
### Basic Transforms
|
|
314
|
+
|
|
315
|
+
1. `UVScale` -- uniform or non-uniform scale around a pivot
|
|
316
|
+
2. `UVTranslate` -- offset UV by a vec2
|
|
317
|
+
3. `UVRotate` -- rotate around a pivot point
|
|
318
|
+
4. `UVFlipX` -- mirror horizontally
|
|
319
|
+
5. `UVFlipY` -- mirror vertically
|
|
320
|
+
6. `UVFlipXY` -- mirror both axes
|
|
321
|
+
7. `UVSwapAxes` -- transpose x and y
|
|
322
|
+
8. `UVCentre` -- recentres UV to -0.5…0.5 space and back
|
|
323
|
+
9. `UVAspectCorrect` ★ NEW -- multiplies UV x by (width/height) so downstream effects are circular rather than elliptical. Essential whenever working with length() or radial operations. Used in ColorBends, Grainient, and most noise-rotation patterns.
|
|
324
|
+
|
|
325
|
+
### Mouse / Pointer
|
|
326
|
+
|
|
327
|
+
10. `UVParallax` ★ NEW -- offsets UV by pointer NDC position scaled by a parallax factor. Creates the illusion of depth as the mouse moves. Used in ColorBends. `p += uPointer * parallax * 0.1`
|
|
328
|
+
11. `UVMousePull` ★ NEW -- additive UV pull toward the current pointer position, scaled by an influence factor. Different from Parallax -- this warps the UV toward the cursor rather than shifting the whole field. Used in ColorBends. `q += (pointer - rp) * mouseInfluence * 0.2`
|
|
329
|
+
|
|
330
|
+
### Noise-Driven Rotation
|
|
331
|
+
|
|
332
|
+
12. `UVNoiseRotate` ★ NEW -- rotates UV by an angle derived from GradientNoise sampled at `(time * speed, uv.x * uv.y)`. The core of the Grainient and Shadertoy gradient looks. Exposes `noiseScale`, `rotationAmount` (degrees of max rotation), `speed`. The noise output remapped to `(degree - 0.5) * rotationAmount + 180` prevents the rotation from snapping back to zero.
|
|
333
|
+
|
|
334
|
+
### Tiling & Repetition
|
|
335
|
+
|
|
336
|
+
13. `UVTile` -- tile by integer repeat count
|
|
337
|
+
14. `UVMirrorTile` -- alternating mirrored tiles
|
|
338
|
+
15. `UVOffsetTile` -- brick-offset tiling
|
|
339
|
+
16. `UVHexTile` -- hexagonal tiling coordinate system
|
|
340
|
+
17. `UVClamp` -- clamps UV to 0--1 (no repeat)
|
|
341
|
+
18. `UVCrop` -- maps a sub-region to full 0--1 space
|
|
342
|
+
|
|
343
|
+
### Distortion
|
|
344
|
+
|
|
345
|
+
19. `UVWarp` -- sine-based two-axis warp
|
|
346
|
+
20. `UVSineWarpXY` ★ NEW -- independent sine warp on both axes simultaneously with frequency multiplier on Y (the `frequency * 1.5` pattern). This is the specific warp used in Grainient and Shadertoy snippets 3 & 4: `tuv.x += sin(tuv.y * freq + speed) / amplitude` `tuv.y += sin(tuv.x * freq * 1.5 + speed) / (amplitude * 0.5)` Distinct from `UVWarp` (which is a single vec2 sine) -- this is asymmetric by design.
|
|
347
|
+
21. `UVTwirl` -- spiral distortion from centre
|
|
348
|
+
22. `UVFisheye` -- barrel/fisheye lens distortion
|
|
349
|
+
23. `UVPinch` -- pinch toward or away from a point
|
|
350
|
+
24. `UVBulge` -- radial bulge/push from centre
|
|
351
|
+
25. `UVSinWave` -- single-axis sine wave displacement
|
|
352
|
+
26. `UVRipple` -- radial ripple from a point
|
|
353
|
+
27. `UVShear` -- shear/skew transform
|
|
354
|
+
28. `UVPolar` -- converts Cartesian UV to polar coordinates
|
|
355
|
+
29. `UVCartesian` -- converts polar back to Cartesian
|
|
356
|
+
|
|
357
|
+
### Noise-Driven
|
|
358
|
+
|
|
359
|
+
30. `UVNoiseWarp` -- FBM-driven domain warp
|
|
360
|
+
31. `UVTurbulence` -- high-frequency noise displacement
|
|
361
|
+
32. `UVCurlWarp` -- curl noise UV advection
|
|
362
|
+
33. `UVCrystal` -- Voronoi-warped UV
|
|
363
|
+
34. `UVMelt` -- slow downward gravity-like noise drip
|
|
364
|
+
|
|
365
|
+
### Time-Driven
|
|
366
|
+
|
|
367
|
+
35. `UVScroll` -- constant UV translation over time
|
|
368
|
+
36. `UVScrollX` -- horizontal scroll only
|
|
369
|
+
37. `UVScrollY` -- vertical scroll only
|
|
370
|
+
38. `UVPulse` -- oscillating scale over time
|
|
371
|
+
39. `UVOrbit` -- UV rotates around a centre point over time
|
|
372
|
+
40. `UVBreath` -- gentle scale oscillation
|
|
373
|
+
|
|
374
|
+
### Mapping
|
|
375
|
+
|
|
376
|
+
41. `UVSpherical` -- spherical projection mapping
|
|
377
|
+
42. `UVCylindrical` -- cylindrical projection
|
|
378
|
+
43. `UVPlanar` -- planar world-space projection
|
|
379
|
+
44. `UVTriplanar` -- world-normal-blended triplanar mapping
|
|
380
|
+
45. `UVScreenSpace` -- projects to screen-space UV regardless of geometry
|
|
381
|
+
46. `UVReflect` -- reflection vector-based UV
|
|
382
|
+
47. `UVMatCap` -- MatCap/LitSphere normal-based UV
|
|
383
|
+
48. `UVFlowMap` -- samples a flow map texture to drive UV offset
|
|
384
|
+
|
|
385
|
+
### Experimental
|
|
386
|
+
|
|
387
|
+
49. `UVKaleidoscope` -- mirror-fold UV into kaleidoscope symmetry
|
|
388
|
+
50. `UVFractalZoom` -- recursive self-similar UV zoom
|
|
389
|
+
51. `UVPixelate` -- floor UV to a pixel grid
|
|
390
|
+
52. `UVScan` -- animated scanline sweep
|
|
391
|
+
53. `UVShatter` -- Voronoi-cell-based UV fragmentation
|
|
392
|
+
54. `UVMosaic` -- large-block mosaic coordinate snapping
|
|
393
|
+
55. `UVFeedback` -- previous frame UV offset (requires render target pass)
|
|
394
|
+
|
|
395
|
+
### Banding & Repetition ★ NEW
|
|
396
|
+
|
|
397
|
+
56. `UVColumnOffset` ★ NEW -- offsets UV Y by a column-index-derived value, creating vertical bands that are each slightly offset. The dawn4/5 pattern: `floor(uv.x * repetitions) * repetitions * factor`. Combined with sine: `uv.y += columnOffset + sin(s * softness)`. Creates the vertical stripe morphing effect. Exposes: `repetitions`, `offsetFactor`, `sineSoftness`.
|
|
398
|
+
57. `UVFractBand` ★ NEW -- applies `fract(uv.y * frequency) * scale` to create horizontal banding via the fractional part. Outputs a float that adds to colour via `pow(s, 2.0)`. The dawn2 shimmer band pattern. Exposes: `frequency`, `scale`, `power`. Note: this outputs a float additive term, not a UV -- it straddles the UV/Scalar boundary, best used as a `ScalarAddend` passed to `CosinePalette` or added to the final colour directly.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## IV. Colour Ops
|
|
403
|
+
|
|
404
|
+
> colour → colour. Applied to the output of generators or previous ops.
|
|
405
|
+
|
|
406
|
+
### Blending
|
|
407
|
+
|
|
408
|
+
1. `MixBlend` -- linear interpolation between two colour inputs
|
|
409
|
+
2. `MultiplyBlend` -- Multiply mode
|
|
410
|
+
3. `ScreenBlend` -- Screen mode
|
|
411
|
+
4. `OverlayBlend` -- Overlay mode
|
|
412
|
+
5. `SoftLightBlend` -- Soft Light
|
|
413
|
+
6. `HardLightBlend` -- Hard Light
|
|
414
|
+
7. `DifferenceBlend` -- Difference (abs subtraction)
|
|
415
|
+
8. `ExclusionBlend` -- softer Difference
|
|
416
|
+
9. `DodgeBlend` -- colour dodge
|
|
417
|
+
10. `BurnBlend` -- colour burn
|
|
418
|
+
11. `AddBlend` -- additive blend
|
|
419
|
+
12. `SubtractBlend` -- subtractive blend
|
|
420
|
+
13. `DivideBlend` -- division blend
|
|
421
|
+
14. `DarkenBlend` -- min() per channel
|
|
422
|
+
15. `LightenBlend` -- max() per channel
|
|
423
|
+
|
|
424
|
+
### Gradient Blend Ops
|
|
425
|
+
|
|
426
|
+
16. `RotatedGradientBlend` ★ NEW -- blends two colours along a rotated axis using smoothstep. The `(tuv * Rot(angle)).x` pattern from Grainient and Shadertoy snippets 3 & 4. Takes `colorA`, `colorB`, `angle`, `edge0`, `edge1`. Used as the base colour layer before the Y-axis layer mix.
|
|
427
|
+
|
|
428
|
+
### Grading
|
|
429
|
+
|
|
430
|
+
17. `BrightnessContrast` -- lift and compress tonal range
|
|
431
|
+
18. `Exposure` -- multiply-based exposure stop adjustment
|
|
432
|
+
19. `Gamma` -- power-curve gamma correction. `pow(max(col, 0), 1/gamma)`
|
|
433
|
+
20. `Levels` -- input/output range remapping
|
|
434
|
+
21. `Curves` -- arbitrary tone curve via 1D LUT texture
|
|
435
|
+
22. `WhiteBalance` -- colour temperature and tint adjustment
|
|
436
|
+
23. `LiftGammaGain` -- cinematic three-way colour grade
|
|
437
|
+
24. `Hue` -- hue rotation in HSL space
|
|
438
|
+
25. `Saturation` -- HSL saturation adjustment. `mix(vec3(luma), col, saturation)`
|
|
439
|
+
26. `Vibrance` -- saturation boost that protects already-saturated colours
|
|
440
|
+
27. `Lightness` -- HSL lightness adjustment
|
|
441
|
+
|
|
442
|
+
### Channel Operations
|
|
443
|
+
|
|
444
|
+
28. `ChannelMixer` -- arbitrary RGB channel mixing matrix
|
|
445
|
+
29. `SwapChannels` -- remap colour channels
|
|
446
|
+
30. `Tint` -- multiply colour by a tint colour
|
|
447
|
+
31. `ColourBalance` -- shadows/midtones/highlights colour push
|
|
448
|
+
32. `SelectiveColour` -- adjust a specific hue range only
|
|
449
|
+
|
|
450
|
+
### Remapping
|
|
451
|
+
|
|
452
|
+
33. `Invert` -- 1 - colour
|
|
453
|
+
34. `Posterise` -- quantise to n colour steps
|
|
454
|
+
35. `Threshold` -- binary black/white from luminance
|
|
455
|
+
36. `ColourRamp` -- map luminance to a colour gradient
|
|
456
|
+
37. `DuoTone` -- map shadows/highlights to two colours
|
|
457
|
+
38. `Tritone` -- three-stop colour mapping
|
|
458
|
+
39. `FalseColour` -- scientific false colour mapping
|
|
459
|
+
40. `ThermalMap` -- cool-to-hot colour ramp
|
|
460
|
+
41. `Solarise` -- Sabattier photographic effect
|
|
461
|
+
|
|
462
|
+
### Colour Space
|
|
463
|
+
|
|
464
|
+
42. `LinearToSRGB` -- gamma encode for display
|
|
465
|
+
43. `SRGBToLinear` -- gamma decode to linear light
|
|
466
|
+
44. `RGBToHSL` -- colour space conversion
|
|
467
|
+
45. `HSLToRGB` -- colour space conversion
|
|
468
|
+
46. `RGBToOKLab` -- perceptually uniform colour space
|
|
469
|
+
47. `OKLabToRGB` -- back to RGB from OKLab
|
|
470
|
+
48. `Desaturate` -- perceptual luminance-preserving greyscale
|
|
471
|
+
49. `MonochromeTint` -- desaturate then tint with a single colour
|
|
472
|
+
50. `SplitTone` -- separate tint for shadows vs highlights
|
|
473
|
+
|
|
474
|
+
### Time-Based Colour
|
|
475
|
+
|
|
476
|
+
51. `DayNightCycle` ★ NEW -- sinusoidal interpolation between two sets of colours over time. `sin(time * speed)` remapped to 0--1 with a power curve for easing. Used in Shadertoy snippet 4 to cycle between light and dark palettes. `t = (sign(cycle) * pow(abs(cycle), 0.6) + 1.) / 2.` Takes `colorsLight[]`, `colorsDark[]`, `speed`. Returns the time-blended colour set.
|
|
477
|
+
|
|
478
|
+
52. `ChromaticAberration` -- per-channel UV offset
|
|
479
|
+
|
|
480
|
+
### Tonemapping & Colour Science
|
|
481
|
+
|
|
482
|
+
53. `ACESTonemap` ★ NEW -- ACES filmic tone mapping via two 3x3 matrix transforms. Industry-standard operator that compresses HDR values to display range with a characteristic S-curve and warm highlight rolloff. Essential companion to any HDR-range generator (SkyAtmosphere, RaymarchTunnel, Bloom). Input/output both vec3 RGB. No uniforms required -- the matrices are constant.
|
|
483
|
+
54. `ReinhardTonemap` -- simple Reinhard `col / (col + 1)` tone compression
|
|
484
|
+
55. `TanhTonemap` ★ NEW -- `tanh(col * exposure)` soft sigmoid compression. Gentler than ACES -- preserves saturation and has no warm rolloff. Used in dawn5. Particularly good for procedural colour fields that can briefly exceed 1.0. `exposure` prop controls how aggressively values are compressed (dawn5 uses 2.5).
|
|
485
|
+
56. `ExponentialFog` ★ NEW -- depth/height based fog blend to a fog colour using `exp2(-depth * density)` falloff.
|
|
486
|
+
|
|
487
|
+
### SDF-to-Colour ★ NEW
|
|
488
|
+
|
|
489
|
+
57. `SDFColourMask` ★ NEW -- multiplies a colour by a float mask value. The `col.mul(t)` pattern that appears in every phobon shader -- applies a SDF or noise-derived float as a multiplicative mask to the colour. While technically equivalent to `MultiplyBlend` with a greyscale input, naming it explicitly makes the scalar-pipeline intent clear. Exposes: `maskInput` (float node), `clampMask` (bool, default true).
|
|
490
|
+
58. `SDFRadialMask` ★ NEW -- `oneMinus(length(uv * scale))` -- simple radial falloff from centre without smoothstep. Softer than Vignette, additive rather than darkening. The `r.addAssign(oneMinus(length(_uv.mul(vignette))))` pattern in flare8/9. Exposes: `scale`, `offset`.
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## V. Overlays
|
|
495
|
+
|
|
496
|
+
> colour → colour, additive or composited, post-process in nature.
|
|
497
|
+
|
|
498
|
+
### Film & Analogue
|
|
499
|
+
|
|
500
|
+
1. `Grain` -- white noise film grain (fract/sin/dot pattern)
|
|
501
|
+
2. `FilmGrain` ★ NEW -- hash-based film grain using length(hash(uv)) rather than the fract(sin(dot())) pattern. Produces a slightly coarser, more organic texture. Used in Shadertoy snippet 4. `filmGrainNoise = length(hash(vec2(uv.x, uv.y)))`. Subtracted from colour rather than added, darkening rather than brightening.
|
|
502
|
+
3. `ColourGrain` -- per-channel coloured grain
|
|
503
|
+
4. `FilmBurn` -- orange-edge light leak effect
|
|
504
|
+
5. `Halation` -- red glow bleed around highlights
|
|
505
|
+
6. `Dust` -- random static dust particle overlay
|
|
506
|
+
7. `Scratches` -- animated vertical film scratch lines
|
|
507
|
+
8. `AgedFilm` -- combined grain + scratch + tone shift preset
|
|
508
|
+
9. `CRTScanlines` -- horizontal scanline darkening. The simple vertical sine `sin(uv.y) / 2 + 0.7` pattern from the CRT snippet.
|
|
509
|
+
10. `CRTCurvature` -- barrel distortion + edge darkening
|
|
510
|
+
11. `VHSBleed` -- colour bleed + horizontal noise drift
|
|
511
|
+
|
|
512
|
+
### CRT / Screen Effects
|
|
513
|
+
|
|
514
|
+
12. `ChromaticScreenWaves` ★ NEW -- per-channel animated sine waves along the Y axis, producing a chromatic oscillation effect. Each RGB channel gets its own phase offset: `R: sin(20/res.y * uv.y + (-time*2 - 0.4)) / 10 + 0.85` `G: sin(20/res.y * uv.y + (-time*2)) / 10 + 0.85` `B: sin(20/res.y * uv.y + (-time*2 + 0.4)) / 10 + 0.85` Combined with screen stripe and applied as a multiplicative overlay. From the CRT Shadertoy snippet. Takes `amplitude`, `frequency`, `speed`, `phaseOffset` (the 0.4 channel separation).
|
|
515
|
+
|
|
516
|
+
### Spatial
|
|
517
|
+
|
|
518
|
+
13. `Vignette` -- edge darkening
|
|
519
|
+
14. `ColourVignette` -- vignette that shifts colour rather than just darkens
|
|
520
|
+
15. `Bloom` -- glow on bright areas (requires BackdropPass)
|
|
521
|
+
16. `LensFlare` -- procedural lens flare
|
|
522
|
+
17. `GodRays` -- volumetric light shaft from a point
|
|
523
|
+
18. `DepthFog` -- depth-based fog blend
|
|
524
|
+
19. `ChromaticAberration` -- RGB fringe at screen edges. The subtle UV offset pattern from the CRT snippet: `UR = U + vec2(0.001, 0)` etc.
|
|
525
|
+
20. `MotionBlur` -- velocity-based directional blur
|
|
526
|
+
21. `RadialBlur` -- zoom blur from centre
|
|
527
|
+
22. `TiltShift` -- simulated tilt-shift focus blur bands
|
|
528
|
+
|
|
529
|
+
### Texture & Surface
|
|
530
|
+
|
|
531
|
+
23. `Roughness` -- micro-detail noise
|
|
532
|
+
24. `PaperTexture` -- overlaid paper grain texture
|
|
533
|
+
25. `CanvasTexture` -- woven canvas fibre texture
|
|
534
|
+
26. `WatercolourEdge` -- soft paint bleed at edges
|
|
535
|
+
27. `HalftoneOverlay` -- dot-screen halftone blend
|
|
536
|
+
28. `CrossHatchOverlay` -- ink crosshatch texture
|
|
537
|
+
29. `RisographGrain` -- coarse risograph-style grain per layer
|
|
538
|
+
30. `PrintNoise` -- ink spread simulation
|
|
539
|
+
31. `FabricWeave` -- fine woven texture overlay
|
|
540
|
+
32. `ConcreteSurface` -- rough mineral surface texture
|
|
541
|
+
|
|
542
|
+
### Atmosphere
|
|
543
|
+
|
|
544
|
+
33. `Haze` -- additive white haze toward screen edges
|
|
545
|
+
34. `DustAtmosphere` -- floating particles in depth
|
|
546
|
+
35. `Bokeh` -- out-of-focus circular light discs
|
|
547
|
+
36. `Starfield` -- procedural star point field
|
|
548
|
+
37. `Aurora` -- layered animated colour bands
|
|
549
|
+
38. `LightShaft` -- single animated crepuscular ray
|
|
550
|
+
39. `Caustics` -- animated refracted light patterns
|
|
551
|
+
40. `HeatHaze` -- animated UV distortion simulating heat
|
|
552
|
+
41. `RainDrops` -- animated spherical lens drops on surface
|
|
553
|
+
42. `SnowOverlay` -- drifting particle snow
|
|
554
|
+
|
|
555
|
+
### Edge & Shape
|
|
556
|
+
|
|
557
|
+
43. `EdgeGlow` -- glow along detected colour edges
|
|
558
|
+
44. `OuterGlow` -- glow emanating outward from shapes
|
|
559
|
+
45. `InnerGlow` -- glow emanating inward from edges
|
|
560
|
+
46. `Outline` -- hard edge detection outline
|
|
561
|
+
47. `Emboss` -- directional normal-map style emboss
|
|
562
|
+
48. `NeonGlow` -- intense additive bloom on saturated colours
|
|
563
|
+
49. `DropShadow` -- procedural directional shadow offset
|
|
564
|
+
50. `FrostEdge` -- crystalline frost texture toward edges
|
|
565
|
+
51. `BurnEdge` -- darkened scorch mark at edges
|
|
566
|
+
52. `GlitchEdge` -- pixel displacement noise on detected edges
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## VI. Shader Conversions
|
|
571
|
+
|
|
572
|
+
> Each existing shader mapped to block compositions.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
### 1. ColorBends (vue-bits)
|
|
577
|
+
|
|
578
|
+
**What it does:** Rotatable, aspect-corrected, mouse-reactive field of colour bands. Each colour generates a sine-based wave layer. Blended with a length-based intensity and optional coverage-based transparency.
|
|
579
|
+
|
|
580
|
+
**Key insight:** The `q /= 0.5 + 0.2 * dot(q, q)` line is a lens/fisheye distortion applied in UV space before the wave layers. Worth exposing as a standalone UV op.
|
|
581
|
+
|
|
582
|
+
<ShaderPipeline>
|
|
583
|
+
<!-- UV prep -->
|
|
584
|
+
<UVAspectCorrect :order="0" />
|
|
585
|
+
<UVCentre :order="1" />
|
|
586
|
+
<UVRotate :angle-prop="rotation" :auto-rotate="autoRotate" :order="2" />
|
|
587
|
+
<UVScale :scale="scale" :order="3" />
|
|
588
|
+
<UVFisheye :strength="0.2" :order="4" /> <!-- q /= 0.5 + 0.2*dot(q,q) -->
|
|
589
|
+
<UVParallax :factor="parallax" :order="5" />
|
|
590
|
+
<UVMousePull :influence="mouseInfluence" :order="6" />
|
|
591
|
+
|
|
592
|
+
<!-- One WaveBendLayer per colour, stacked -->
|
|
593
|
+
<WaveBendLayer
|
|
594
|
+
v-for="(color, i) in colors"
|
|
595
|
+
:key="i"
|
|
596
|
+
:color="color"
|
|
597
|
+
:frequency="frequency"
|
|
598
|
+
:warp-strength="warpStrength"
|
|
599
|
+
:index="i"
|
|
600
|
+
:order="7 + i"
|
|
601
|
+
/>
|
|
602
|
+
|
|
603
|
+
<!-- Alpha mode -->
|
|
604
|
+
<CoverageAlpha v-if="transparent" :order="20" />
|
|
605
|
+
|
|
606
|
+
<!-- Overlays -->
|
|
607
|
+
<Grain :intensity="noise" :animated="true" :order="21" />
|
|
608
|
+
</ShaderPipeline>
|
|
609
|
+
|
|
610
|
+
**Uniform reactivity:** `rotation` and `autoRotate` drive a computed vec2 `(cos(rad), sin(rad))` uniform updated in the render loop. The rotation is not a UV transform in the traditional sense -- it's a rotation matrix applied before the wave layers, so it lives in the UV chain. `UVRotate` handles this with a `time-driven` prop for auto-rotation.
|
|
611
|
+
|
|
612
|
+
**New blocks identified:** `UVAspectCorrect`, `UVParallax`, `UVMousePull`, `WaveBendLayer`, `CoverageAlpha`
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
### 2. Grainient (vue-bits / OGL)
|
|
617
|
+
|
|
618
|
+
**What it does:** Noise-driven UV rotation, asymmetric sine warp, two-layer rotated gradient blend, grain + contrast/gamma/saturation post-processing.
|
|
619
|
+
|
|
620
|
+
<ShaderPipeline>
|
|
621
|
+
<!-- UV transforms -->
|
|
622
|
+
<UVAspectCorrect :order="0" />
|
|
623
|
+
<UVCentre :order="1" />
|
|
624
|
+
<UVScale :scale="zoom" :offset-x="centerX" :offset-y="centerY" :order="2" />
|
|
625
|
+
<UVNoiseRotate
|
|
626
|
+
:noise-scale="noiseScale"
|
|
627
|
+
:rotation-amount="rotationAmount"
|
|
628
|
+
:speed="0.1"
|
|
629
|
+
:order="3"
|
|
630
|
+
/>
|
|
631
|
+
<UVSineWarpXY
|
|
632
|
+
:frequency="warpFrequency"
|
|
633
|
+
:amplitude="warpAmplitude"
|
|
634
|
+
:speed="warpSpeed"
|
|
635
|
+
:order="4"
|
|
636
|
+
/>
|
|
637
|
+
|
|
638
|
+
<!-- Two colour layers blended by Y axis -->
|
|
639
|
+
<RotatedGradientBlend
|
|
640
|
+
:color-a="color3"
|
|
641
|
+
:color-b="color2"
|
|
642
|
+
:angle="-blendAngle"
|
|
643
|
+
:edge0="edge0"
|
|
644
|
+
:edge1="edge1"
|
|
645
|
+
:layer="1"
|
|
646
|
+
:order="5"
|
|
647
|
+
/>
|
|
648
|
+
<RotatedGradientBlend
|
|
649
|
+
:color-a="color2"
|
|
650
|
+
:color-b="color1"
|
|
651
|
+
:angle="-blendAngle"
|
|
652
|
+
:edge0="edge0"
|
|
653
|
+
:edge1="edge1"
|
|
654
|
+
:layer="2"
|
|
655
|
+
:order="6"
|
|
656
|
+
/>
|
|
657
|
+
<!-- Mix the two layers by tuv.y -->
|
|
658
|
+
<MixBlend :axis="'y'" :edge0="blendSoftness" :order="7" />
|
|
659
|
+
|
|
660
|
+
<!-- Post-processing -->
|
|
661
|
+
<BrightnessContrast :contrast="contrast" :order="8" />
|
|
662
|
+
<Saturation :amount="saturation" :order="9" />
|
|
663
|
+
<Gamma :value="gamma" :order="10" />
|
|
664
|
+
|
|
665
|
+
<!-- Overlay -->
|
|
666
|
+
<Grain
|
|
667
|
+
:intensity="grainAmount"
|
|
668
|
+
:scale="grainScale"
|
|
669
|
+
:animated="grainAnimated"
|
|
670
|
+
:order="11"
|
|
671
|
+
/>
|
|
672
|
+
</ShaderPipeline>
|
|
673
|
+
|
|
674
|
+
**New blocks identified:** `UVNoiseRotate`, `UVSineWarpXY`, `RotatedGradientBlend`
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
### 3. Shadertoy -- Basic Gradient (snippet 3)
|
|
679
|
+
|
|
680
|
+
**What it does:** Same core as Grainient but no grain, no post-processing, hardcoded two colour pairs, single blend rotation angle.
|
|
681
|
+
|
|
682
|
+
<ShaderPipeline>
|
|
683
|
+
<UVAspectCorrect :order="0" />
|
|
684
|
+
<UVCentre :order="1" />
|
|
685
|
+
<UVNoiseRotate :rotation-amount="720" :speed="0.1" :order="2" />
|
|
686
|
+
<UVSineWarpXY :frequency="5" :amplitude="30" :speed="2" :order="3" />
|
|
687
|
+
|
|
688
|
+
<RotatedGradientBlend
|
|
689
|
+
color-a="#f4cd9f"
|
|
690
|
+
color-b="#3162ee"
|
|
691
|
+
:angle="-5"
|
|
692
|
+
:edge0="-0.3"
|
|
693
|
+
:edge1="0.2"
|
|
694
|
+
:layer="1"
|
|
695
|
+
:order="4"
|
|
696
|
+
/>
|
|
697
|
+
<RotatedGradientBlend
|
|
698
|
+
color-a="#e882cc"
|
|
699
|
+
color-b="#59b5f3"
|
|
700
|
+
:angle="-5"
|
|
701
|
+
:edge0="-0.3"
|
|
702
|
+
:edge1="0.2"
|
|
703
|
+
:layer="2"
|
|
704
|
+
:order="5"
|
|
705
|
+
/>
|
|
706
|
+
<MixBlend :axis="'y'" :edge0="0.5" :edge1="-0.3" :order="6" />
|
|
707
|
+
</ShaderPipeline>
|
|
708
|
+
|
|
709
|
+
This is the minimal form of the Grainient pattern -- useful as a named preset `<BasicGradient />`.
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
### 4. Shadertoy -- Grainient + Day/Night Cycle (snippet 4)
|
|
714
|
+
|
|
715
|
+
**What it does:** Same warp as snippet 3, but colours cycle between light and dark palettes using a sinusoidal time function, and adds hash-based film grain.
|
|
716
|
+
|
|
717
|
+
<ShaderPipeline>
|
|
718
|
+
<UVAspectCorrect :order="0" />
|
|
719
|
+
<UVCentre :order="1" />
|
|
720
|
+
<UVNoiseRotate :rotation-amount="720" :speed="0.05" :order="2" />
|
|
721
|
+
<UVSineWarpXY :frequency="5" :amplitude="30" :speed="2" :order="3" />
|
|
722
|
+
|
|
723
|
+
<!-- Day/night colour cycle -->
|
|
724
|
+
<DayNightCycle
|
|
725
|
+
:colors-light="['#4bba89', '#3162ee', '#f69292', '#59b5f3']"
|
|
726
|
+
:colors-dark="['#6931f5', '#202a32', '#e93334', '#e9a04b']"
|
|
727
|
+
:speed="0.5"
|
|
728
|
+
:order="4"
|
|
729
|
+
/>
|
|
730
|
+
<!-- The cycle outputs 4 resolved colours used by the gradient layers below -->
|
|
731
|
+
|
|
732
|
+
<RotatedGradientBlend :angle="-5" :edge0="-0.3" :edge1="0.2" :layer="1" :order="5" />
|
|
733
|
+
<RotatedGradientBlend :angle="-5" :edge0="-0.3" :edge1="0.2" :layer="2" :order="6" />
|
|
734
|
+
<MixBlend :axis="'y'" :edge0="0.5" :edge1="-0.3" :order="7" />
|
|
735
|
+
|
|
736
|
+
<!-- Hash-based film grain, subtractive -->
|
|
737
|
+
<FilmGrain :intensity="0.05" :subtractive="true" :order="8" />
|
|
738
|
+
</ShaderPipeline>
|
|
739
|
+
|
|
740
|
+
**New blocks identified:** `DayNightCycle`, `FilmGrain`
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
### 5. Shadertoy -- Volumetric Tunnel (snippet 1)
|
|
745
|
+
|
|
746
|
+
**What it does:** A raymarching loop producing a glowing volumetric tunnel. SDF sphere at z=15, fractional XY tiling with a rotation matrix driven by sin(z)\*sin(time), additive colour accumulation per step with chromatic time offsets.
|
|
747
|
+
|
|
748
|
+
This cannot be meaningfully decomposed -- the raymarching loop is load-bearing. It lives as a self-contained `RaymarchTunnel` generator.
|
|
749
|
+
|
|
750
|
+
<ShaderPipeline>
|
|
751
|
+
<RaymarchTunnel
|
|
752
|
+
:glow-color="[0.2, 0.1, 0.04]"
|
|
753
|
+
:glow-falloff="0.1"
|
|
754
|
+
:steps="100"
|
|
755
|
+
:sphere-radius="1.0"
|
|
756
|
+
:sphere-z="15.0"
|
|
757
|
+
:tiling-rotation="true"
|
|
758
|
+
:speed="0.5"
|
|
759
|
+
:chromatic-spread="0.15"
|
|
760
|
+
:order="0"
|
|
761
|
+
/>
|
|
762
|
+
|
|
763
|
+
<!-- Overlays are still composable on top -->
|
|
764
|
+
<Vignette :strength="0.3" :order="1" />
|
|
765
|
+
</ShaderPipeline>
|
|
766
|
+
|
|
767
|
+
**Architecture note:** `RaymarchTunnel` is the first generator that uses a loop internally. TSL supports `Loop()` nodes natively, so this compiles correctly -- but it means the block cannot feed into or receive from UV transformers in the normal pipeline sense. It reads `uv()` and `time` directly and outputs the final colour. Mark this block as `terminal: true` -- it should always be `order: 0` and UV transformers have no effect on it.
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
### 6. Shadertoy -- CRT / Chromatic Screen Effect (snippet 2)
|
|
772
|
+
|
|
773
|
+
**What it does:** Takes a background texture (iChannel0), applies subtle per-channel UV offset for chromatic aberration, multiplies by per-channel animated sine waves plus a screen stripe, and adjusts for contrast loss.
|
|
774
|
+
|
|
775
|
+
<ShaderPipeline>
|
|
776
|
+
<!-- Base texture with RGB channel split -->
|
|
777
|
+
<TextureSampler :src="backgroundTexture" :order="0" />
|
|
778
|
+
<ChromaticAberration :red-offset="[0.001, 0]" :blue-offset="[-0.001, 0]" :order="1" />
|
|
779
|
+
|
|
780
|
+
<!-- CRT screen overlays -->
|
|
781
|
+
<CRTScanlines :order="2" />
|
|
782
|
+
<ChromaticScreenWaves
|
|
783
|
+
:amplitude="0.1"
|
|
784
|
+
:frequency="20"
|
|
785
|
+
:speed="2"
|
|
786
|
+
:phase-offset="0.4"
|
|
787
|
+
:order="3"
|
|
788
|
+
/>
|
|
789
|
+
|
|
790
|
+
<!-- Compensate for contrast loss from blending -->
|
|
791
|
+
<BrightnessContrast :contrast="1.25" :order="4" />
|
|
792
|
+
</ShaderPipeline>
|
|
793
|
+
|
|
794
|
+
**New blocks identified:** `ChromaticScreenWaves`
|
|
795
|
+
|
|
796
|
+
---
|
|
797
|
+
|
|
798
|
+
### 7. Stripe Gradient
|
|
799
|
+
|
|
800
|
+
**What it does:** Subdivided plane with vertex noise displacement, per-colour simplex noise wave layers in the fragment shader, diagonal cut via CSS.
|
|
801
|
+
|
|
802
|
+
<!-- CSS container: transform: skewY(-12deg); overflow: hidden -->
|
|
803
|
+
<TresCanvas>
|
|
804
|
+
<TresMesh>
|
|
805
|
+
<TresPlaneGeometry :args="[2, 2, 320, 80]" />
|
|
806
|
+
<ShaderPipeline>
|
|
807
|
+
<!-- Vertex stage -->
|
|
808
|
+
<MeshWarp
|
|
809
|
+
:noise-freq="[2, 3]"
|
|
810
|
+
:noise-amp="320"
|
|
811
|
+
:noise-speed="0.2"
|
|
812
|
+
:noise-flow="3"
|
|
813
|
+
:incline="0.4"
|
|
814
|
+
stage="vertex"
|
|
815
|
+
:order="0"
|
|
816
|
+
/>
|
|
817
|
+
|
|
818
|
+
<!-- Fragment stages -->
|
|
819
|
+
<SolidColour css-color="--color-stripe-base" :order="0" />
|
|
820
|
+
<WaveColourLayer
|
|
821
|
+
v-for="(color, i) in stripeColors"
|
|
822
|
+
:key="i"
|
|
823
|
+
:css-color="`--color-stripe-${i + 1}`"
|
|
824
|
+
v-bind="useWaveLayerDefaults(i, stripeColors.length)"
|
|
825
|
+
:order="1 + i"
|
|
826
|
+
/>
|
|
827
|
+
</ShaderPipeline>
|
|
828
|
+
</TresMesh>
|
|
829
|
+
</TresCanvas>
|
|
830
|
+
|
|
831
|
+
---
|
|
832
|
+
|
|
833
|
+
### 8. Heatmap
|
|
834
|
+
|
|
835
|
+
**What it does:** Pre-processed image texture (R=contour, G=outerBlur, B=innerBlur), animated organic shadow shapes (3 time-offset copies), heat float → n-colour gradient.
|
|
836
|
+
|
|
837
|
+
<ShaderPipeline>
|
|
838
|
+
<ProcessedImageSampler :src="imageSrc" :order="0" />
|
|
839
|
+
<ShadowShape
|
|
840
|
+
:inner-glow="innerGlow"
|
|
841
|
+
:contour="contour"
|
|
842
|
+
:order="1"
|
|
843
|
+
/>
|
|
844
|
+
<OuterHeatGlow
|
|
845
|
+
:outer-glow="outerGlow"
|
|
846
|
+
:angle="angle"
|
|
847
|
+
:order="2"
|
|
848
|
+
/>
|
|
849
|
+
<HeatToGradient
|
|
850
|
+
:colors="colors"
|
|
851
|
+
css-color-back="--color-bg-base"
|
|
852
|
+
:order="3"
|
|
853
|
+
/>
|
|
854
|
+
<Grain :intensity="noise" :order="4" />
|
|
855
|
+
</ShaderPipeline>
|
|
856
|
+
|
|
857
|
+
**CPU pre-processing step lives outside the shader system:**
|
|
858
|
+
|
|
859
|
+
const { blob } = await processHeatmapImage(src)
|
|
860
|
+
// Bakes contour(R), outerBlur(G), innerBlur(B) into a single texture
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
### 9. Glass / Wave Distortion
|
|
865
|
+
|
|
866
|
+
**What it does:** FBM-derived normal map drives refraction UV offset into a blurred backdrop sample. Fresnel edge brightening, tint, chromatic aberration.
|
|
867
|
+
|
|
868
|
+
<TresCanvas>
|
|
869
|
+
<SceneContent />
|
|
870
|
+
<BackdropPass :blur="12" id="scene-backdrop" />
|
|
871
|
+
|
|
872
|
+
<TresMesh>
|
|
873
|
+
<ShaderPipeline backdrop="scene-backdrop">
|
|
874
|
+
<FBMNoise :octaves="3" :scale="2.0" :order="0" />
|
|
875
|
+
<NormalFromHeight :strength="0.04" :order="1" />
|
|
876
|
+
<RefractionOffset :ior="0.08" :order="2" />
|
|
877
|
+
<BackdropBlur :order="3" />
|
|
878
|
+
<MixBlend css-color="--color-glass-tint" :factor="0.08" :order="4" />
|
|
879
|
+
<FresnelBlend css-color="--color-glass-edge" :power="2.0" :order="5" />
|
|
880
|
+
<ChromaticAberration :strength="0.003" :order="6" />
|
|
881
|
+
<EdgeGlow :strength="0.15" :order="7" />
|
|
882
|
+
</ShaderPipeline>
|
|
883
|
+
</TresMesh>
|
|
884
|
+
</TresCanvas>
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## Block Interaction Map
|
|
889
|
+
|
|
890
|
+
Reliable composition patterns:
|
|
891
|
+
|
|
892
|
+
UVNoiseRotate + UVSineWarpXY + RotatedGradientBlend → Grainient family
|
|
893
|
+
UVNoiseRotate + UVSineWarpXY + DayNightCycle → animated palette gradient
|
|
894
|
+
WaveBendLayer × n + CoverageAlpha → ColorBends
|
|
895
|
+
WaveColourLayer × n + MeshWarp (vertex) → Stripe gradient
|
|
896
|
+
FBMNoise + NormalFromHeight + RefractionOffset → glass refraction
|
|
897
|
+
TextureSampler + ChromaticAberration + ChromaticScreenWaves + CRTScanlines → CRT
|
|
898
|
+
UVPolar + Stripes → radial spokes
|
|
899
|
+
DomainWarpedNoise + ColourRamp → flowing lava / aurora
|
|
900
|
+
UVKaleidoscope + any generator → mandala
|
|
901
|
+
Halation + Bloom + ColourGrain → cinematic film look
|
|
902
|
+
DuoTone + Grain + Vignette → risograph print preset
|
|
903
|
+
DayNightCycle + Grain + FilmGrain → animated film look
|
|
904
|
+
RaymarchTunnel + Vignette → tunnel scene
|
|
905
|
+
FisheyeRay + RayTiltBasis + SkyAtmosphere + ACESTonemap → sky dome
|
|
906
|
+
FisheyeRay + RaySphereReflect + SkyAtmosphere → mirrored sky ball
|
|
907
|
+
BilinearGradient (4 CSS vars) → fully theme-reactive background
|
|
908
|
+
SkyAtmosphere + ExponentialFog + Grain → hazy atmospheric scene
|
|
909
|
+
|
|
910
|
+
// CosinePalette scalar pipeline patterns
|
|
911
|
+
CosinePalette(uv.y) + UVFractBand + Grain → dawn2 animated shimmer
|
|
912
|
+
CosinePalette(sdSphere * noise) + UVColumnOffset + SDFColourMask → dawn4/5 family
|
|
913
|
+
CosinePalette(length(uv) * noise) + ChebyshevNoiseField → genuary22
|
|
914
|
+
CosinePalette(imaginary) + ComplexPlaneField + Grain → imaginary2 complex field
|
|
915
|
+
CosinePalette(uv.y) + RingField × n + SDFRadialMask → flare8/9 ring gradient
|
|
916
|
+
any SDF + CosinePalette + SDFColourMask → any shape with palette colouring
|
|
917
|
+
SimplexNoise → CosinePalette → animated noise gradient
|
|
918
|
+
|
|
919
|
+
---
|
|
920
|
+
|
|
921
|
+
### 13. imaginary2 (phobon TSL)
|
|
922
|
+
|
|
923
|
+
**What it does:** Visualises the imaginary component of a complex logarithm of a Möbius transformation. Two poles at `p` and `q` (placed by angle/distance) divide the screen. For every pixel `z`, computes `log((z-p)/(z-q))` -- the imaginary part gives the winding angle around the two poles, creating smooth bands that wrap the plane. Combined with a radial `exp(z.x)/exp(z.y)` term for the hyperbolic texture.
|
|
924
|
+
|
|
925
|
+
<ShaderPipeline>
|
|
926
|
+
<!-- Scalar pipeline: UV → complex field float → palette → colour -->
|
|
927
|
+
<ComplexPlaneField
|
|
928
|
+
:pole-angle="PI * 1.8"
|
|
929
|
+
:pole-distance="0.25"
|
|
930
|
+
:imaginary-weight="0.75"
|
|
931
|
+
:radial-weight="1.0"
|
|
932
|
+
:order="0"
|
|
933
|
+
/>
|
|
934
|
+
<!-- t from ComplexPlaneField feeds CosinePalette -->
|
|
935
|
+
<CosinePalette
|
|
936
|
+
:a="[0.5, 0.52, 0.53]"
|
|
937
|
+
:b="[0.46, 0.32, 0.35]"
|
|
938
|
+
:c="[0.82, 0.84, 0.65]"
|
|
939
|
+
:d="[0.53, 0.23, 0.22]"
|
|
940
|
+
:frequency="0.6 * 2 * PI"
|
|
941
|
+
:order="1"
|
|
942
|
+
/>
|
|
943
|
+
<Grain :intensity="0.2" :order="2" />
|
|
944
|
+
</ShaderPipeline>
|
|
945
|
+
|
|
946
|
+
---
|
|
947
|
+
|
|
948
|
+
### 14. dawn2 (phobon TSL)
|
|
949
|
+
|
|
950
|
+
**What it does:** Animated cosine palette scrolling vertically. Adds a fract-banding shimmer: `fract(uv.y * 24) * 0.3` raised to power 2 creates bright horizontal flicker lines that sit on top of the smooth gradient.
|
|
951
|
+
|
|
952
|
+
<ShaderPipeline>
|
|
953
|
+
<!-- Base: time-scrolling cosine palette on Y -->
|
|
954
|
+
<CosinePalette
|
|
955
|
+
:a="[0.5, 0.5, 0.5]"
|
|
956
|
+
:b="[0.5, 0.5, 0.5]"
|
|
957
|
+
:c="[1.0, 0.7, 0.4]"
|
|
958
|
+
:d="[0.0, 0.15, 0.2]"
|
|
959
|
+
scalar-input="uv.y - time * 0.1"
|
|
960
|
+
:order="0"
|
|
961
|
+
/>
|
|
962
|
+
<!-- Additive shimmer bands -->
|
|
963
|
+
<UVFractBand
|
|
964
|
+
:frequency="24"
|
|
965
|
+
:scale="0.3"
|
|
966
|
+
:power="2.0"
|
|
967
|
+
:order="1"
|
|
968
|
+
/>
|
|
969
|
+
<Grain :intensity="0.2" :order="2" />
|
|
970
|
+
</ShaderPipeline>
|
|
971
|
+
|
|
972
|
+
**Note:** `scalar-input="uv.y - time * 0.1"` is a string expression shorthand. In practice this would be a TSL node: `uv().y.sub(time.mul(0.1))` passed as a prop.
|
|
973
|
+
|
|
974
|
+
---
|
|
975
|
+
|
|
976
|
+
### 15. dawn4 (phobon TSL)
|
|
977
|
+
|
|
978
|
+
**What it does:** Column-offset UV creates vertical banding, simplex noise drives both a box SDF mask and the cosine palette. Two noise octaves at different frequencies.
|
|
979
|
+
|
|
980
|
+
<ShaderPipeline>
|
|
981
|
+
<!-- UV: column offset + sine softening -->
|
|
982
|
+
<UVAspectCorrect :order="0" />
|
|
983
|
+
<UVColumnOffset
|
|
984
|
+
:repetitions="12"
|
|
985
|
+
:offset-factor="0.005"
|
|
986
|
+
:sine-softness="0.05"
|
|
987
|
+
:order="1"
|
|
988
|
+
/>
|
|
989
|
+
|
|
990
|
+
<!-- Scalar pipeline: sdSphere * noise → palette -->
|
|
991
|
+
<CosinePalette
|
|
992
|
+
:a="[0.8, 0.5, 0.4]"
|
|
993
|
+
:b="[0.2, 0.4, 0.2]"
|
|
994
|
+
:c="[2.0, 1.0, 1.0]"
|
|
995
|
+
:d="[0.0, 0.25, 0.25]"
|
|
996
|
+
scalar-input="sdSphere(uv0) * noise3d(offsetUv)"
|
|
997
|
+
:order="2"
|
|
998
|
+
/>
|
|
999
|
+
|
|
1000
|
+
<!-- Mask: 1 - sdBox2d(uv * noise) applied multiplicatively -->
|
|
1001
|
+
<SDFColourMask
|
|
1002
|
+
mask-input="oneMinus(sdBox2d(offsetUv * noiseScale))"
|
|
1003
|
+
:order="3"
|
|
1004
|
+
/>
|
|
1005
|
+
|
|
1006
|
+
<Grain :intensity="0.2" :order="4" />
|
|
1007
|
+
</ShaderPipeline>
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
1011
|
+
### 16. dawn5 (phobon TSL)
|
|
1012
|
+
|
|
1013
|
+
**What it does:** Like dawn4 but uses `abs(sdSphere) + noise` for the mask (creates a ring-like silhouette rather than box), and applies `tanh` tonemapping.
|
|
1014
|
+
|
|
1015
|
+
<ShaderPipeline>
|
|
1016
|
+
<UVAspectCorrect :order="0" />
|
|
1017
|
+
<UVColumnOffset :repetitions="12" :offset-factor="0.005" :order="1" />
|
|
1018
|
+
|
|
1019
|
+
<CosinePalette
|
|
1020
|
+
:a="[0.5, 0.5, 0.5]"
|
|
1021
|
+
:b="[0.5, 0.5, 0.5]"
|
|
1022
|
+
:c="[1.0, 1.0, 0.5]"
|
|
1023
|
+
:d="[0.8, 0.9, 0.3]"
|
|
1024
|
+
scalar-input="sdSphere(uv0) + noise3d(offsetUv * 3, time * 0.25) * 0.12"
|
|
1025
|
+
:order="2"
|
|
1026
|
+
/>
|
|
1027
|
+
|
|
1028
|
+
<!-- Ring mask: 1 - |sdSphere(offsetUv)| - noise -->
|
|
1029
|
+
<SDFColourMask
|
|
1030
|
+
mask-input="oneMinus(abs(sdSphere(offsetUv)) + noise)"
|
|
1031
|
+
:order="3"
|
|
1032
|
+
/>
|
|
1033
|
+
|
|
1034
|
+
<!-- Tanh tone compression before grain -->
|
|
1035
|
+
<TanhTonemap :exposure="2.5" :order="4" />
|
|
1036
|
+
<Grain :intensity="0.2" :order="5" />
|
|
1037
|
+
</ShaderPipeline>
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
1041
|
+
### 17. flare9 (phobon TSL)
|
|
1042
|
+
|
|
1043
|
+
**What it does:** Single ring field (`abs(length(uv + offset) - radius)`) combined with a radial vignette mask. Cosine palette on uv.y.
|
|
1044
|
+
|
|
1045
|
+
<ShaderPipeline>
|
|
1046
|
+
<UVAspectCorrect :order="0" />
|
|
1047
|
+
|
|
1048
|
+
<!-- Ring SDF accumulated with radial vignette into a float mask -->
|
|
1049
|
+
<RingField
|
|
1050
|
+
:rings="[{ offset: [0.5, -0.5], radius: 0.55, scale: 1.75 }]"
|
|
1051
|
+
:vignette-scale="3"
|
|
1052
|
+
:order="1"
|
|
1053
|
+
/>
|
|
1054
|
+
|
|
1055
|
+
<!-- Palette driven by uv.y -->
|
|
1056
|
+
<CosinePalette
|
|
1057
|
+
:a="[0.5, 0.5, 0.5]"
|
|
1058
|
+
:b="[0.5, 0.5, 0.5]"
|
|
1059
|
+
:c="[1.0, 1.0, 1.0]"
|
|
1060
|
+
:d="[0.0, 0.33, 0.67]"
|
|
1061
|
+
scalar-input="uv.y * 0.25 + 2.25"
|
|
1062
|
+
:order="2"
|
|
1063
|
+
/>
|
|
1064
|
+
|
|
1065
|
+
<!-- Ring mask applied multiplicatively to palette colour -->
|
|
1066
|
+
<SDFColourMask :order="3" />
|
|
1067
|
+
<Grain :intensity="0.2" :order="4" />
|
|
1068
|
+
</ShaderPipeline>
|
|
1069
|
+
|
|
1070
|
+
---
|
|
1071
|
+
|
|
1072
|
+
### 18. flare8 (phobon TSL)
|
|
1073
|
+
|
|
1074
|
+
**What it does:** Three overlapping rings at different positions, scales, and radii, accumulated into a single mask. More complex ring field than flare9. Uniform-driven so all parameters are live-adjustable.
|
|
1075
|
+
|
|
1076
|
+
<ShaderPipeline>
|
|
1077
|
+
<UVAspectCorrect :order="0" />
|
|
1078
|
+
|
|
1079
|
+
<!-- Three rings accumulated + radial vignette -->
|
|
1080
|
+
<RingField
|
|
1081
|
+
:rings="[
|
|
1082
|
+
{ offset: [0.1, 0.5], radius: 0.25, scale: 1.5, mode: 'abs' },
|
|
1083
|
+
{ offset: [-0.25,-0.3], radius: 0.45, scale: 1.75, mode: 'oneMinus' },
|
|
1084
|
+
{ offset: [0.25, -0.5], radius: 0.75, scale: 0.5, mode: 'abs' }
|
|
1085
|
+
]"
|
|
1086
|
+
:vignette-scale="3"
|
|
1087
|
+
:order="1"
|
|
1088
|
+
/>
|
|
1089
|
+
|
|
1090
|
+
<CosinePalette
|
|
1091
|
+
css-a="--shader-palette-a"
|
|
1092
|
+
css-b="--shader-palette-b"
|
|
1093
|
+
css-c="--shader-palette-c"
|
|
1094
|
+
css-d="--shader-palette-d"
|
|
1095
|
+
:offset="0.5"
|
|
1096
|
+
scalar-input="uv.y * 0.5 + offset"
|
|
1097
|
+
:order="2"
|
|
1098
|
+
/>
|
|
1099
|
+
|
|
1100
|
+
<SDFColourMask :order="3" />
|
|
1101
|
+
<Grain :intensity="0.2" :order="4" />
|
|
1102
|
+
</ShaderPipeline>
|
|
1103
|
+
|
|
1104
|
+
**Note:** flare8 uses `uniform()` for its palette parameters -- mapped directly to CSS var props on `CosinePalette` (`css-a`, `css-b` etc.) for full theme reactivity.
|
|
1105
|
+
|
|
1106
|
+
---
|
|
1107
|
+
|
|
1108
|
+
### 19. genuary22 (phobon TSL)
|
|
1109
|
+
|
|
1110
|
+
**What it does:** Noise-distorted Chebyshev box shape coloured by cosine palette. Two simplex noise octaves modulate both the mask and the palette input. No grain.
|
|
1111
|
+
|
|
1112
|
+
<ShaderPipeline>
|
|
1113
|
+
<UVAspectCorrect :order="0" />
|
|
1114
|
+
|
|
1115
|
+
<!-- Noise-eroded Chebyshev cross mask -->
|
|
1116
|
+
<ChebyshevNoiseField
|
|
1117
|
+
:scale-x="2.5"
|
|
1118
|
+
:scale-y="1.5"
|
|
1119
|
+
:noise-frequency-2="2.0"
|
|
1120
|
+
:time-speed="0.1"
|
|
1121
|
+
:order="1"
|
|
1122
|
+
/>
|
|
1123
|
+
|
|
1124
|
+
<!-- Palette: length(uv) * noise as input -->
|
|
1125
|
+
<CosinePalette
|
|
1126
|
+
:a="[0.5, 0.5, 0.5]"
|
|
1127
|
+
:b="[0.5, 0.5, 0.5]"
|
|
1128
|
+
:c="[1.0, 0.7, 0.4]"
|
|
1129
|
+
:d="[0.0, 0.15, 0.2]"
|
|
1130
|
+
scalar-input="length(uv) * noise"
|
|
1131
|
+
:order="2"
|
|
1132
|
+
/>
|
|
1133
|
+
|
|
1134
|
+
<SDFColourMask :order="3" />
|
|
1135
|
+
</ShaderPipeline>
|
|
1136
|
+
|
|
1137
|
+
**New blocks identified:** `CosinePalette`, `RingField`, `ChebyshevNoiseField`, `ComplexPlaneField`, `SDFColourMask`, `SDFRadialMask`, `TanhTonemap`, `UVColumnOffset`, `UVFractBand`, `ComplexDiv`, `ComplexLog`
|
|
1138
|
+
|
|
1139
|
+
---
|
|
1140
|
+
|
|
1141
|
+
### 10. Four-Corner Gradient (Shadertoy snippet 2)
|
|
1142
|
+
|
|
1143
|
+
**What it does:** The simplest possible 2D colour field. Four colours at each corner, bilinearly interpolated across the UV space. No animation, no noise, no distortion.
|
|
1144
|
+
|
|
1145
|
+
<ShaderPipeline>
|
|
1146
|
+
<BilinearGradient
|
|
1147
|
+
css-color-bl="--color-bg-base"
|
|
1148
|
+
css-color-br="--color-accent"
|
|
1149
|
+
css-color-tl="--color-surface"
|
|
1150
|
+
css-color-tr="--color-highlight"
|
|
1151
|
+
:order="0"
|
|
1152
|
+
/>
|
|
1153
|
+
</ShaderPipeline>
|
|
1154
|
+
|
|
1155
|
+
This is the atomic baseline generator -- worth having as a named block purely because it composes so well with UV transformers. Run it through `UVNoiseRotate` + `UVSineWarpXY` and you get a variant of the Grainient family with distinct corner anchors.
|
|
1156
|
+
|
|
1157
|
+
---
|
|
1158
|
+
|
|
1159
|
+
### 11. Sky Gradient (Shadertoy -- Hazel Quantock)
|
|
1160
|
+
|
|
1161
|
+
**What it does:** Physically-approximated sky dome using exponential atmospheric scattering. Ray-based -- converts screen coords to a 3D ray, tilts it upward, applies optional fisheye and sphere reflection, evaluates sky colour + sun disc, ACES tonemaps.
|
|
1162
|
+
|
|
1163
|
+
**Key insight:** The sky is built from three separable concerns -- the ray setup, the atmospheric function, and the tonemapping. Each maps cleanly to a block.
|
|
1164
|
+
|
|
1165
|
+
<ShaderPipeline>
|
|
1166
|
+
<!-- Ray Pipeline (stage: 'ray') -- builds and transforms the view ray -->
|
|
1167
|
+
<FisheyeRay :strength="0.5" stage="ray" :order="0" />
|
|
1168
|
+
<RayTiltBasis :forward="[0, 0.8, 1]" stage="ray" :order="1" />
|
|
1169
|
+
<!-- Optional: -->
|
|
1170
|
+
<!-- <RaySphereReflect :centre-z="1.8" :radius="1.0" stage="ray" :order="2" /> -->
|
|
1171
|
+
|
|
1172
|
+
<!-- Terminal generator -- consumes ray, outputs sky colour (HDR range) -->
|
|
1173
|
+
<SkyAtmosphere
|
|
1174
|
+
:scatter-coeffs="[0.1, 0.3, 0.6]"
|
|
1175
|
+
:sun-disc-sharpness="0.9995"
|
|
1176
|
+
:sun-disc-colour="[10, 3, 0.2]"
|
|
1177
|
+
:order="0"
|
|
1178
|
+
>
|
|
1179
|
+
<!-- Sun direction driven by mouse or time -->
|
|
1180
|
+
<SunDirectionUniform slot="sunDir" :speed="0.5" :mouse-driven="true" />
|
|
1181
|
+
</SkyAtmosphere>
|
|
1182
|
+
|
|
1183
|
+
<!-- Tonemapping -- ACES compresses HDR sky values to display range -->
|
|
1184
|
+
<ACESTonemap :order="1" />
|
|
1185
|
+
|
|
1186
|
+
<!-- Overlays still composable on top -->
|
|
1187
|
+
<ExponentialFog css-color="--color-fog" :density="0.1" :order="2" />
|
|
1188
|
+
</ShaderPipeline>
|
|
1189
|
+
|
|
1190
|
+
**The sun direction slot** is a new pattern -- `SkyAtmosphere` exposes a named input slot for its sun direction uniform rather than taking it as a plain prop. This lets you swap between `SunDirectionUniform` (mouse/time driven), `CSSFloatUniform` (theme driven), or a static `AngleConst` without changing the generator itself.
|
|
1191
|
+
|
|
1192
|
+
**ACES is mandatory here** -- `SkyAtmosphere` outputs HDR values (sun disc peaks at `vec3(10, 3, 0.2)`). Without tonemapping the canvas clips to white. The pipeline compositor should warn if a terminal HDR generator has no tonemap op downstream.
|
|
1193
|
+
|
|
1194
|
+
**New blocks identified:** `SkyAtmosphere`, `BilinearGradient`, `FisheyeRay`, `RayTiltBasis`, `RaySphereReflect`, `SunDirectionUniform`, `RayCameraUniform`, `ACESTonemap`, `ExponentialFog`, `ReinhardTonemap`
|
|
1195
|
+
|
|
1196
|
+
---
|
|
1197
|
+
|
|
1198
|
+
### 12. Mountain Terrain (Shadertoy -- DDA Heightmap Raymarcher)
|
|
1199
|
+
|
|
1200
|
+
**What it does:** First-person voxel terrain using 2D DDA traversal. A separate buffer pass (iChannel0) stores and updates camera position and rotation each frame. The main pass reads that state, builds a view ray, then marches through a hexagonally-skewed grid, sampling a noise texture for height at each cell boundary until the ray intersects the terrain surface.
|
|
1201
|
+
|
|
1202
|
+
**The three non-obvious parts:**
|
|
1203
|
+
|
|
1204
|
+
1. **Hexagonal grid skew** -- `F = (sqrt(3)-1)/2` is the skew factor that transforms a square grid into one where diagonal traversal is equal cost to axis-aligned traversal. It gives the terrain its diamond/isometric facet appearance without using actual hexagonal geometry.
|
|
1205
|
+
|
|
1206
|
+
2. **DDA traversal** -- not raymarching in the SDF sense. The loop steps cell by cell in 2D, choosing X or Y boundary based on which is closer, then comparing the ray Z against the elevation at that cell. When `e1 >= fpos.z` the ray has gone underground -- binary interpolate back to the surface.
|
|
1207
|
+
|
|
1208
|
+
3. **Multi-pass state** -- the camera controller lives in a separate buffer shader that reads keyboard/mouse input and integrates position over time, writing the result as pixel colours. The terrain shader reads this back via `texture(iChannel0, ...)`. This is outside the block system's scope -- it's a simulation pass, not a shader effect. The block system receives the final position/rotation as uniforms.
|
|
1209
|
+
|
|
1210
|
+
**Block composition:**
|
|
1211
|
+
|
|
1212
|
+
<!--
|
|
1213
|
+
Camera state comes from outside the shader system --
|
|
1214
|
+
a separate simulation pass or JS-driven uniforms.
|
|
1215
|
+
The terrain block receives position and rotation as plain uniforms.
|
|
1216
|
+
-->
|
|
1217
|
+
<ShaderPipeline>
|
|
1218
|
+
|
|
1219
|
+
<!-- Ray Pipeline: build view ray from camera state -->
|
|
1220
|
+
<RayFromStateBuffer
|
|
1221
|
+
:position-uniform="cameraPosition"
|
|
1222
|
+
:rotation-uniform="cameraRotation"
|
|
1223
|
+
stage="ray"
|
|
1224
|
+
:order="0"
|
|
1225
|
+
/>
|
|
1226
|
+
|
|
1227
|
+
<!-- Terminal generator: DDA terrain traversal -->
|
|
1228
|
+
<HeightmapTerrain
|
|
1229
|
+
:height-texture="noiseTexture"
|
|
1230
|
+
:grid-scale="100"
|
|
1231
|
+
:terrain-amplitude="5"
|
|
1232
|
+
:terrain-radius="20"
|
|
1233
|
+
:facet-colour-a="[1.0, 1.0, 1.0]"
|
|
1234
|
+
:facet-colour-b="[0.15, 0.3, 0.5]"
|
|
1235
|
+
:march-steps="32"
|
|
1236
|
+
:order="0"
|
|
1237
|
+
/>
|
|
1238
|
+
|
|
1239
|
+
<!-- Overlays still fully composable -->
|
|
1240
|
+
<ExponentialFog
|
|
1241
|
+
css-color="--color-sky-horizon"
|
|
1242
|
+
:density="0.04"
|
|
1243
|
+
:order="1"
|
|
1244
|
+
/>
|
|
1245
|
+
<Vignette :strength="0.25" :order="2" />
|
|
1246
|
+
|
|
1247
|
+
</ShaderPipeline>
|
|
1248
|
+
|
|
1249
|
+
**The simulation pass** (camera movement) sits outside `ShaderPipeline` entirely. In a TresJS context this is a `useRenderTarget` composable that runs each frame, writing position/rotation into a 2×1 float texture that gets passed to the terrain block as a uniform. It does not participate in the block pipeline -- it's infrastructure.
|
|
1250
|
+
|
|
1251
|
+
// Outside the shader system -- camera simulation
|
|
1252
|
+
const { texture: cameraState } = useSimulationPass({
|
|
1253
|
+
width: 2, height: 1,
|
|
1254
|
+
shader: cameraSimulationGLSL, // integrates velocity, handles input
|
|
1255
|
+
floatTexture: true
|
|
1256
|
+
})
|
|
1257
|
+
// cameraState passed as :position-uniform and :rotation-uniform to RayFromStateBuffer
|
|
1258
|
+
|
|
1259
|
+
**New blocks identified:** `HeightmapTerrain`, `HeightmapSampler`, `StateBufferSampler`, `RayFromStateBuffer`
|
|
1260
|
+
|
|
1261
|
+
**New system concept:** `useSimulationPass` -- a composable for multi-pass state simulation that sits outside the block pipeline but feeds into it via uniforms. This is the same pattern needed for fluid simulation, particle systems, and anything that requires frame-to-frame state accumulation.
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
## Implementation Priority
|
|
1266
|
+
|
|
1267
|
+
Reliable composition patterns worth noting:
|
|
1268
|
+
|
|
1269
|
+
RayFromStateBuffer + HeightmapTerrain + ExponentialFog → terrain flythrough
|
|
1270
|
+
RayFromStateBuffer + HeightmapTerrain + Vignette → minimal terrain view
|
|
1271
|
+
FisheyeRay + RayTiltBasis + SkyAtmosphere + ACESTonemap → sky dome
|
|
1272
|
+
BilinearGradient + UVNoiseRotate + UVSineWarpXY → theme-anchored gradient
|
|
1273
|
+
useSimulationPass → StateBufferSampler → any ray generator → stateful camera
|
|
1274
|
+
|
|
1275
|
+
### Phase 1 -- Foundation
|
|
1276
|
+
|
|
1277
|
+
Primitives (values, uniforms, CSS bridge, CoverageAlpha), SolidColour, LinearGradient, RadialGradient, GradientNoise, FBMNoise, SimplexNoise, UVAspectCorrect, UVCentre, UVScale, UVRotate, UVNoiseRotate, UVSineWarpXY, UVScroll, **CosinePalette** ← promote to Phase 1 -- backbone of the scalar pipeline, SDFColourMask, SDFRadialMask, RotatedGradientBlend, MixBlend, BrightnessContrast, Gamma, Saturation, TanhTonemap, Grain, FilmGrain, Vignette
|
|
1278
|
+
|
|
1279
|
+
### Phase 2 -- Expression
|
|
1280
|
+
|
|
1281
|
+
WaveBendLayer, WaveColourLayer, MeshWarp (vertex), UVParallax, UVMousePull, UVWarp, UVTwirl, UVPolar, UVColumnOffset, UVFractBand, DayNightCycle, ColourRamp, DuoTone, Desaturate, Tint, RingField, ChebyshevNoiseField, ChromaticAberration, ChromaticScreenWaves, CRTScanlines, Bloom, Halation
|
|
1282
|
+
|
|
1283
|
+
### Phase 3 -- Depth
|
|
1284
|
+
|
|
1285
|
+
ProcessedImageSampler, ShadowShape, HeatToGradient, BackdropPass, BackdropBlur, RefractionOffset, FresnelBlend, NormalFromHeight, MultiStopGradient, MeshGradient, DomainWarpedNoise, UVKaleidoscope, UVFeedback, CurlNoise, LiftGammaGain, Curves (LUT), FilmBurn, RaymarchTunnel, BilinearGradient, ACESTonemap, ReinhardTonemap, ComplexPlaneField, ComplexDiv, ComplexLog
|
|
1286
|
+
|
|
1287
|
+
### Phase 4 -- Ray & Atmosphere
|
|
1288
|
+
|
|
1289
|
+
Ray Pipeline infrastructure (RayCameraUniform, FisheyeRay, RayTiltBasis, RayFromStateBuffer), SkyAtmosphere, SunDirectionUniform, RaySphereReflect, ExponentialFog, RayMouseOrbit, RayAutoOrbit
|
|
1290
|
+
|
|
1291
|
+
### Phase 5 -- Terrain & Simulation
|
|
1292
|
+
|
|
1293
|
+
HeightmapTerrain, HeightmapSampler, StateBufferSampler, useSimulationPass, UVFeedback (render target), fluid simulation pass, particle simulation pass
|
|
1294
|
+
|
|
1295
|
+
### Phase 6 -- Surface & Detail
|
|
1296
|
+
|
|
1297
|
+
All texture overlays, GodRays, LensFlare, Caustics, Bokeh, FabricWeave, WatercolourEdge, TriplanarMapping, FlowMap, MotionBlur, full film grain suite, CRTCurvature, VHSBleed, AgedFilm preset, RisographGrain preset
|
|
1298
|
+
|
|
1299
|
+
---
|
|
1300
|
+
|
|
1301
|
+
## VII. Implementation Guide
|
|
1302
|
+
|
|
1303
|
+
> Complete code for core infrastructure plus one reference implementation per category. All remaining blocks follow the same patterns -- build these first, then use them as templates.
|
|
1304
|
+
|
|
1305
|
+
---
|
|
1306
|
+
|
|
1307
|
+
### File Structure
|
|
1308
|
+
|
|
1309
|
+
layers/shader/
|
|
1310
|
+
├── index.ts # barrel export
|
|
1311
|
+
├── composables/
|
|
1312
|
+
│ ├── useShaderStage.ts
|
|
1313
|
+
│ ├── useCSSUniform.ts
|
|
1314
|
+
│ └── useThemeProvider.ts
|
|
1315
|
+
├── components/
|
|
1316
|
+
│ ├── ShaderPipeline.vue # core compositor
|
|
1317
|
+
│ ├── primitives/
|
|
1318
|
+
│ │ ├── FloatUniform.vue
|
|
1319
|
+
│ │ └── ColourUniform.vue
|
|
1320
|
+
│ ├── generators/
|
|
1321
|
+
│ │ ├── SolidColour.vue
|
|
1322
|
+
│ │ ├── LinearGradient.vue
|
|
1323
|
+
│ │ └── CosinePalette.vue # scalar generator
|
|
1324
|
+
│ ├── uvTransformers/
|
|
1325
|
+
│ │ ├── UVAspectCorrect.vue
|
|
1326
|
+
│ │ ├── UVNoiseRotate.vue
|
|
1327
|
+
│ │ └── UVSineWarpXY.vue
|
|
1328
|
+
│ ├── colourOps/
|
|
1329
|
+
│ │ ├── BrightnessContrast.vue
|
|
1330
|
+
│ │ ├── TanhTonemap.vue
|
|
1331
|
+
│ │ └── SDFColourMask.vue
|
|
1332
|
+
│ └── overlays/
|
|
1333
|
+
│ ├── Grain.vue
|
|
1334
|
+
│ └── Vignette.vue
|
|
1335
|
+
├── tsl/
|
|
1336
|
+
│ ├── utils/
|
|
1337
|
+
│ │ ├── screenAspectUV.ts
|
|
1338
|
+
│ │ └── colour.ts # CSS colour resolution
|
|
1339
|
+
│ ├── generators/
|
|
1340
|
+
│ │ ├── solidColour.ts
|
|
1341
|
+
│ │ ├── linearGradient.ts
|
|
1342
|
+
│ │ └── cosinePalette.ts
|
|
1343
|
+
│ ├── uvTransformers/
|
|
1344
|
+
│ │ ├── uvAspectCorrect.ts
|
|
1345
|
+
│ │ ├── uvNoiseRotate.ts
|
|
1346
|
+
│ │ └── uvSineWarpXY.ts
|
|
1347
|
+
│ ├── colourOps/
|
|
1348
|
+
│ │ ├── brightnessContrast.ts
|
|
1349
|
+
│ │ └── tanhTonemap.ts
|
|
1350
|
+
│ ├── overlays/
|
|
1351
|
+
│ │ ├── grain.ts
|
|
1352
|
+
│ │ └── vignette.ts
|
|
1353
|
+
│ ├── noise/
|
|
1354
|
+
│ │ └── gradientNoise.ts
|
|
1355
|
+
│ └── sdf/
|
|
1356
|
+
│ └── shapes.ts
|
|
1357
|
+
└── types.ts
|
|
1358
|
+
|
|
1359
|
+
---
|
|
1360
|
+
|
|
1361
|
+
### 1. Types
|
|
1362
|
+
|
|
1363
|
+
// layers/shader/types.ts
|
|
1364
|
+
import type { Node } from 'three/tsl'
|
|
1365
|
+
|
|
1366
|
+
export type ShaderStage = 'fragment' | 'vertex' | 'ray'
|
|
1367
|
+
|
|
1368
|
+
export interface StageEntry {
|
|
1369
|
+
fn: (input: Node) => Node
|
|
1370
|
+
order: number
|
|
1371
|
+
stage: ShaderStage
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
export interface ShaderPipelineContext {
|
|
1375
|
+
register: (fn: StageEntry['fn'], order: number, stage: ShaderStage) => void
|
|
1376
|
+
unregister: (fn: StageEntry['fn']) => void
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
export interface CosineParams {
|
|
1380
|
+
a: [number, number, number]
|
|
1381
|
+
b: [number, number, number]
|
|
1382
|
+
c: [number, number, number]
|
|
1383
|
+
d: [number, number, number]
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
---
|
|
1387
|
+
|
|
1388
|
+
### 2. Core Composables
|
|
1389
|
+
|
|
1390
|
+
// layers/shader/composables/useThemeProvider.ts
|
|
1391
|
+
import { ref, provide, watch } from 'vue'
|
|
1392
|
+
import { useColorMode } from '@vueuse/core'
|
|
1393
|
+
|
|
1394
|
+
export function useThemeProvider() {
|
|
1395
|
+
const mode = useColorMode()
|
|
1396
|
+
const palette = ref('default')
|
|
1397
|
+
const contrast = ref('standard')
|
|
1398
|
+
|
|
1399
|
+
provide('shaderTheme', { mode, palette, contrast })
|
|
1400
|
+
return { mode, palette, contrast }
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
// layers/shader/composables/useShaderStage.ts
|
|
1406
|
+
import { inject, onMounted, onUnmounted } from 'vue'
|
|
1407
|
+
import type { Node } from 'three/tsl'
|
|
1408
|
+
import type { ShaderPipelineContext, ShaderStage } from '../types'
|
|
1409
|
+
|
|
1410
|
+
export function useShaderStage(
|
|
1411
|
+
stageFn: (input: Node) => Node,
|
|
1412
|
+
order = 0,
|
|
1413
|
+
stage: ShaderStage = 'fragment'
|
|
1414
|
+
) {
|
|
1415
|
+
const pipeline = inject<ShaderPipelineContext>('shaderPipeline')
|
|
1416
|
+
if (!pipeline) {
|
|
1417
|
+
console.warn('[useShaderStage] No ShaderPipeline found in parent tree')
|
|
1418
|
+
return
|
|
1419
|
+
}
|
|
1420
|
+
onMounted(() => pipeline.register(stageFn, order, stage))
|
|
1421
|
+
onUnmounted(() => pipeline.unregister(stageFn))
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
|
|
1426
|
+
// layers/shader/composables/useCSSUniform.ts
|
|
1427
|
+
import { uniform } from 'three/tsl'
|
|
1428
|
+
import { Color } from 'three'
|
|
1429
|
+
import { inject, watch, nextTick, onUnmounted } from 'vue'
|
|
1430
|
+
import { resolveColour } from '../tsl/utils/colour'
|
|
1431
|
+
|
|
1432
|
+
function readColourVar(varName: string) {
|
|
1433
|
+
const raw = getComputedStyle(document.documentElement)
|
|
1434
|
+
.getPropertyValue(varName).trim()
|
|
1435
|
+
return new Color(resolveColour(raw || '#000000'))
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
function readFloatVar(varName: string) {
|
|
1439
|
+
return parseFloat(
|
|
1440
|
+
getComputedStyle(document.documentElement).getPropertyValue(varName)
|
|
1441
|
+
) || 0
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
export function useCSSColourUniform(varName: string) {
|
|
1445
|
+
const { mode, palette, contrast } = inject<any>('shaderTheme')
|
|
1446
|
+
const node = uniform(readColourVar(varName))
|
|
1447
|
+
|
|
1448
|
+
const stop = watch([mode, palette, contrast], () => {
|
|
1449
|
+
nextTick(() => { node.value.set(readColourVar(varName)) })
|
|
1450
|
+
})
|
|
1451
|
+
|
|
1452
|
+
onUnmounted(stop)
|
|
1453
|
+
return node
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
export function useCSSFloatUniform(varName: string) {
|
|
1457
|
+
const { mode, palette, contrast } = inject<any>('shaderTheme')
|
|
1458
|
+
const node = uniform(readFloatVar(varName))
|
|
1459
|
+
|
|
1460
|
+
const stop = watch([mode, palette, contrast], () => {
|
|
1461
|
+
nextTick(() => { node.value = readFloatVar(varName) })
|
|
1462
|
+
})
|
|
1463
|
+
|
|
1464
|
+
onUnmounted(stop)
|
|
1465
|
+
return node
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
---
|
|
1469
|
+
|
|
1470
|
+
### 3. Colour Utility
|
|
1471
|
+
|
|
1472
|
+
// layers/shader/tsl/utils/colour.ts
|
|
1473
|
+
export function resolveColour(raw: string): string {
|
|
1474
|
+
const str = raw.trim()
|
|
1475
|
+
if (/^oklch\(/i.test(str)) return oklchToHex(str)
|
|
1476
|
+
if (/^hsla?\(/i.test(str)) return hslaToHex(str)
|
|
1477
|
+
return canvasResolve(str)
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
function canvasResolve(str: string): string {
|
|
1481
|
+
const ctx = Object.assign(
|
|
1482
|
+
document.createElement('canvas'), { width: 1, height: 1 }
|
|
1483
|
+
).getContext('2d')!
|
|
1484
|
+
ctx.fillStyle = str
|
|
1485
|
+
return ctx.fillStyle
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
function oklchToHex(str: string): string {
|
|
1489
|
+
const [l, c, h] = parseArgs(str).map(Number)
|
|
1490
|
+
const hRad = (h * Math.PI) / 180
|
|
1491
|
+
const a = c * Math.cos(hRad)
|
|
1492
|
+
const b = c * Math.sin(hRad)
|
|
1493
|
+
const lr = l + 0.3963377774 * a + 0.2158037573 * b
|
|
1494
|
+
const lg = l - 0.1055613458 * a - 0.0638541728 * b
|
|
1495
|
+
const lb = l - 0.0894841775 * a - 1.2914855480 * b
|
|
1496
|
+
const r = gamma(lr**3 * 4.0767416621 + lg**3 * -3.3077115913 + lb**3 * 0.2309699292)
|
|
1497
|
+
const g = gamma(lr**3 * -1.2684380046 + lg**3 * 2.6097574011 + lb**3 * -0.3413193965)
|
|
1498
|
+
const bv = gamma(lr**3 * -0.0041960863 + lg**3 * -0.7034186147 + lb**3 * 1.7076147010)
|
|
1499
|
+
return toHex(r, g, bv)
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
function hslaToHex(str: string): string {
|
|
1503
|
+
let [h, s, l] = parseArgs(str).map(Number)
|
|
1504
|
+
s /= 100; l /= 100
|
|
1505
|
+
const k = (n: number) => (n + h / 30) % 12
|
|
1506
|
+
const f = (n: number) => l - s * Math.min(l, 1 - l) * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1))
|
|
1507
|
+
return toHex(f(0), f(8), f(4))
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const parseArgs = (s: string) =>
|
|
1511
|
+
s.replace(/^[\w-]+\(/, '').replace(')', '').split(/[\s,\/]+/).filter(Boolean)
|
|
1512
|
+
const gamma = (x: number) => x <= 0.0031308 ? 12.92 * x : 1.055 * x ** (1/2.4) - 0.055
|
|
1513
|
+
const sat = (x: number) => Math.max(0, Math.min(1, x))
|
|
1514
|
+
const toHex = (r: number, g: number, b: number) =>
|
|
1515
|
+
'#' + [r, g, b].map(v => Math.round(sat(v) * 255).toString(16).padStart(2, '0')).join('')
|
|
1516
|
+
|
|
1517
|
+
---
|
|
1518
|
+
|
|
1519
|
+
### 4. TSL Utilities
|
|
1520
|
+
|
|
1521
|
+
// layers/shader/tsl/utils/screenAspectUV.ts
|
|
1522
|
+
import { Fn, uv, vec2, screenSize } from 'three/tsl'
|
|
1523
|
+
|
|
1524
|
+
// Aspect-corrected UV centred at origin (-0.5..0.5 on short axis)
|
|
1525
|
+
export const screenAspectUV = Fn(() => {
|
|
1526
|
+
const aspect = screenSize.x.div(screenSize.y)
|
|
1527
|
+
return uv().sub(0.5).mul(vec2(aspect, 1.0))
|
|
1528
|
+
})
|
|
1529
|
+
|
|
1530
|
+
|
|
1531
|
+
|
|
1532
|
+
// layers/shader/tsl/noise/gradientNoise.ts
|
|
1533
|
+
import { Fn, vec2, dot, fract, sin, floor, mix } from 'three/tsl'
|
|
1534
|
+
|
|
1535
|
+
// Inigo Quilez gradient noise -- same hash used across phobon shaders
|
|
1536
|
+
const hash2 = Fn(([p]: [any]) => {
|
|
1537
|
+
const q = vec2(
|
|
1538
|
+
dot(p, vec2(2127.1, 81.17)),
|
|
1539
|
+
dot(p, vec2(1269.5, 283.37))
|
|
1540
|
+
)
|
|
1541
|
+
return fract(sin(q).mul(43758.5453))
|
|
1542
|
+
})
|
|
1543
|
+
|
|
1544
|
+
export const gradientNoise = Fn(([p]: [any]) => {
|
|
1545
|
+
const i = floor(p)
|
|
1546
|
+
const f = fract(p)
|
|
1547
|
+
const u = f.mul(f).mul(f.oneMinus().mul(2).add(1)) // smoothstep
|
|
1548
|
+
|
|
1549
|
+
const n = mix(
|
|
1550
|
+
mix(
|
|
1551
|
+
dot(hash2(i.add(vec2(0, 0))).mul(2).sub(1), f.sub(vec2(0, 0))),
|
|
1552
|
+
dot(hash2(i.add(vec2(1, 0))).mul(2).sub(1), f.sub(vec2(1, 0))),
|
|
1553
|
+
u.x
|
|
1554
|
+
),
|
|
1555
|
+
mix(
|
|
1556
|
+
dot(hash2(i.add(vec2(0, 1))).mul(2).sub(1), f.sub(vec2(0, 1))),
|
|
1557
|
+
dot(hash2(i.add(vec2(1, 1))).mul(2).sub(1), f.sub(vec2(1, 1))),
|
|
1558
|
+
u.x
|
|
1559
|
+
),
|
|
1560
|
+
u.y
|
|
1561
|
+
)
|
|
1562
|
+
return n.mul(0.5).add(0.5) // remap to 0..1
|
|
1563
|
+
})
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
// layers/shader/tsl/sdf/shapes.ts
|
|
1568
|
+
import { Fn, length, abs, max, vec2 } from 'three/tsl'
|
|
1569
|
+
|
|
1570
|
+
// Signed distance to a sphere/circle at origin
|
|
1571
|
+
export const sdSphere = Fn(([p]: [any]) => length(p))
|
|
1572
|
+
|
|
1573
|
+
// Signed distance to a 2D box centred at origin, half-extents b
|
|
1574
|
+
export const sdBox2d = Fn(([p, b]: [any, any]) => {
|
|
1575
|
+
const d = abs(p).sub(b)
|
|
1576
|
+
return length(max(d, 0.0)).add(max(d.x, d.y).min(0.0))
|
|
1577
|
+
})
|
|
1578
|
+
|
|
1579
|
+
---
|
|
1580
|
+
|
|
1581
|
+
### 5. ShaderPipeline Component
|
|
1582
|
+
|
|
1583
|
+
<!-- layers/shader/components/ShaderPipeline.vue -->
|
|
1584
|
+
<script setup lang="ts">
|
|
1585
|
+
import { provide, shallowRef, watch } from 'vue'
|
|
1586
|
+
import * as THREE from 'three'
|
|
1587
|
+
import { vec4, positionLocal } from 'three/tsl'
|
|
1588
|
+
import type { Node } from 'three/tsl'
|
|
1589
|
+
import type { StageEntry, ShaderPipelineContext } from '../types'
|
|
1590
|
+
|
|
1591
|
+
const fragmentStages = shallowRef<StageEntry[]>([])
|
|
1592
|
+
const vertexStages = shallowRef<StageEntry[]>([])
|
|
1593
|
+
|
|
1594
|
+
// MeshBasicNodeMaterial works with WebGPU and WebGL2 via TSL
|
|
1595
|
+
const material = new THREE.MeshBasicNodeMaterial({ transparent: true })
|
|
1596
|
+
|
|
1597
|
+
function sortedInsert(arr: StageEntry[], entry: StageEntry): StageEntry[] {
|
|
1598
|
+
return [...arr, entry].sort((a, b) => a.order - b.order)
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const ctx: ShaderPipelineContext = {
|
|
1602
|
+
register(fn, order = 0, stage = 'fragment') {
|
|
1603
|
+
const entry: StageEntry = { fn, order, stage }
|
|
1604
|
+
if (stage === 'vertex') {
|
|
1605
|
+
vertexStages.value = sortedInsert(vertexStages.value, entry)
|
|
1606
|
+
} else {
|
|
1607
|
+
fragmentStages.value = sortedInsert(fragmentStages.value, entry)
|
|
1608
|
+
}
|
|
1609
|
+
},
|
|
1610
|
+
unregister(fn) {
|
|
1611
|
+
fragmentStages.value = fragmentStages.value.filter(s => s.fn !== fn)
|
|
1612
|
+
vertexStages.value = vertexStages.value.filter(s => s.fn !== fn)
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
provide('shaderPipeline', ctx)
|
|
1617
|
+
|
|
1618
|
+
// Rebuild colorNode whenever fragment stages change
|
|
1619
|
+
watch(fragmentStages, (stages) => {
|
|
1620
|
+
material.colorNode = stages.reduce<Node>(
|
|
1621
|
+
(node, { fn }) => fn(node),
|
|
1622
|
+
vec4(0, 0, 0, 1)
|
|
1623
|
+
)
|
|
1624
|
+
material.needsUpdate = true
|
|
1625
|
+
}, { deep: true })
|
|
1626
|
+
|
|
1627
|
+
// Rebuild positionNode whenever vertex stages change
|
|
1628
|
+
watch(vertexStages, (stages) => {
|
|
1629
|
+
if (stages.length === 0) {
|
|
1630
|
+
material.positionNode = null
|
|
1631
|
+
return
|
|
1632
|
+
}
|
|
1633
|
+
material.positionNode = stages.reduce<Node>(
|
|
1634
|
+
(node, { fn }) => fn(node),
|
|
1635
|
+
positionLocal
|
|
1636
|
+
)
|
|
1637
|
+
material.needsUpdate = true
|
|
1638
|
+
}, { deep: true })
|
|
1639
|
+
|
|
1640
|
+
defineExpose({ material })
|
|
1641
|
+
</script>
|
|
1642
|
+
|
|
1643
|
+
<template>
|
|
1644
|
+
<primitive :object="material" />
|
|
1645
|
+
<slot />
|
|
1646
|
+
</template>
|
|
1647
|
+
|
|
1648
|
+
---
|
|
1649
|
+
|
|
1650
|
+
### 6. Generators
|
|
1651
|
+
|
|
1652
|
+
#### TSL functions
|
|
1653
|
+
|
|
1654
|
+
// layers/shader/tsl/generators/solidColour.ts
|
|
1655
|
+
import { Fn, vec4 } from 'three/tsl'
|
|
1656
|
+
|
|
1657
|
+
export const solidColour = Fn(([colour]: [any]) => vec4(colour, 1.0))
|
|
1658
|
+
|
|
1659
|
+
|
|
1660
|
+
|
|
1661
|
+
// layers/shader/tsl/generators/linearGradient.ts
|
|
1662
|
+
import { Fn, mix, uv, vec4, smoothstep } from 'three/tsl'
|
|
1663
|
+
|
|
1664
|
+
export const linearGradient = Fn(([colourA, colourB, axis, smooth]: [any, any, any, any]) => {
|
|
1665
|
+
const t = axis // pass uv().y or uv().x from the component
|
|
1666
|
+
const factor = smooth.equal(1).select(t.smoothstep(0, 1), t)
|
|
1667
|
+
return mix(vec4(colourA, 1), vec4(colourB, 1), factor)
|
|
1668
|
+
})
|
|
1669
|
+
|
|
1670
|
+
|
|
1671
|
+
|
|
1672
|
+
// layers/shader/tsl/generators/cosinePalette.ts
|
|
1673
|
+
import { Fn, add, mul, cos, vec3 } from 'three/tsl'
|
|
1674
|
+
|
|
1675
|
+
// IQ cosine palette: a + b * cos(TAU * (c*t + d))
|
|
1676
|
+
// All params are vec3 nodes; t is a float node
|
|
1677
|
+
export const cosinePalette = Fn(([t, a, b, c, d]: [any, any, any, any, any]) => {
|
|
1678
|
+
const TAU = 6.28318530718
|
|
1679
|
+
return add(a, mul(b, cos(mul(TAU, add(mul(c, t), d)))))
|
|
1680
|
+
})
|
|
1681
|
+
|
|
1682
|
+
#### Vue components
|
|
1683
|
+
|
|
1684
|
+
<!-- layers/shader/components/generators/SolidColour.vue -->
|
|
1685
|
+
<script setup lang="ts">
|
|
1686
|
+
import { uniform, vec3, vec4 } from 'three/tsl'
|
|
1687
|
+
import { Color } from 'three'
|
|
1688
|
+
import { watch } from 'vue'
|
|
1689
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1690
|
+
import { useCSSColourUniform } from '../../composables/useCSSUniform'
|
|
1691
|
+
import { resolveColour } from '../../tsl/utils/colour'
|
|
1692
|
+
|
|
1693
|
+
const props = defineProps<{
|
|
1694
|
+
color?: string
|
|
1695
|
+
cssColor?: string
|
|
1696
|
+
order?: number
|
|
1697
|
+
}>()
|
|
1698
|
+
|
|
1699
|
+
const colorNode = props.cssColor
|
|
1700
|
+
? useCSSColourUniform(props.cssColor)
|
|
1701
|
+
: uniform(new Color(resolveColour(props.color ?? '#000000')))
|
|
1702
|
+
|
|
1703
|
+
if (!props.cssColor && props.color) {
|
|
1704
|
+
watch(() => props.color, v => {
|
|
1705
|
+
if (v) (colorNode as any).value.set(resolveColour(v))
|
|
1706
|
+
})
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
useShaderStage(
|
|
1710
|
+
(_prev) => vec4(colorNode, 1.0),
|
|
1711
|
+
props.order ?? 0
|
|
1712
|
+
)
|
|
1713
|
+
</script>
|
|
1714
|
+
|
|
1715
|
+
<template />
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
<!-- layers/shader/components/generators/CosinePalette.vue -->
|
|
1720
|
+
<script setup lang="ts">
|
|
1721
|
+
import { uniform, vec3, vec4, uv, time, length } from 'three/tsl'
|
|
1722
|
+
import { Color } from 'three'
|
|
1723
|
+
import { watch } from 'vue'
|
|
1724
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1725
|
+
import { useCSSColourUniform } from '../../composables/useCSSUniform'
|
|
1726
|
+
import { cosinePalette as cosinePaletteFn } from '../../tsl/generators/cosinePalette'
|
|
1727
|
+
|
|
1728
|
+
type Vec3Tuple = [number, number, number]
|
|
1729
|
+
|
|
1730
|
+
const props = defineProps<{
|
|
1731
|
+
// Palette vectors -- static or CSS var
|
|
1732
|
+
a?: Vec3Tuple; cssA?: string
|
|
1733
|
+
b?: Vec3Tuple; cssB?: string
|
|
1734
|
+
c?: Vec3Tuple; cssC?: string
|
|
1735
|
+
d?: Vec3Tuple; cssD?: string
|
|
1736
|
+
// Scalar input source
|
|
1737
|
+
scalarSource?: 'uv.y' | 'uv.x' | 'radial' | 'prev.r'
|
|
1738
|
+
offset?: number
|
|
1739
|
+
timeScale?: number
|
|
1740
|
+
order?: number
|
|
1741
|
+
}>()
|
|
1742
|
+
|
|
1743
|
+
const toNode = (css: string | undefined, val: Vec3Tuple = [0.5, 0.5, 0.5]) =>
|
|
1744
|
+
css
|
|
1745
|
+
? useCSSColourUniform(css)
|
|
1746
|
+
: uniform(new Color().setRGB(...val))
|
|
1747
|
+
|
|
1748
|
+
const aNode = toNode(props.cssA, props.a)
|
|
1749
|
+
const bNode = toNode(props.cssB, props.b)
|
|
1750
|
+
const cNode = toNode(props.cssC, props.c)
|
|
1751
|
+
const dNode = toNode(props.cssD, props.d)
|
|
1752
|
+
|
|
1753
|
+
const offsetU = uniform(props.offset ?? 0)
|
|
1754
|
+
const timeScaleU = uniform(props.timeScale ?? 0)
|
|
1755
|
+
|
|
1756
|
+
watch(() => props.offset, v => { offsetU.value = v ?? 0 })
|
|
1757
|
+
watch(() => props.timeScale, v => { timeScaleU.value = v ?? 0 })
|
|
1758
|
+
|
|
1759
|
+
useShaderStage((_prev) => {
|
|
1760
|
+
// Derive t from chosen scalar source
|
|
1761
|
+
let t: any
|
|
1762
|
+
switch (props.scalarSource ?? 'uv.y') {
|
|
1763
|
+
case 'uv.x': t = uv().x.add(offsetU).sub(time.mul(timeScaleU)); break
|
|
1764
|
+
case 'radial': t = length(uv().sub(0.5)).add(offsetU); break
|
|
1765
|
+
case 'prev.r': t = _prev.r.add(offsetU); break
|
|
1766
|
+
default: t = uv().y.add(offsetU).sub(time.mul(timeScaleU))
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const col = cosinePaletteFn(t, aNode, bNode, cNode, dNode)
|
|
1770
|
+
return vec4(col, 1.0)
|
|
1771
|
+
}, props.order ?? 0)
|
|
1772
|
+
</script>
|
|
1773
|
+
|
|
1774
|
+
<template />
|
|
1775
|
+
|
|
1776
|
+
---
|
|
1777
|
+
|
|
1778
|
+
### 7. UV Transformers
|
|
1779
|
+
|
|
1780
|
+
#### TSL functions
|
|
1781
|
+
|
|
1782
|
+
// layers/shader/tsl/uvTransformers/uvAspectCorrect.ts
|
|
1783
|
+
import { Fn, uv, vec2, screenSize } from 'three/tsl'
|
|
1784
|
+
|
|
1785
|
+
export const uvAspectCorrect = Fn(() => {
|
|
1786
|
+
const aspect = screenSize.x.div(screenSize.y)
|
|
1787
|
+
return uv().sub(0.5).mul(vec2(aspect, 1.0))
|
|
1788
|
+
})
|
|
1789
|
+
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
// layers/shader/tsl/uvTransformers/uvNoiseRotate.ts
|
|
1793
|
+
import { Fn, vec2, cos, sin, time } from 'three/tsl'
|
|
1794
|
+
import { gradientNoise } from '../noise/gradientNoise'
|
|
1795
|
+
|
|
1796
|
+
// Rotate UV by a noise-derived angle -- the Grainient core
|
|
1797
|
+
export const uvNoiseRotate = Fn(([uvIn, noiseScale, rotationAmount, speed]: [any, any, any, any]) => {
|
|
1798
|
+
const t = time.mul(speed)
|
|
1799
|
+
const deg = gradientNoise(vec2(t.mul(0.1), uvIn.x.mul(uvIn.y)).mul(noiseScale))
|
|
1800
|
+
const angle = deg.sub(0.5).mul(rotationAmount).add(180).mul(0.017453293) // to radians
|
|
1801
|
+
|
|
1802
|
+
const aspect = vec2(1.0) // already aspect-corrected upstream
|
|
1803
|
+
const c = cos(angle)
|
|
1804
|
+
const s = sin(angle)
|
|
1805
|
+
|
|
1806
|
+
const centred = uvIn
|
|
1807
|
+
const rotated = vec2(
|
|
1808
|
+
c.mul(centred.x).sub(s.mul(centred.y)),
|
|
1809
|
+
s.mul(centred.x).add(c.mul(centred.y))
|
|
1810
|
+
)
|
|
1811
|
+
// Restore aspect
|
|
1812
|
+
return rotated
|
|
1813
|
+
})
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
|
|
1817
|
+
// layers/shader/tsl/uvTransformers/uvSineWarpXY.ts
|
|
1818
|
+
import { Fn, vec2, sin, time } from 'three/tsl'
|
|
1819
|
+
|
|
1820
|
+
// Asymmetric sine warp -- the Grainient/dawn family pattern
|
|
1821
|
+
export const uvSineWarpXY = Fn(([uvIn, frequency, amplitude, speed]: [any, any, any, any]) => {
|
|
1822
|
+
const t = time.mul(speed)
|
|
1823
|
+
const wx = sin(uvIn.y.mul(frequency).add(t)).div(amplitude)
|
|
1824
|
+
const wy = sin(uvIn.x.mul(frequency.mul(1.5)).add(t)).div(amplitude.mul(0.5))
|
|
1825
|
+
return uvIn.add(vec2(wx, wy))
|
|
1826
|
+
})
|
|
1827
|
+
|
|
1828
|
+
#### Vue component pattern
|
|
1829
|
+
|
|
1830
|
+
<!-- layers/shader/components/uvTransformers/UVAspectCorrect.vue -->
|
|
1831
|
+
<script setup lang="ts">
|
|
1832
|
+
import { uv, vec2, screenSize } from 'three/tsl'
|
|
1833
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1834
|
+
|
|
1835
|
+
const props = defineProps<{ order?: number }>()
|
|
1836
|
+
|
|
1837
|
+
useShaderStage(
|
|
1838
|
+
(_prev) => {
|
|
1839
|
+
const aspect = screenSize.x.div(screenSize.y)
|
|
1840
|
+
return uv().sub(0.5).mul(vec2(aspect, 1.0))
|
|
1841
|
+
},
|
|
1842
|
+
props.order ?? 0
|
|
1843
|
+
)
|
|
1844
|
+
</script>
|
|
1845
|
+
|
|
1846
|
+
<template />
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
|
|
1850
|
+
<!-- layers/shader/components/uvTransformers/UVSineWarpXY.vue -->
|
|
1851
|
+
<script setup lang="ts">
|
|
1852
|
+
import { uniform } from 'three/tsl'
|
|
1853
|
+
import { watch } from 'vue'
|
|
1854
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1855
|
+
import { uvSineWarpXY } from '../../tsl/uvTransformers/uvSineWarpXY'
|
|
1856
|
+
|
|
1857
|
+
const props = defineProps<{
|
|
1858
|
+
frequency?: number
|
|
1859
|
+
amplitude?: number
|
|
1860
|
+
speed?: number
|
|
1861
|
+
order?: number
|
|
1862
|
+
}>()
|
|
1863
|
+
|
|
1864
|
+
const freqU = uniform(props.frequency ?? 5.0)
|
|
1865
|
+
const ampU = uniform(props.amplitude ?? 30.0)
|
|
1866
|
+
const spdU = uniform(props.speed ?? 2.0)
|
|
1867
|
+
|
|
1868
|
+
watch(() => props.frequency, v => { freqU.value = v ?? 5.0 })
|
|
1869
|
+
watch(() => props.amplitude, v => { ampU.value = v ?? 30.0 })
|
|
1870
|
+
watch(() => props.speed, v => { spdU.value = v ?? 2.0 })
|
|
1871
|
+
|
|
1872
|
+
useShaderStage(
|
|
1873
|
+
(prev) => uvSineWarpXY(prev, freqU, ampU, spdU),
|
|
1874
|
+
props.order ?? 0
|
|
1875
|
+
)
|
|
1876
|
+
</script>
|
|
1877
|
+
|
|
1878
|
+
<template />
|
|
1879
|
+
|
|
1880
|
+
---
|
|
1881
|
+
|
|
1882
|
+
### 8. Colour Ops
|
|
1883
|
+
|
|
1884
|
+
// layers/shader/tsl/colourOps/brightnessContrast.ts
|
|
1885
|
+
import { Fn, clamp } from 'three/tsl'
|
|
1886
|
+
|
|
1887
|
+
export const brightnessContrast = Fn(([col, brightness, contrast]: [any, any, any]) =>
|
|
1888
|
+
clamp(col.add(brightness).sub(0.5).mul(contrast).add(0.5), 0, 1)
|
|
1889
|
+
)
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
|
|
1893
|
+
// layers/shader/tsl/colourOps/tanhTonemap.ts
|
|
1894
|
+
import { Fn, tanh } from 'three/tsl'
|
|
1895
|
+
|
|
1896
|
+
// Soft sigmoid tone compression -- gentler than ACES, preserves saturation
|
|
1897
|
+
export const tanhTonemap = Fn(([col, exposure]: [any, any]) =>
|
|
1898
|
+
tanh(col.mul(exposure))
|
|
1899
|
+
)
|
|
1900
|
+
|
|
1901
|
+
|
|
1902
|
+
|
|
1903
|
+
<!-- layers/shader/components/colourOps/TanhTonemap.vue -->
|
|
1904
|
+
<script setup lang="ts">
|
|
1905
|
+
import { uniform } from 'three/tsl'
|
|
1906
|
+
import { watch } from 'vue'
|
|
1907
|
+
import { vec4 } from 'three/tsl'
|
|
1908
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1909
|
+
import { tanhTonemap } from '../../tsl/colourOps/tanhTonemap'
|
|
1910
|
+
|
|
1911
|
+
const props = defineProps<{ exposure?: number; order?: number }>()
|
|
1912
|
+
|
|
1913
|
+
const expU = uniform(props.exposure ?? 1.0)
|
|
1914
|
+
watch(() => props.exposure, v => { expU.value = v ?? 1.0 })
|
|
1915
|
+
|
|
1916
|
+
useShaderStage(
|
|
1917
|
+
(prev) => vec4(tanhTonemap(prev.rgb, expU), prev.a),
|
|
1918
|
+
props.order ?? 0
|
|
1919
|
+
)
|
|
1920
|
+
</script>
|
|
1921
|
+
|
|
1922
|
+
<template />
|
|
1923
|
+
|
|
1924
|
+
---
|
|
1925
|
+
|
|
1926
|
+
### 9. Overlays
|
|
1927
|
+
|
|
1928
|
+
// layers/shader/tsl/overlays/grain.ts
|
|
1929
|
+
import { Fn, fract, sin, dot, uv, time, vec2, clamp } from 'three/tsl'
|
|
1930
|
+
|
|
1931
|
+
export const grain = Fn(([col, intensity, animated]: [any, any, any]) => {
|
|
1932
|
+
const seed = animated.equal(1).select(uv().add(time.mul(0.001)), uv())
|
|
1933
|
+
const noise = fract(sin(dot(seed, vec2(12.9898, 78.233))).mul(43758.5453))
|
|
1934
|
+
return clamp(col.add(noise.sub(0.5).mul(intensity)), 0, 1)
|
|
1935
|
+
})
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
// layers/shader/tsl/overlays/vignette.ts
|
|
1940
|
+
import { Fn, uv, vec2, length, smoothstep, mix } from 'three/tsl'
|
|
1941
|
+
|
|
1942
|
+
export const vignette = Fn(([col, strength, softness]: [any, any, any]) => {
|
|
1943
|
+
const dist = length(uv().sub(0.5)).mul(1.4142) // normalise: corner = 1
|
|
1944
|
+
const vig = dist.smoothstep(strength.oneMinus().mul(softness), 1.0).mul(strength)
|
|
1945
|
+
return mix(col, col.mul(0), vig)
|
|
1946
|
+
})
|
|
1947
|
+
|
|
1948
|
+
|
|
1949
|
+
|
|
1950
|
+
<!-- layers/shader/components/overlays/Grain.vue -->
|
|
1951
|
+
<script setup lang="ts">
|
|
1952
|
+
import { uniform, vec4 } from 'three/tsl'
|
|
1953
|
+
import { watch } from 'vue'
|
|
1954
|
+
import { useShaderStage } from '../../composables/useShaderStage'
|
|
1955
|
+
import { grain as grainFn } from '../../tsl/overlays/grain'
|
|
1956
|
+
|
|
1957
|
+
const props = defineProps<{
|
|
1958
|
+
intensity?: number
|
|
1959
|
+
animated?: boolean
|
|
1960
|
+
order?: number
|
|
1961
|
+
}>()
|
|
1962
|
+
|
|
1963
|
+
const intU = uniform(props.intensity ?? 0.04)
|
|
1964
|
+
const animU = uniform(props.animated ? 1 : 0)
|
|
1965
|
+
|
|
1966
|
+
watch(() => props.intensity, v => { intU.value = v ?? 0.04 })
|
|
1967
|
+
watch(() => props.animated, v => { animU.value = v ? 1 : 0 })
|
|
1968
|
+
|
|
1969
|
+
useShaderStage(
|
|
1970
|
+
(prev) => vec4(grainFn(prev.rgb, intU, animU), prev.a),
|
|
1971
|
+
props.order ?? 0
|
|
1972
|
+
)
|
|
1973
|
+
</script>
|
|
1974
|
+
|
|
1975
|
+
<template />
|
|
1976
|
+
|
|
1977
|
+
---
|
|
1978
|
+
|
|
1979
|
+
### 10. Barrel Export
|
|
1980
|
+
|
|
1981
|
+
// layers/shader/index.ts
|
|
1982
|
+
// Core
|
|
1983
|
+
export { default as ShaderPipeline } from './components/ShaderPipeline.vue'
|
|
1984
|
+
export { useShaderStage } from './composables/useShaderStage'
|
|
1985
|
+
export { useThemeProvider } from './composables/useThemeProvider'
|
|
1986
|
+
export { useCSSColourUniform, useCSSFloatUniform } from './composables/useCSSUniform'
|
|
1987
|
+
|
|
1988
|
+
// Generators
|
|
1989
|
+
export { default as SolidColour } from './components/generators/SolidColour.vue'
|
|
1990
|
+
export { default as CosinePalette } from './components/generators/CosinePalette.vue'
|
|
1991
|
+
export { default as LinearGradient } from './components/generators/LinearGradient.vue'
|
|
1992
|
+
|
|
1993
|
+
// UV Transformers
|
|
1994
|
+
export { default as UVAspectCorrect } from './components/uvTransformers/UVAspectCorrect.vue'
|
|
1995
|
+
export { default as UVSineWarpXY } from './components/uvTransformers/UVSineWarpXY.vue'
|
|
1996
|
+
|
|
1997
|
+
// Colour Ops
|
|
1998
|
+
export { default as TanhTonemap } from './components/colourOps/TanhTonemap.vue'
|
|
1999
|
+
|
|
2000
|
+
// Overlays
|
|
2001
|
+
export { default as Grain } from './components/overlays/Grain.vue'
|
|
2002
|
+
export { default as Vignette } from './components/overlays/Vignette.vue'
|
|
2003
|
+
|
|
2004
|
+
// TSL primitives (for building custom blocks)
|
|
2005
|
+
export { cosinePalette } from './tsl/generators/cosinePalette'
|
|
2006
|
+
export { gradientNoise } from './tsl/noise/gradientNoise'
|
|
2007
|
+
export { screenAspectUV } from './tsl/utils/screenAspectUV'
|
|
2008
|
+
export { sdSphere, sdBox2d } from './tsl/sdf/shapes'
|
|
2009
|
+
export { resolveColour } from './tsl/utils/colour'
|
|
2010
|
+
|
|
2011
|
+
---
|
|
2012
|
+
|
|
2013
|
+
### 11. Usage Example -- Dawn2 Recreation
|
|
2014
|
+
|
|
2015
|
+
<!-- A complete working shader using the block system -->
|
|
2016
|
+
<script setup lang="ts">
|
|
2017
|
+
import { TresCanvas } from '@tresjs/core'
|
|
2018
|
+
import {
|
|
2019
|
+
ShaderPipeline,
|
|
2020
|
+
CosinePalette,
|
|
2021
|
+
UVAspectCorrect,
|
|
2022
|
+
Grain,
|
|
2023
|
+
useThemeProvider
|
|
2024
|
+
} from '@/layers/shader'
|
|
2025
|
+
|
|
2026
|
+
useThemeProvider()
|
|
2027
|
+
</script>
|
|
2028
|
+
|
|
2029
|
+
<template>
|
|
2030
|
+
<TresCanvas>
|
|
2031
|
+
<TresMesh>
|
|
2032
|
+
<TresPlaneGeometry :args="[2, 2]" />
|
|
2033
|
+
<ShaderPipeline>
|
|
2034
|
+
<UVAspectCorrect :order="0" />
|
|
2035
|
+
<CosinePalette
|
|
2036
|
+
:a="[0.5, 0.5, 0.5]"
|
|
2037
|
+
:b="[0.5, 0.5, 0.5]"
|
|
2038
|
+
:c="[1.0, 0.7, 0.4]"
|
|
2039
|
+
:d="[0.0, 0.15, 0.2]"
|
|
2040
|
+
scalar-source="uv.y"
|
|
2041
|
+
:time-scale="0.1"
|
|
2042
|
+
:order="1"
|
|
2043
|
+
/>
|
|
2044
|
+
<Grain :intensity="0.2" :animated="true" :order="2" />
|
|
2045
|
+
</ShaderPipeline>
|
|
2046
|
+
</TresMesh>
|
|
2047
|
+
</TresCanvas>
|
|
2048
|
+
</template>
|
|
2049
|
+
|
|
2050
|
+
---
|
|
2051
|
+
|
|
2052
|
+
### 12. Building New Blocks -- Rules
|
|
2053
|
+
|
|
2054
|
+
Every block follows this exact pattern:
|
|
2055
|
+
|
|
2056
|
+
**TSL function** (`tsl/<category>/<name>.ts`)
|
|
2057
|
+
|
|
2058
|
+
- Pure TSL node function, no Vue, no Three scene knowledge
|
|
2059
|
+
- Takes node params, returns a node
|
|
2060
|
+
- Exported as a named `Fn`
|
|
2061
|
+
|
|
2062
|
+
**Vue component** (`components/<category>/<Name>.vue`)
|
|
2063
|
+
|
|
2064
|
+
- `<template />` -- always empty
|
|
2065
|
+
- Props → `uniform()` nodes with `watch` for reactivity
|
|
2066
|
+
- CSS var props → `useCSSColourUniform()` / `useCSSFloatUniform()`
|
|
2067
|
+
- Calls `useShaderStage(stageFn, order, stage)` in `<script setup>`
|
|
2068
|
+
- `stageFn` signature: `(prev: Node) => Node`
|
|
2069
|
+
- Generators ignore `prev` and return a new colour node
|
|
2070
|
+
- UV Transformers return a new UV node (the whole pipeline is UV-shaped at that point)
|
|
2071
|
+
- Colour Ops and Overlays receive and return a `vec4` colour node
|