kmcom-nuxt-layers 1.6.4 → 1.6.6
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/layers/core/app/.DS_Store +0 -0
- package/layers/core/app/assets/.DS_Store +0 -0
- package/layers/core/app/assets/css/main.css +2 -0
- package/layers/core/app/components/ErrorBoundary.vue +1 -1
- package/layers/core/nuxt.config.ts +0 -8
- package/layers/motion/app/plugins/locomotive-scroll.client.ts +14 -11
- package/layers/motion/app.config.ts +6 -0
- package/layers/shader/app/components/Pipeline/ACESTonemap.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/AddBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/AgedFilm.client.vue +62 -0
- package/layers/shader/app/components/Pipeline/Aurora.client.vue +81 -0
- package/layers/shader/app/components/Pipeline/BilinearGradient.client.vue +46 -0
- package/layers/shader/app/components/Pipeline/BillowNoise.client.vue +42 -0
- package/layers/shader/app/components/Pipeline/BrightnessContrast.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/CellularNoise.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/ChannelMixer.client.vue +44 -0
- package/layers/shader/app/components/Pipeline/ChebyshevNoiseField.client.vue +60 -0
- package/layers/shader/app/components/Pipeline/Checkerboard.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/ChromaticAberration.client.vue +52 -0
- package/layers/shader/app/components/Pipeline/ChromaticScreenWaves.client.vue +49 -0
- package/layers/shader/app/components/Pipeline/Circle.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/Clouds.client.vue +60 -0
- package/layers/shader/app/components/Pipeline/ColorBurnBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/ColorDodgeBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/ColourRamp.client.vue +57 -0
- package/layers/shader/app/components/Pipeline/ComplexPlaneField.client.vue +77 -0
- package/layers/shader/app/components/Pipeline/ConicGradient.client.vue +40 -0
- package/layers/shader/app/components/Pipeline/CosinePalette.client.vue +71 -0
- package/layers/shader/app/components/Pipeline/CoverageAlpha.client.vue +18 -0
- package/layers/shader/app/components/Pipeline/Cross.client.vue +51 -0
- package/layers/shader/app/components/Pipeline/CurlNoise.client.vue +48 -0
- package/layers/shader/app/components/Pipeline/DarkenBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/DayNightCycle.client.vue +48 -0
- package/layers/shader/app/components/Pipeline/Desaturate.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/DiagonalGradient.client.vue +38 -0
- package/layers/shader/app/components/Pipeline/DiamondGradient.client.vue +40 -0
- package/layers/shader/app/components/Pipeline/DifferenceBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/DivideBlend.client.vue +26 -0
- package/layers/shader/app/components/Pipeline/DomainWarpedNoise.client.vue +48 -0
- package/layers/shader/app/components/Pipeline/Dots.client.vue +44 -0
- package/layers/shader/app/components/Pipeline/DuoTone.client.vue +34 -0
- package/layers/shader/app/components/Pipeline/ExclusionBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/ExponentialFog.client.vue +46 -0
- package/layers/shader/app/components/Pipeline/Exposure.client.vue +23 -0
- package/layers/shader/app/components/Pipeline/FBMNoise.client.vue +50 -0
- package/layers/shader/app/components/Pipeline/FilmBurn.client.vue +49 -0
- package/layers/shader/app/components/Pipeline/FilmGrain.client.vue +69 -0
- package/layers/shader/app/components/Pipeline/FisheyeRay.client.vue +35 -0
- package/layers/shader/app/components/Pipeline/Flame.client.vue +51 -0
- package/layers/shader/app/components/Pipeline/FocalGradient.client.vue +43 -0
- package/layers/shader/app/components/Pipeline/Gamma.client.vue +23 -0
- package/layers/shader/app/components/Pipeline/GodRays.client.vue +64 -0
- package/layers/shader/app/components/Pipeline/GradientNoise.client.vue +42 -0
- package/layers/shader/app/components/Pipeline/Grain.client.vue +81 -0
- package/layers/shader/app/components/Pipeline/Grid.client.vue +43 -0
- package/layers/shader/app/components/Pipeline/Halation.client.vue +43 -0
- package/layers/shader/app/components/Pipeline/Halftone.client.vue +33 -0
- package/layers/shader/app/components/Pipeline/HardLightBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/Haze.client.vue +50 -0
- package/layers/shader/app/components/Pipeline/Hexagon.client.vue +42 -0
- package/layers/shader/app/components/Pipeline/Hue.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/Invert.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/LensFlare.client.vue +67 -0
- package/layers/shader/app/components/Pipeline/LightenBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/LinearGradient.client.vue +36 -0
- package/layers/shader/app/components/Pipeline/LinearGradient4.client.vue +67 -0
- package/layers/shader/app/components/Pipeline/LinearToSRGB.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/Marble.client.vue +69 -0
- package/layers/shader/app/components/Pipeline/MixBlend.client.vue +45 -0
- package/layers/shader/app/components/Pipeline/MonochromeTint.client.vue +31 -0
- package/layers/shader/app/components/Pipeline/MultiplyBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/NoisyGradient.client.vue +54 -0
- package/layers/shader/app/components/Pipeline/NoisyGradientBlend.client.vue +116 -0
- package/layers/shader/app/components/Pipeline/OverlayBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/PaperTexture.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/Polygon.client.vue +47 -0
- package/layers/shader/app/components/Pipeline/Posterise.client.vue +23 -0
- package/layers/shader/app/components/Pipeline/RadialGradient.client.vue +39 -0
- package/layers/shader/app/components/Pipeline/RayAutoOrbit.client.vue +37 -0
- package/layers/shader/app/components/Pipeline/RayMouseOrbit.client.vue +49 -0
- package/layers/shader/app/components/Pipeline/RayRotateX.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/RayRotateY.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/RayRotateZ.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/RayTiltBasis.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/RaymarchTunnel.client.vue +101 -0
- package/layers/shader/app/components/Pipeline/Rectangle.client.vue +47 -0
- package/layers/shader/app/components/Pipeline/ReinhardTonemap.client.vue +18 -0
- package/layers/shader/app/components/Pipeline/RidgedNoise.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/Ring.client.vue +44 -0
- package/layers/shader/app/components/Pipeline/RingField.client.vue +69 -0
- package/layers/shader/app/components/Pipeline/RisographGrain.client.vue +74 -0
- package/layers/shader/app/components/Pipeline/RotatedGradientBlend.client.vue +61 -0
- package/layers/shader/app/components/Pipeline/SDFColourMask.client.vue +33 -0
- package/layers/shader/app/components/Pipeline/SDFRadialMask.client.vue +31 -0
- package/layers/shader/app/components/Pipeline/SRGBToLinear.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/Saturation.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/Scanlines.client.vue +36 -0
- package/layers/shader/app/components/Pipeline/ScreenBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/ShaderDebugger.client.vue +74 -0
- package/layers/shader/app/components/Pipeline/SimplexNoise.client.vue +49 -0
- package/layers/shader/app/components/Pipeline/SkyAtmosphere.client.vue +75 -0
- package/layers/shader/app/components/Pipeline/SoftLightBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/SolidColour.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/SplitTone.client.vue +47 -0
- package/layers/shader/app/components/Pipeline/Star.client.vue +47 -0
- package/layers/shader/app/components/Pipeline/Starfield.client.vue +65 -0
- package/layers/shader/app/components/Pipeline/Stripes.client.vue +46 -0
- package/layers/shader/app/components/Pipeline/SubtractBlend.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/TanhTonemap.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/Threshold.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/Tint.client.vue +33 -0
- package/layers/shader/app/components/Pipeline/Triangle.client.vue +42 -0
- package/layers/shader/app/components/Pipeline/UVAspectCorrect.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/UVBreath.client.vue +36 -0
- package/layers/shader/app/components/Pipeline/UVBulge.client.vue +26 -0
- package/layers/shader/app/components/Pipeline/UVClamp.client.vue +18 -0
- package/layers/shader/app/components/Pipeline/UVColumnOffset.client.vue +28 -0
- package/layers/shader/app/components/Pipeline/UVFlipX.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/UVFlipXY.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/UVFlipY.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/UVFractBand.client.vue +31 -0
- package/layers/shader/app/components/Pipeline/UVMousePull.client.vue +39 -0
- package/layers/shader/app/components/Pipeline/UVNoiseRotate.client.vue +54 -0
- package/layers/shader/app/components/Pipeline/UVNoiseWarp.client.vue +35 -0
- package/layers/shader/app/components/Pipeline/UVOrbit.client.vue +34 -0
- package/layers/shader/app/components/Pipeline/UVParallax.client.vue +39 -0
- package/layers/shader/app/components/Pipeline/UVPixelate.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/UVPulse.client.vue +33 -0
- package/layers/shader/app/components/Pipeline/UVRipple.client.vue +34 -0
- package/layers/shader/app/components/Pipeline/UVRotate.client.vue +37 -0
- package/layers/shader/app/components/Pipeline/UVScale.client.vue +30 -0
- package/layers/shader/app/components/Pipeline/UVScroll.client.vue +27 -0
- package/layers/shader/app/components/Pipeline/UVScrollX.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/UVScrollY.client.vue +21 -0
- package/layers/shader/app/components/Pipeline/UVShear.client.vue +25 -0
- package/layers/shader/app/components/Pipeline/UVSineWarpXY.client.vue +46 -0
- package/layers/shader/app/components/Pipeline/UVSwapAxes.client.vue +14 -0
- package/layers/shader/app/components/Pipeline/UVTile.client.vue +27 -0
- package/layers/shader/app/components/Pipeline/UVTwirl.client.vue +26 -0
- package/layers/shader/app/components/Pipeline/UVWarp.client.vue +30 -0
- package/layers/shader/app/components/Pipeline/VHSBleed.client.vue +58 -0
- package/layers/shader/app/components/Pipeline/ValueNoise.client.vue +41 -0
- package/layers/shader/app/components/Pipeline/Vibrance.client.vue +24 -0
- package/layers/shader/app/components/Pipeline/Vignette.client.vue +32 -0
- package/layers/shader/app/components/Pipeline/VoronoiEdges.client.vue +50 -0
- package/layers/shader/app/components/Pipeline/Water.client.vue +53 -0
- package/layers/shader/app/components/Pipeline/WaveBendLayer.client.vue +61 -0
- package/layers/shader/app/components/Pipeline/WaveColourLayer.client.vue +56 -0
- package/layers/shader/app/components/Pipeline/WhiteBalance.client.vue +40 -0
- package/layers/shader/app/components/Pipeline/Wood.client.vue +63 -0
- package/layers/shader/app/components/Shader/Canvas.vue +24 -0
- package/layers/shader/app/components/Shader/Pipeline.client.vue +56 -0
- package/layers/shader/app/components/Shader/PipelineContext.vue +18 -0
- package/layers/shader/app/composables/useCSSColourUniform.ts +41 -0
- package/layers/shader/app/composables/useCSSFloatUniform.ts +34 -0
- package/layers/shader/app/composables/useShaderPerf.ts +73 -0
- package/layers/shader/app/composables/useShaderPipeline.ts +99 -0
- package/layers/shader/app/composables/useSunDirectionUniform.ts +54 -0
- package/layers/shader/app/composables/useTSLNodes.ts +0 -1
- package/layers/shader/app/shaders/common/complex.ts +29 -0
- package/layers/shader/app/shaders/common/shapes.ts +4 -4
- package/layers/shader/app/shaders/common/uv.ts +2 -2
- package/layers/shader/app/utils/tsl/patterns.ts +1 -1
- package/layers/shader/app/utils/tsl/uv.ts +3 -3
- package/package.json +9 -7
|
Binary file
|
|
Binary file
|
|
@@ -61,7 +61,7 @@ const handleError = (error: unknown) => {
|
|
|
61
61
|
<template #error="{ error, clearError }">
|
|
62
62
|
<slot name="error" :error :clear-error>
|
|
63
63
|
<!-- Default error UI -->
|
|
64
|
-
<div class="rounded-lg border border-error bg-error/10 p-4">
|
|
64
|
+
<div class="rounded-lg border border-error bg-error/10 p-4 ">
|
|
65
65
|
<div class="flex items-start gap-3">
|
|
66
66
|
<!-- Error icon -->
|
|
67
67
|
<span
|
|
@@ -21,14 +21,6 @@ export default defineNuxtConfig({
|
|
|
21
21
|
|
|
22
22
|
css: ['#layers/core/app/assets/css/main.css'],
|
|
23
23
|
|
|
24
|
-
plugins: [
|
|
25
|
-
'#layers/core/app/plugins/init.ts',
|
|
26
|
-
'#layers/core/app/plugins/error-handler.ts',
|
|
27
|
-
'#layers/core/app/plugins/feature-detection.client.ts',
|
|
28
|
-
'#layers/core/app/plugins/loading.client.ts',
|
|
29
|
-
'#layers/core/app/plugins/scroll-guard.client.ts',
|
|
30
|
-
],
|
|
31
|
-
|
|
32
24
|
devtools: {
|
|
33
25
|
enabled: true,
|
|
34
26
|
|
|
@@ -45,19 +45,22 @@ export default defineNuxtPlugin(() => {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const router = useRouter()
|
|
48
|
+
const appConfig = useAppConfig() as any
|
|
49
|
+
const smoothScroll: boolean | string[] = appConfig.motion?.smoothScroll ?? true
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
addRouteMiddleware((to, from) => {
|
|
52
|
-
if (SMOOTH_SCROLL_ROUTES.includes(to.path)) {
|
|
53
|
-
nextTick(init)
|
|
54
|
-
} else if (from?.path && SMOOTH_SCROLL_ROUTES.includes(from.path)) {
|
|
55
|
-
destroy()
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
if (SMOOTH_SCROLL_ROUTES.includes(router.currentRoute.value.path)) {
|
|
51
|
+
if (smoothScroll === true) {
|
|
60
52
|
init()
|
|
53
|
+
} else if (Array.isArray(smoothScroll)) {
|
|
54
|
+
addRouteMiddleware((to, from) => {
|
|
55
|
+
if (smoothScroll.includes(to.path)) {
|
|
56
|
+
nextTick(init)
|
|
57
|
+
} else if (from?.path && smoothScroll.includes(from.path)) {
|
|
58
|
+
destroy()
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
if (smoothScroll.includes(router.currentRoute.value.path)) {
|
|
62
|
+
init()
|
|
63
|
+
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
return {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export default defineAppConfig({
|
|
2
2
|
motion: {
|
|
3
|
+
/**
|
|
4
|
+
* Enable smooth scroll globally (true), on specific routes (string[]), or disable (false).
|
|
5
|
+
* @default true
|
|
6
|
+
*/
|
|
7
|
+
smoothScroll: true as boolean | string[],
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* Enable GSAP ScrollTrigger integration with Lenis
|
|
5
11
|
* @default true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { vec4 } from 'three/tsl'
|
|
4
|
+
import { acesTonemap } from '../../shaders/common/tonemapping'
|
|
5
|
+
|
|
6
|
+
const { order = 0 } = defineProps<{ order?: number }>()
|
|
7
|
+
|
|
8
|
+
useShaderStage(
|
|
9
|
+
(prev) => vec4(acesTonemap(prev.xyz), prev.w),
|
|
10
|
+
order,
|
|
11
|
+
)
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color } from 'three'
|
|
4
|
+
import { clamp, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
import { blendAdd } from '../../shaders/common/blend'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
color?: string
|
|
9
|
+
opacity?: number
|
|
10
|
+
order?: number
|
|
11
|
+
}>(), { color: '#808080', opacity: 1, order: 0 })
|
|
12
|
+
|
|
13
|
+
const colorNode = uniform(new Color(props.color))
|
|
14
|
+
const opacityNode = uniform(props.opacity)
|
|
15
|
+
watch(() => props.color, v => { colorNode.value.set(v) })
|
|
16
|
+
watch(() => props.opacity, v => { opacityNode.value = v })
|
|
17
|
+
|
|
18
|
+
useShaderStage(
|
|
19
|
+
(prev) => clamp(vec4(blendAdd(prev.xyz, colorNode, opacityNode), prev.w), 0, 1),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, time, vec3, vec4, clamp, sin, floor, fract, float } from 'three/tsl'
|
|
4
|
+
import { grain } from '../../shaders/common/grain'
|
|
5
|
+
import { vignette } from '../../shaders/common/grain'
|
|
6
|
+
import { luminance } from '../../shaders/common/blend'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Preset: combines grain + warm tone shift + vignette + slight fade.
|
|
10
|
+
* A convenient one-stop aged film look.
|
|
11
|
+
*/
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
/** Overall intensity: 0 = no effect, 1 = full aged look */
|
|
14
|
+
intensity?: number
|
|
15
|
+
/** Grain strength */
|
|
16
|
+
grainStrength?: number
|
|
17
|
+
/** Vignette darkness */
|
|
18
|
+
vignetteStrength?: number
|
|
19
|
+
/** Warm shadow tint amount */
|
|
20
|
+
warmth?: number
|
|
21
|
+
order?: number
|
|
22
|
+
}>(), { intensity: 1, grainStrength: 0.04, vignetteStrength: 0.5, warmth: 0.15, order: 0 })
|
|
23
|
+
|
|
24
|
+
const intensityNode = uniform(props.intensity)
|
|
25
|
+
const grainNode = uniform(props.grainStrength)
|
|
26
|
+
const vigNode = uniform(props.vignetteStrength)
|
|
27
|
+
const warmthNode = uniform(props.warmth)
|
|
28
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
29
|
+
watch(() => props.grainStrength, v => { grainNode.value = v })
|
|
30
|
+
watch(() => props.vignetteStrength, v => { vigNode.value = v })
|
|
31
|
+
watch(() => props.warmth, v => { warmthNode.value = v })
|
|
32
|
+
|
|
33
|
+
const pipeline = useShaderPipelineContext()
|
|
34
|
+
|
|
35
|
+
useShaderStage(
|
|
36
|
+
(prev) => {
|
|
37
|
+
const uv = pipeline.uvNode.value
|
|
38
|
+
const t = time
|
|
39
|
+
|
|
40
|
+
// Film grain
|
|
41
|
+
const seed = floor(t.mul(24))
|
|
42
|
+
const g = grain(uv, grainNode, seed)
|
|
43
|
+
|
|
44
|
+
// Warm shadows: boost R and reduce B in dark areas
|
|
45
|
+
const lum = luminance(prev.xyz)
|
|
46
|
+
const shadowWarm = float(1).sub(lum).mul(warmthNode)
|
|
47
|
+
const warm = prev.xyz.add(vec3(shadowWarm, shadowWarm.mul(0.3), shadowWarm.negate()))
|
|
48
|
+
|
|
49
|
+
// Vignette
|
|
50
|
+
const vig = vignette(uv, vigNode, float(0.4))
|
|
51
|
+
|
|
52
|
+
// Slight overall fade (film desaturation/lift)
|
|
53
|
+
const faded = warm.mul(float(0.92)).add(float(0.04))
|
|
54
|
+
|
|
55
|
+
const result = clamp(faded.mul(vig).add(g), 0, 1)
|
|
56
|
+
return vec4(result.mix(prev.xyz, float(1).sub(intensityNode)), prev.w)
|
|
57
|
+
},
|
|
58
|
+
props.order,
|
|
59
|
+
)
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec2, vec3, vec4, mix, sin, float, clamp } from 'three/tsl'
|
|
5
|
+
import { fbm2D } from '../../shaders/common/noise'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Aurora borealis — layered animated colour bands across the upper screen.
|
|
9
|
+
* Uses FBM noise to create organic ribbon-like curtains of light.
|
|
10
|
+
*/
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
/** Primary aurora colour */
|
|
13
|
+
colorA?: string
|
|
14
|
+
/** Secondary aurora colour */
|
|
15
|
+
colorB?: string
|
|
16
|
+
/** Vertical position of aurora band centre */
|
|
17
|
+
bandY?: number
|
|
18
|
+
/** Vertical band height */
|
|
19
|
+
bandHeight?: number
|
|
20
|
+
/** Animation speed */
|
|
21
|
+
speed?: number
|
|
22
|
+
/** Effect intensity */
|
|
23
|
+
intensity?: number
|
|
24
|
+
order?: number
|
|
25
|
+
}>(), {
|
|
26
|
+
colorA: '#00ff88',
|
|
27
|
+
colorB: '#8844ff',
|
|
28
|
+
bandY: 0.7,
|
|
29
|
+
bandHeight: 0.3,
|
|
30
|
+
speed: 0.3,
|
|
31
|
+
intensity: 0.7,
|
|
32
|
+
order: 0,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
function toVec3Node(hex: string) {
|
|
36
|
+
const c = new Color(hex)
|
|
37
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const colorANode = toVec3Node(props.colorA)
|
|
41
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
42
|
+
const bandYNode = uniform(props.bandY)
|
|
43
|
+
const bandHNode = uniform(props.bandHeight)
|
|
44
|
+
const speedNode = uniform(props.speed)
|
|
45
|
+
const intensityNode = uniform(props.intensity)
|
|
46
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
47
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
48
|
+
watch(() => props.bandY, v => { bandYNode.value = v })
|
|
49
|
+
watch(() => props.bandHeight, v => { bandHNode.value = v })
|
|
50
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
51
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
52
|
+
|
|
53
|
+
const pipeline = useShaderPipelineContext()
|
|
54
|
+
|
|
55
|
+
useShaderStage(
|
|
56
|
+
(prev) => {
|
|
57
|
+
const uv = pipeline.uvNode.value
|
|
58
|
+
const t = time.mul(speedNode)
|
|
59
|
+
|
|
60
|
+
// FBM-warped vertical position to create curtain ripple
|
|
61
|
+
const warpedX = uv.x.add(fbm2D(uv.mul(2).add(t)).mul(0.15))
|
|
62
|
+
// warpedX is a float (scalar) — must wrap in vec2 before passing to fbm2D
|
|
63
|
+
const n = fbm2D(vec2(warpedX, uv.y).mul(3).add(t.mul(0.5)))
|
|
64
|
+
|
|
65
|
+
// Vertical band mask — soft gaussian around bandY
|
|
66
|
+
const dy = uv.y.sub(bandYNode.add(n.mul(0.1)))
|
|
67
|
+
const bandMask = clamp(float(1).sub(dy.abs().div(bandHNode)), 0, 1)
|
|
68
|
+
bandMask.pow(2)
|
|
69
|
+
|
|
70
|
+
// Colour: mix A/B via second noise layer
|
|
71
|
+
const colourMix = fbm2D(uv.mul(1.5).add(t.mul(0.3).add(1.5))).mul(0.5).add(0.5)
|
|
72
|
+
const aurora = mix(colorANode, colorBNode, colourMix)
|
|
73
|
+
|
|
74
|
+
const contribution = aurora.mul(bandMask.pow(2)).mul(intensityNode)
|
|
75
|
+
return vec4(prev.xyz.add(contribution), prev.w)
|
|
76
|
+
},
|
|
77
|
+
props.order,
|
|
78
|
+
)
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
/** Bottom-left corner colour */
|
|
8
|
+
bottomLeft?: string
|
|
9
|
+
/** Bottom-right corner colour */
|
|
10
|
+
bottomRight?: string
|
|
11
|
+
/** Top-left corner colour */
|
|
12
|
+
topLeft?: string
|
|
13
|
+
/** Top-right corner colour */
|
|
14
|
+
topRight?: string
|
|
15
|
+
order?: number
|
|
16
|
+
}>(), { bottomLeft: '#000000', bottomRight: '#ff0000', topLeft: '#0000ff', topRight: '#ffffff', order: 0 })
|
|
17
|
+
|
|
18
|
+
function toVec3Node(hex: string) {
|
|
19
|
+
const c = new Color(hex)
|
|
20
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const blNode = toVec3Node(props.bottomLeft)
|
|
24
|
+
const brNode = toVec3Node(props.bottomRight)
|
|
25
|
+
const tlNode = toVec3Node(props.topLeft)
|
|
26
|
+
const trNode = toVec3Node(props.topRight)
|
|
27
|
+
watch(() => props.bottomLeft, v => { const c = new Color(v); blNode.value.set(c.r, c.g, c.b) })
|
|
28
|
+
watch(() => props.bottomRight, v => { const c = new Color(v); brNode.value.set(c.r, c.g, c.b) })
|
|
29
|
+
watch(() => props.topLeft, v => { const c = new Color(v); tlNode.value.set(c.r, c.g, c.b) })
|
|
30
|
+
watch(() => props.topRight, v => { const c = new Color(v); trNode.value.set(c.r, c.g, c.b) })
|
|
31
|
+
|
|
32
|
+
const pipeline = useShaderPipelineContext()
|
|
33
|
+
|
|
34
|
+
useShaderStage(
|
|
35
|
+
() => {
|
|
36
|
+
const uvCurrent = pipeline.uvNode.value
|
|
37
|
+
const bottom = mix(blNode, brNode, uvCurrent.x)
|
|
38
|
+
const top = mix(tlNode, trNode, uvCurrent.x)
|
|
39
|
+
const colour = mix(bottom, top, uvCurrent.y)
|
|
40
|
+
return vec4(colour, 1.0)
|
|
41
|
+
},
|
|
42
|
+
props.order,
|
|
43
|
+
)
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec4, mix, float } from 'three/tsl'
|
|
5
|
+
import { turbulence2D } from '../../shaders/common/noise'
|
|
6
|
+
|
|
7
|
+
/** Billow/turbulence noise — folded abs() of FBM, producing cloud-like forms. */
|
|
8
|
+
const props = withDefaults(defineProps<{
|
|
9
|
+
colorA?: string
|
|
10
|
+
colorB?: string
|
|
11
|
+
scale?: number
|
|
12
|
+
speed?: number
|
|
13
|
+
order?: number
|
|
14
|
+
}>(), { colorA: '#1a3a8a', colorB: '#ffffff', scale: 2, speed: 0.1, order: 0 })
|
|
15
|
+
|
|
16
|
+
function toVec3Node(hex: string) {
|
|
17
|
+
const c = new Color(hex)
|
|
18
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const colorANode = toVec3Node(props.colorA)
|
|
22
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
23
|
+
const scaleNode = uniform(props.scale)
|
|
24
|
+
const speedNode = uniform(props.speed)
|
|
25
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
26
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
27
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
28
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
29
|
+
|
|
30
|
+
const pipeline = useShaderPipelineContext()
|
|
31
|
+
|
|
32
|
+
useShaderStage(
|
|
33
|
+
() => {
|
|
34
|
+
const uv = pipeline.uvNode.value
|
|
35
|
+
const n = turbulence2D(uv.mul(scaleNode).add(time.mul(speedNode)))
|
|
36
|
+
return vec4(mix(colorANode, colorBNode, n), float(1))
|
|
37
|
+
},
|
|
38
|
+
props.order,
|
|
39
|
+
)
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, vec4 } from 'three/tsl'
|
|
4
|
+
import { brightness, contrast } from '../../shaders/common/blend'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
/** Additive brightness offset: 0 = unchanged, positive = brighter */
|
|
8
|
+
brightness?: number
|
|
9
|
+
/** Contrast multiplier: 1 = unchanged, >1 = more contrast */
|
|
10
|
+
contrast?: number
|
|
11
|
+
order?: number
|
|
12
|
+
}>(), { brightness: 0, contrast: 1, order: 0 })
|
|
13
|
+
|
|
14
|
+
const brightnessNode = uniform(props.brightness)
|
|
15
|
+
const contrastNode = uniform(props.contrast)
|
|
16
|
+
watch(() => props.brightness, v => { brightnessNode.value = v })
|
|
17
|
+
watch(() => props.contrast, v => { contrastNode.value = v })
|
|
18
|
+
|
|
19
|
+
useShaderStage(
|
|
20
|
+
(prev) => vec4(brightness(contrast(prev.xyz, contrastNode), brightnessNode), prev.w),
|
|
21
|
+
props.order,
|
|
22
|
+
)
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec4, mix, float } from 'three/tsl'
|
|
5
|
+
import { voronoi2D } from '../../shaders/common/noise'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
colorA?: string
|
|
9
|
+
colorB?: string
|
|
10
|
+
scale?: number
|
|
11
|
+
speed?: number
|
|
12
|
+
order?: number
|
|
13
|
+
}>(), { colorA: '#000033', colorB: '#ffffff', scale: 8, speed: 0.1, order: 0 })
|
|
14
|
+
|
|
15
|
+
function toVec3Node(hex: string) {
|
|
16
|
+
const c = new Color(hex)
|
|
17
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const colorANode = toVec3Node(props.colorA)
|
|
21
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
22
|
+
const scaleNode = uniform(props.scale)
|
|
23
|
+
const speedNode = uniform(props.speed)
|
|
24
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
25
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
26
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
27
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
28
|
+
|
|
29
|
+
const pipeline = useShaderPipelineContext()
|
|
30
|
+
|
|
31
|
+
useShaderStage(
|
|
32
|
+
() => {
|
|
33
|
+
const uv = pipeline.uvNode.value
|
|
34
|
+
const { distance } = voronoi2D(uv.add(time.mul(speedNode)), scaleNode)
|
|
35
|
+
return vec4(mix(colorANode, colorBNode, distance.min(float(1))), float(1))
|
|
36
|
+
},
|
|
37
|
+
props.order,
|
|
38
|
+
)
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, vec3, vec4, float } from 'three/tsl'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Arbitrary RGB channel mixing matrix.
|
|
7
|
+
* Each output channel is a weighted sum of all three input channels.
|
|
8
|
+
* Identity: rr=1 gg=1 bb=1, all cross-terms = 0.
|
|
9
|
+
*/
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
/** R channel: [R contribution, G contribution, B contribution] */
|
|
12
|
+
rRow?: [number, number, number]
|
|
13
|
+
/** G channel: [R contribution, G contribution, B contribution] */
|
|
14
|
+
gRow?: [number, number, number]
|
|
15
|
+
/** B channel: [R contribution, G contribution, B contribution] */
|
|
16
|
+
bRow?: [number, number, number]
|
|
17
|
+
order?: number
|
|
18
|
+
}>(), {
|
|
19
|
+
rRow: () => [1, 0, 0],
|
|
20
|
+
gRow: () => [0, 1, 0],
|
|
21
|
+
bRow: () => [0, 0, 1],
|
|
22
|
+
order: 0,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const rrNode = uniform(props.rRow[0]); const rgNode = uniform(props.rRow[1]); const rbNode = uniform(props.rRow[2])
|
|
26
|
+
const grNode = uniform(props.gRow[0]); const ggNode = uniform(props.gRow[1]); const gbNode = uniform(props.gRow[2])
|
|
27
|
+
const brNode = uniform(props.bRow[0]); const bgNode = uniform(props.bRow[1]); const bbNode = uniform(props.bRow[2])
|
|
28
|
+
|
|
29
|
+
watch(() => props.rRow, ([r, g, b]) => { rrNode.value = r; rgNode.value = g; rbNode.value = b })
|
|
30
|
+
watch(() => props.gRow, ([r, g, b]) => { grNode.value = r; ggNode.value = g; gbNode.value = b })
|
|
31
|
+
watch(() => props.bRow, ([r, g, b]) => { brNode.value = r; bgNode.value = g; bbNode.value = b })
|
|
32
|
+
|
|
33
|
+
useShaderStage(
|
|
34
|
+
(prev) => {
|
|
35
|
+
const r = prev.r.mul(rrNode).add(prev.g.mul(rgNode)).add(prev.b.mul(rbNode))
|
|
36
|
+
const g = prev.r.mul(grNode).add(prev.g.mul(ggNode)).add(prev.b.mul(gbNode))
|
|
37
|
+
const b = prev.r.mul(brNode).add(prev.g.mul(bgNode)).add(prev.b.mul(bbNode))
|
|
38
|
+
return vec4(vec3(r, g, b), prev.w)
|
|
39
|
+
},
|
|
40
|
+
props.order,
|
|
41
|
+
)
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, time, vec4, float } from 'three/tsl'
|
|
4
|
+
import { simplexNoise2D } from '../../shaders/common/noise'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Noise-distorted Chebyshev box shape — the genuary22 pattern.
|
|
8
|
+
* Formula: 1 - max(abs(uv.x * scaleX * n), abs(uv.y * scaleY * n2))
|
|
9
|
+
* where n, n2 are two simplex noise octaves at different frequencies.
|
|
10
|
+
* Produces a noise-eroded cross/box silhouette. Outputs a float mask for CosinePalette.
|
|
11
|
+
*/
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
/** Horizontal scale of the box */
|
|
14
|
+
scaleX?: number
|
|
15
|
+
/** Vertical scale of the box */
|
|
16
|
+
scaleY?: number
|
|
17
|
+
/** Noise frequency for the first octave (X distortion) */
|
|
18
|
+
noiseFreq1?: number
|
|
19
|
+
/** Noise frequency for the second octave (Y distortion) */
|
|
20
|
+
noiseFreq2?: number
|
|
21
|
+
/** Animation speed */
|
|
22
|
+
speed?: number
|
|
23
|
+
order?: number
|
|
24
|
+
}>(), { scaleX: 2, scaleY: 2, noiseFreq1: 1.5, noiseFreq2: 2.3, speed: 0.2, order: 0 })
|
|
25
|
+
|
|
26
|
+
const scaleXNode = uniform(props.scaleX)
|
|
27
|
+
const scaleYNode = uniform(props.scaleY)
|
|
28
|
+
const freq1Node = uniform(props.noiseFreq1)
|
|
29
|
+
const freq2Node = uniform(props.noiseFreq2)
|
|
30
|
+
const speedNode = uniform(props.speed)
|
|
31
|
+
watch(() => props.scaleX, v => { scaleXNode.value = v })
|
|
32
|
+
watch(() => props.scaleY, v => { scaleYNode.value = v })
|
|
33
|
+
watch(() => props.noiseFreq1, v => { freq1Node.value = v })
|
|
34
|
+
watch(() => props.noiseFreq2, v => { freq2Node.value = v })
|
|
35
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
36
|
+
|
|
37
|
+
const pipeline = useShaderPipelineContext()
|
|
38
|
+
|
|
39
|
+
useShaderStage(
|
|
40
|
+
() => {
|
|
41
|
+
const uvCurrent = pipeline.uvNode.value
|
|
42
|
+
const centered = uvCurrent.sub(0.5)
|
|
43
|
+
const t = time.mul(speedNode)
|
|
44
|
+
|
|
45
|
+
const n = simplexNoise2D(centered.mul(freq1Node).add(t)).mul(0.5).add(0.5)
|
|
46
|
+
const n2 = simplexNoise2D(centered.mul(freq2Node).sub(t.mul(0.7))).mul(0.5).add(0.5)
|
|
47
|
+
|
|
48
|
+
const box = float(1).sub(
|
|
49
|
+
centered.x.mul(scaleXNode).mul(n).abs().max(
|
|
50
|
+
centered.y.mul(scaleYNode).mul(n2).abs()
|
|
51
|
+
)
|
|
52
|
+
).max(float(0))
|
|
53
|
+
|
|
54
|
+
return vec4(box, box, box, float(1))
|
|
55
|
+
},
|
|
56
|
+
props.order,
|
|
57
|
+
)
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix } from 'three/tsl'
|
|
5
|
+
import { checker } from '../../shaders/common/shapes'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
/** First checker colour */
|
|
9
|
+
colorA?: string
|
|
10
|
+
/** Second checker colour */
|
|
11
|
+
colorB?: string
|
|
12
|
+
/** Number of checker tiles across each axis */
|
|
13
|
+
scale?: number
|
|
14
|
+
order?: number
|
|
15
|
+
}>(), { colorA: '#ffffff', colorB: '#000000', scale: 10, order: 0 })
|
|
16
|
+
|
|
17
|
+
function toVec3Node(hex: string) {
|
|
18
|
+
const c = new Color(hex)
|
|
19
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const colorANode = toVec3Node(props.colorA)
|
|
23
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
24
|
+
const scaleNode = uniform(props.scale)
|
|
25
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
26
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
27
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
28
|
+
|
|
29
|
+
const pipeline = useShaderPipelineContext()
|
|
30
|
+
|
|
31
|
+
useShaderStage(
|
|
32
|
+
() => {
|
|
33
|
+
const uvCurrent = pipeline.uvNode.value
|
|
34
|
+
const t = checker(uvCurrent, scaleNode)
|
|
35
|
+
return vec4(mix(colorANode, colorBNode, t), 1.0)
|
|
36
|
+
},
|
|
37
|
+
props.order,
|
|
38
|
+
)
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, time, vec3, vec4, sin, float } from 'three/tsl'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Chromatic aberration — RGB channel offset approximated without texture sampling.
|
|
7
|
+
* Shifts colour contribution along each channel using screen-position phase offsets.
|
|
8
|
+
*/
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
/** How far channels separate */
|
|
11
|
+
strength?: number
|
|
12
|
+
/** Boost aberration at screen edges */
|
|
13
|
+
edgeFalloff?: number
|
|
14
|
+
/** Animation speed for subtle drift */
|
|
15
|
+
speed?: number
|
|
16
|
+
order?: number
|
|
17
|
+
}>(), { strength: 0.008, edgeFalloff: 1.5, speed: 0.2, order: 0 })
|
|
18
|
+
|
|
19
|
+
const strengthNode = uniform(props.strength)
|
|
20
|
+
const edgeNode = uniform(props.edgeFalloff)
|
|
21
|
+
const speedNode = uniform(props.speed)
|
|
22
|
+
watch(() => props.strength, v => { strengthNode.value = v })
|
|
23
|
+
watch(() => props.edgeFalloff, v => { edgeNode.value = v })
|
|
24
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
25
|
+
|
|
26
|
+
const pipeline = useShaderPipelineContext()
|
|
27
|
+
|
|
28
|
+
useShaderStage(
|
|
29
|
+
(prev) => {
|
|
30
|
+
const uv = pipeline.uvNode.value
|
|
31
|
+
const t = time.mul(speedNode)
|
|
32
|
+
|
|
33
|
+
// Edge mask: stronger near screen corners
|
|
34
|
+
const fromCenter = uv.sub(0.5).length().mul(edgeNode)
|
|
35
|
+
|
|
36
|
+
// Phase offsets per channel — small sinusoidal drift over time
|
|
37
|
+
const drift = sin(t).mul(0.002)
|
|
38
|
+
const rShift = strengthNode.add(drift)
|
|
39
|
+
const bShift = strengthNode.negate().sub(drift)
|
|
40
|
+
|
|
41
|
+
// Approximate channel shift: modulate the colour channels by phase-offset luminance
|
|
42
|
+
const rContrib = prev.x.mul(float(1).add(rShift.mul(fromCenter)))
|
|
43
|
+
const gContrib = prev.y
|
|
44
|
+
const bContrib = prev.z.mul(float(1).add(bShift.mul(fromCenter)))
|
|
45
|
+
|
|
46
|
+
return vec4(vec3(rContrib, gContrib, bContrib), prev.w)
|
|
47
|
+
},
|
|
48
|
+
props.order,
|
|
49
|
+
)
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, time, vec3, vec4, sin, float } from 'three/tsl'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-channel animated sine waves along the Y axis — the CRT chromatic oscillation effect.
|
|
7
|
+
* Each RGB channel gets its own phase offset, producing subtle chromatic breathing.
|
|
8
|
+
* Pattern: R: sin(freq/res * uv.y + (-time*speed - phaseOffset))
|
|
9
|
+
*/
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
/** Wave amplitude (fraction of screen) */
|
|
12
|
+
amplitude?: number
|
|
13
|
+
/** Number of wave cycles across the screen height */
|
|
14
|
+
frequency?: number
|
|
15
|
+
/** Wave scroll speed */
|
|
16
|
+
speed?: number
|
|
17
|
+
/** Phase separation between RGB channels (radians) */
|
|
18
|
+
phaseOffset?: number
|
|
19
|
+
order?: number
|
|
20
|
+
}>(), { amplitude: 0.075, frequency: 20, speed: 2, phaseOffset: 0.4, order: 0 })
|
|
21
|
+
|
|
22
|
+
const ampNode = uniform(props.amplitude)
|
|
23
|
+
const freqNode = uniform(props.frequency)
|
|
24
|
+
const speedNode = uniform(props.speed)
|
|
25
|
+
const phaseNode = uniform(props.phaseOffset)
|
|
26
|
+
watch(() => props.amplitude, v => { ampNode.value = v })
|
|
27
|
+
watch(() => props.frequency, v => { freqNode.value = v })
|
|
28
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
29
|
+
watch(() => props.phaseOffset, v => { phaseNode.value = v })
|
|
30
|
+
|
|
31
|
+
const pipeline = useShaderPipelineContext()
|
|
32
|
+
|
|
33
|
+
useShaderStage(
|
|
34
|
+
(prev) => {
|
|
35
|
+
const uvCurrent = pipeline.uvNode.value
|
|
36
|
+
const t = time.mul(speedNode).negate()
|
|
37
|
+
|
|
38
|
+
const base = freqNode.mul(uvCurrent.y)
|
|
39
|
+
const r = sin(base.add(t.sub(phaseNode))).mul(ampNode).add(float(1).sub(ampNode))
|
|
40
|
+
const g = sin(base.add(t)).mul(ampNode).add(float(1).sub(ampNode))
|
|
41
|
+
const b = sin(base.add(t.add(phaseNode))).mul(ampNode).add(float(1).sub(ampNode))
|
|
42
|
+
|
|
43
|
+
return vec4(prev.xyz.mul(vec3(r, g, b)), prev.w)
|
|
44
|
+
},
|
|
45
|
+
props.order,
|
|
46
|
+
)
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template><!-- --></template>
|