kmcom-nuxt-layers 1.6.3 → 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 +17 -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
|
@@ -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>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix, smoothstep, float } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
colorA?: string
|
|
8
|
+
colorB?: string
|
|
9
|
+
radius?: number
|
|
10
|
+
softness?: number
|
|
11
|
+
order?: number
|
|
12
|
+
}>(), { colorA: '#000000', colorB: '#ffffff', radius: 0.35, softness: 0.01, order: 0 })
|
|
13
|
+
|
|
14
|
+
function toVec3Node(hex: string) {
|
|
15
|
+
const c = new Color(hex)
|
|
16
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const colorANode = toVec3Node(props.colorA)
|
|
20
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
21
|
+
const radiusNode = uniform(props.radius)
|
|
22
|
+
const softnessNode = uniform(props.softness)
|
|
23
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
24
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
25
|
+
watch(() => props.radius, v => { radiusNode.value = v })
|
|
26
|
+
watch(() => props.softness, v => { softnessNode.value = v })
|
|
27
|
+
|
|
28
|
+
const pipeline = useShaderPipelineContext()
|
|
29
|
+
|
|
30
|
+
useShaderStage(
|
|
31
|
+
() => {
|
|
32
|
+
const uv = pipeline.uvNode.value
|
|
33
|
+
const d = uv.sub(0.5).length()
|
|
34
|
+
const mask = smoothstep(radiusNode.add(softnessNode), radiusNode.sub(softnessNode), d)
|
|
35
|
+
return vec4(mix(colorANode, colorBNode, mask), float(1))
|
|
36
|
+
},
|
|
37
|
+
props.order,
|
|
38
|
+
)
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec4, mix, smoothstep, float } from 'three/tsl'
|
|
5
|
+
import { fbm2D } from '../../shaders/common/noise'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
/** Sky colour */
|
|
9
|
+
colorSky?: string
|
|
10
|
+
/** Cloud colour */
|
|
11
|
+
colorCloud?: string
|
|
12
|
+
/** FBM noise input scale */
|
|
13
|
+
scale?: number
|
|
14
|
+
/** Cloud coverage: lower = fewer clouds, higher = overcast */
|
|
15
|
+
coverage?: number
|
|
16
|
+
/** Cloud softness */
|
|
17
|
+
softness?: number
|
|
18
|
+
/** Wind speed */
|
|
19
|
+
speed?: number
|
|
20
|
+
order?: number
|
|
21
|
+
}>(), { colorSky: '#5588cc', colorCloud: '#ffffff', scale: 2, coverage: 0.5, softness: 0.15, speed: 0.05, order: 0 })
|
|
22
|
+
|
|
23
|
+
function toVec3Node(hex: string) {
|
|
24
|
+
const c = new Color(hex)
|
|
25
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const colorSkyNode = toVec3Node(props.colorSky)
|
|
29
|
+
const colorCloudNode = toVec3Node(props.colorCloud)
|
|
30
|
+
const scaleNode = uniform(props.scale)
|
|
31
|
+
const coverageNode = uniform(props.coverage)
|
|
32
|
+
const softnessNode = uniform(props.softness)
|
|
33
|
+
const speedNode = uniform(props.speed)
|
|
34
|
+
watch(() => props.colorSky, v => { const c = new Color(v); colorSkyNode.value.set(c.r, c.g, c.b) })
|
|
35
|
+
watch(() => props.colorCloud, v => { const c = new Color(v); colorCloudNode.value.set(c.r, c.g, c.b) })
|
|
36
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
37
|
+
watch(() => props.coverage, v => { coverageNode.value = v })
|
|
38
|
+
watch(() => props.softness, v => { softnessNode.value = v })
|
|
39
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
40
|
+
|
|
41
|
+
const pipeline = useShaderPipelineContext()
|
|
42
|
+
|
|
43
|
+
useShaderStage(
|
|
44
|
+
() => {
|
|
45
|
+
const uv = pipeline.uvNode.value
|
|
46
|
+
const wind = time.mul(speedNode)
|
|
47
|
+
const n = fbm2D(uv.mul(scaleNode).add(wind)).mul(0.5).add(0.5)
|
|
48
|
+
// Remap to cloud mask using coverage + softness
|
|
49
|
+
const cloud = smoothstep(
|
|
50
|
+
coverageNode.sub(softnessNode),
|
|
51
|
+
coverageNode.add(softnessNode),
|
|
52
|
+
n,
|
|
53
|
+
)
|
|
54
|
+
return vec4(mix(colorSkyNode, colorCloudNode, cloud), float(1))
|
|
55
|
+
},
|
|
56
|
+
props.order,
|
|
57
|
+
)
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color } from 'three'
|
|
4
|
+
import { mix, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
import { blendColorBurn } 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) => vec4(mix(prev.xyz, blendColorBurn(prev.xyz, colorNode), opacityNode), prev.w),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color } from 'three'
|
|
4
|
+
import { mix, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
import { blendColorDodge } 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) => vec4(mix(prev.xyz, blendColorDodge(prev.xyz, colorNode), opacityNode), prev.w),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix, smoothstep, float } from 'three/tsl'
|
|
5
|
+
import { luminance } from '../../shaders/common/blend'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Maps input luminance to a three-stop colour ramp: shadow → midtone → highlight.
|
|
9
|
+
* For a simple two-stop ramp use DuoTone instead.
|
|
10
|
+
*/
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
shadowColor?: string
|
|
13
|
+
midtoneColor?: string
|
|
14
|
+
highlightColor?: string
|
|
15
|
+
/** Luminance threshold between shadow and midtone */
|
|
16
|
+
shadowPoint?: number
|
|
17
|
+
/** Luminance threshold between midtone and highlight */
|
|
18
|
+
highlightPoint?: number
|
|
19
|
+
order?: number
|
|
20
|
+
}>(), {
|
|
21
|
+
shadowColor: '#000033',
|
|
22
|
+
midtoneColor: '#660066',
|
|
23
|
+
highlightColor: '#ffcc88',
|
|
24
|
+
shadowPoint: 0.33,
|
|
25
|
+
highlightPoint: 0.67,
|
|
26
|
+
order: 0,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
function toVec3Node(hex: string) {
|
|
30
|
+
const c = new Color(hex)
|
|
31
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const shadowNode = toVec3Node(props.shadowColor)
|
|
35
|
+
const midNode = toVec3Node(props.midtoneColor)
|
|
36
|
+
const highlightNode = toVec3Node(props.highlightColor)
|
|
37
|
+
const shadowPtNode = uniform(props.shadowPoint)
|
|
38
|
+
const highlightPtNode = uniform(props.highlightPoint)
|
|
39
|
+
watch(() => props.shadowColor, v => { const c = new Color(v); shadowNode.value.set(c.r, c.g, c.b) })
|
|
40
|
+
watch(() => props.midtoneColor, v => { const c = new Color(v); midNode.value.set(c.r, c.g, c.b) })
|
|
41
|
+
watch(() => props.highlightColor, v => { const c = new Color(v); highlightNode.value.set(c.r, c.g, c.b) })
|
|
42
|
+
watch(() => props.shadowPoint, v => { shadowPtNode.value = v })
|
|
43
|
+
watch(() => props.highlightPoint, v => { highlightPtNode.value = v })
|
|
44
|
+
|
|
45
|
+
useShaderStage(
|
|
46
|
+
(prev) => {
|
|
47
|
+
const lum = luminance(prev.xyz)
|
|
48
|
+
const t1 = smoothstep(float(0), shadowPtNode, lum)
|
|
49
|
+
const t2 = smoothstep(shadowPtNode, highlightPtNode, lum)
|
|
50
|
+
const colour = mix(mix(shadowNode, midNode, t1), mix(midNode, highlightNode, t2), t2)
|
|
51
|
+
return vec4(colour, prev.w)
|
|
52
|
+
},
|
|
53
|
+
props.order,
|
|
54
|
+
)
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, time, vec2, vec4, float, cos, sin } from 'three/tsl'
|
|
4
|
+
import { complexDiv, complexLog } from '../../shaders/common/complex'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Complex number plane visualisation — the imaginary2 pattern.
|
|
8
|
+
* Places two poles p, q derived from poleAngle/poleDistance,
|
|
9
|
+
* computes the Möbius transformation (z-p)/(z-q), takes the complex log,
|
|
10
|
+
* and extracts the imaginary component (winding angle) as a scalar float.
|
|
11
|
+
* Outputs grayscale for CosinePalette (scalarSource: 'prev').
|
|
12
|
+
*/
|
|
13
|
+
const props = withDefaults(defineProps<{
|
|
14
|
+
/** Angle between the two poles in radians */
|
|
15
|
+
poleAngle?: number
|
|
16
|
+
/** Distance of poles from origin */
|
|
17
|
+
poleDistance?: number
|
|
18
|
+
/** Weight of the imaginary (winding) component */
|
|
19
|
+
imaginaryWeight?: number
|
|
20
|
+
/** Weight of the radial (exp) component */
|
|
21
|
+
radialWeight?: number
|
|
22
|
+
/** Animation speed */
|
|
23
|
+
speed?: number
|
|
24
|
+
order?: number
|
|
25
|
+
}>(), {
|
|
26
|
+
poleAngle: Math.PI / 3,
|
|
27
|
+
poleDistance: 0.4,
|
|
28
|
+
imaginaryWeight: 0.6,
|
|
29
|
+
radialWeight: 0.4,
|
|
30
|
+
speed: 0.05,
|
|
31
|
+
order: 0,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const poleAngleNode = uniform(props.poleAngle)
|
|
35
|
+
const poleDistNode = uniform(props.poleDistance)
|
|
36
|
+
const imagWeightNode = uniform(props.imaginaryWeight)
|
|
37
|
+
const radWeightNode = uniform(props.radialWeight)
|
|
38
|
+
const speedNode = uniform(props.speed)
|
|
39
|
+
watch(() => props.poleAngle, v => { poleAngleNode.value = v })
|
|
40
|
+
watch(() => props.poleDistance, v => { poleDistNode.value = v })
|
|
41
|
+
watch(() => props.imaginaryWeight, v => { imagWeightNode.value = v })
|
|
42
|
+
watch(() => props.radialWeight, v => { radWeightNode.value = v })
|
|
43
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
44
|
+
|
|
45
|
+
const pipeline = useShaderPipelineContext()
|
|
46
|
+
|
|
47
|
+
useShaderStage(
|
|
48
|
+
() => {
|
|
49
|
+
const uvCurrent = pipeline.uvNode.value
|
|
50
|
+
const t = time.mul(speedNode)
|
|
51
|
+
|
|
52
|
+
// UV centred
|
|
53
|
+
const z = uvCurrent.sub(0.5)
|
|
54
|
+
|
|
55
|
+
// Pole position (complex number)
|
|
56
|
+
const angle = poleAngleNode.add(t)
|
|
57
|
+
const p = vec2(cos(angle).mul(poleDistNode), sin(angle).mul(poleDistNode))
|
|
58
|
+
const q = vec2(p.x, p.y.negate()) // conjugate
|
|
59
|
+
|
|
60
|
+
// Möbius: (z - p) / (z - q)
|
|
61
|
+
const w = complexDiv(z.sub(p), z.sub(q))
|
|
62
|
+
|
|
63
|
+
// Complex log: (ln|w|, arg(w))
|
|
64
|
+
const logW = complexLog(w)
|
|
65
|
+
|
|
66
|
+
// Combine imaginary (winding angle) and radial (exp magnitude) components
|
|
67
|
+
const imagPart = logW.y.mul(imagWeightNode)
|
|
68
|
+
const radPart = z.x.exp().div(z.y.abs().add(float(0.001))).mul(radWeightNode)
|
|
69
|
+
const scalar = imagPart.add(radPart).mul(0.5).add(0.5)
|
|
70
|
+
|
|
71
|
+
return vec4(scalar, scalar, scalar, float(1))
|
|
72
|
+
},
|
|
73
|
+
props.order,
|
|
74
|
+
)
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix, float } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
colorA?: string
|
|
8
|
+
colorB?: string
|
|
9
|
+
/** Rotation offset in degrees */
|
|
10
|
+
rotation?: number
|
|
11
|
+
order?: number
|
|
12
|
+
}>(), { colorA: '#000000', colorB: '#ffffff', rotation: 0, order: 0 })
|
|
13
|
+
|
|
14
|
+
function toVec3Node(hex: string) {
|
|
15
|
+
const c = new Color(hex)
|
|
16
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const colorANode = toVec3Node(props.colorA)
|
|
20
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
21
|
+
const rotationNode = uniform(props.rotation * Math.PI / 180)
|
|
22
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
23
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
24
|
+
watch(() => props.rotation, v => { rotationNode.value = v * Math.PI / 180 })
|
|
25
|
+
|
|
26
|
+
const pipeline = useShaderPipelineContext()
|
|
27
|
+
|
|
28
|
+
useShaderStage(
|
|
29
|
+
() => {
|
|
30
|
+
const uv = pipeline.uvNode.value
|
|
31
|
+
const centered = uv.sub(0.5)
|
|
32
|
+
const angle = centered.y.atan(centered.x).add(rotationNode)
|
|
33
|
+
const t = angle.div(Math.PI * 2).fract()
|
|
34
|
+
return vec4(mix(colorANode, colorBNode, t), float(1))
|
|
35
|
+
},
|
|
36
|
+
props.order,
|
|
37
|
+
)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Vector3 } from 'three'
|
|
4
|
+
import { time, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
import { cosinePalette } from '../../shaders/common/palette'
|
|
6
|
+
import { luminance } from '../../shaders/common/blend'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* IQ's cosine palette — the backbone of phobon/aurora-style shaders.
|
|
10
|
+
* Formula: a + b * cos(2π * (c*t + d))
|
|
11
|
+
* Scalar input t comes from uvNode via scalarSource.
|
|
12
|
+
*/
|
|
13
|
+
const props = withDefaults(defineProps<{
|
|
14
|
+
/** Colour offset (brightness centre) */
|
|
15
|
+
a?: [number, number, number]
|
|
16
|
+
/** Colour amplitude (contrast) */
|
|
17
|
+
b?: [number, number, number]
|
|
18
|
+
/** Frequency per channel */
|
|
19
|
+
c?: [number, number, number]
|
|
20
|
+
/** Phase offset per channel (colour shift) */
|
|
21
|
+
d?: [number, number, number]
|
|
22
|
+
/** How fast the palette cycles over time */
|
|
23
|
+
timeScale?: number
|
|
24
|
+
/**
|
|
25
|
+
* What value to feed as the scalar input `t`.
|
|
26
|
+
* `'prev'` reads luminance of the previous pipeline stage — use after RingField,
|
|
27
|
+
* ChebyshevNoiseField, or any block that outputs a grayscale scalar as its colour.
|
|
28
|
+
*/
|
|
29
|
+
scalarSource?: 'uv.y' | 'uv.x' | 'length' | 'time' | 'prev'
|
|
30
|
+
order?: number
|
|
31
|
+
}>(), {
|
|
32
|
+
a: () => [0.5, 0.5, 0.5],
|
|
33
|
+
b: () => [0.5, 0.5, 0.5],
|
|
34
|
+
c: () => [1.0, 1.0, 1.0],
|
|
35
|
+
d: () => [0.0, 0.33, 0.67],
|
|
36
|
+
timeScale: 0.1,
|
|
37
|
+
scalarSource: 'uv.y',
|
|
38
|
+
order: 0,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const aNode = uniform(new Vector3(...props.a))
|
|
42
|
+
const bNode = uniform(new Vector3(...props.b))
|
|
43
|
+
const cNode = uniform(new Vector3(...props.c))
|
|
44
|
+
const dNode = uniform(new Vector3(...props.d))
|
|
45
|
+
const timeScaleNode = uniform(props.timeScale)
|
|
46
|
+
|
|
47
|
+
watch(() => props.a, v => { aNode.value.set(...v) })
|
|
48
|
+
watch(() => props.b, v => { bNode.value.set(...v) })
|
|
49
|
+
watch(() => props.c, v => { cNode.value.set(...v) })
|
|
50
|
+
watch(() => props.d, v => { dNode.value.set(...v) })
|
|
51
|
+
watch(() => props.timeScale, v => { timeScaleNode.value = v })
|
|
52
|
+
|
|
53
|
+
const { uvNode } = useShaderPipelineContext()
|
|
54
|
+
|
|
55
|
+
useShaderStage(
|
|
56
|
+
(prev) => {
|
|
57
|
+
let scalar
|
|
58
|
+
if (props.scalarSource === 'uv.x') scalar = uvNode.value.x
|
|
59
|
+
else if (props.scalarSource === 'length') scalar = uvNode.value.sub(0.5).length()
|
|
60
|
+
else if (props.scalarSource === 'time') scalar = time
|
|
61
|
+
else if (props.scalarSource === 'prev') scalar = luminance(prev.xyz)
|
|
62
|
+
else scalar = uvNode.value.y
|
|
63
|
+
|
|
64
|
+
const t = scalar.add(time.mul(timeScaleNode))
|
|
65
|
+
return vec4(cosinePalette(t, aNode, bNode, cNode, dNode), 1.0)
|
|
66
|
+
},
|
|
67
|
+
props.order,
|
|
68
|
+
)
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { vec4 } from 'three/tsl'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets alpha to max(r, g, b) — the ColorBends transparent mode pattern.
|
|
7
|
+
* Shaders that generate shape from colour intensity (bright = opaque, dark = transparent)
|
|
8
|
+
* should use this instead of a fixed alpha = 1.
|
|
9
|
+
*/
|
|
10
|
+
const { order = 0 } = defineProps<{ order?: number }>()
|
|
11
|
+
|
|
12
|
+
useShaderStage(
|
|
13
|
+
(prev) => vec4(prev.xyz, prev.x.max(prev.y).max(prev.z)),
|
|
14
|
+
order,
|
|
15
|
+
)
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix, smoothstep, float } from 'three/tsl'
|
|
5
|
+
import { sdBox2d } from '../../shaders/common/sdf'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
colorA?: string
|
|
9
|
+
colorB?: string
|
|
10
|
+
/** Length of each arm */
|
|
11
|
+
size?: number
|
|
12
|
+
/** Width of each arm */
|
|
13
|
+
thickness?: number
|
|
14
|
+
softness?: number
|
|
15
|
+
order?: number
|
|
16
|
+
}>(), { colorA: '#000000', colorB: '#ffffff', size: 0.4, thickness: 0.1, softness: 0.01, 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 colorANode = toVec3Node(props.colorA)
|
|
24
|
+
const colorBNode = toVec3Node(props.colorB)
|
|
25
|
+
const sizeNode = uniform(props.size)
|
|
26
|
+
const thickNode = uniform(props.thickness)
|
|
27
|
+
const softnessNode = uniform(props.softness)
|
|
28
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
29
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
30
|
+
watch(() => props.size, v => { sizeNode.value = v })
|
|
31
|
+
watch(() => props.thickness, v => { thickNode.value = v })
|
|
32
|
+
watch(() => props.softness, v => { softnessNode.value = v })
|
|
33
|
+
|
|
34
|
+
const pipeline = useShaderPipelineContext()
|
|
35
|
+
|
|
36
|
+
useShaderStage(
|
|
37
|
+
() => {
|
|
38
|
+
const uv = pipeline.uvNode.value
|
|
39
|
+
const p = uv.sub(0.5)
|
|
40
|
+
// Horizontal arm and vertical arm — union = min
|
|
41
|
+
const dH = sdBox2d(p, thickNode.div(2)) // approx — sdBox2d expects a scalar half-size
|
|
42
|
+
const dV = sdBox2d(p.yx, thickNode.div(2))
|
|
43
|
+
const d = dH.min(dV)
|
|
44
|
+
const mask = smoothstep(softnessNode, softnessNode.negate(), d)
|
|
45
|
+
return vec4(mix(colorANode, colorBNode, mask), float(1))
|
|
46
|
+
},
|
|
47
|
+
props.order,
|
|
48
|
+
)
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec3, vec4, mix } from 'three/tsl'
|
|
5
|
+
import { curlNoise3d } from '../../shaders/common/noise'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
/** Colour to mix in at curl peak */
|
|
9
|
+
color?: string
|
|
10
|
+
/** Blend opacity */
|
|
11
|
+
opacity?: number
|
|
12
|
+
/** Input scale for the curl noise */
|
|
13
|
+
scale?: number
|
|
14
|
+
/** Animation speed */
|
|
15
|
+
speed?: number
|
|
16
|
+
order?: number
|
|
17
|
+
}>(), { color: '#ffffff', opacity: 1, scale: 1.5, speed: 0.2, order: 0 })
|
|
18
|
+
|
|
19
|
+
function toVec3Node(hex: string) {
|
|
20
|
+
const c = new Color(hex)
|
|
21
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const colorNode = toVec3Node(props.color)
|
|
25
|
+
const opacityNode = uniform(props.opacity)
|
|
26
|
+
const scaleNode = uniform(props.scale)
|
|
27
|
+
const speedNode = uniform(props.speed)
|
|
28
|
+
watch(() => props.color, v => { const c = new Color(v); colorNode.value.set(c.r, c.g, c.b) })
|
|
29
|
+
watch(() => props.opacity, v => { opacityNode.value = v })
|
|
30
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
31
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
32
|
+
|
|
33
|
+
const pipeline = useShaderPipelineContext()
|
|
34
|
+
|
|
35
|
+
useShaderStage(
|
|
36
|
+
(prev) => {
|
|
37
|
+
const uvCurrent = pipeline.uvNode.value
|
|
38
|
+
const t = time.mul(speedNode)
|
|
39
|
+
const p = vec3(uvCurrent.mul(scaleNode), t)
|
|
40
|
+
const curl = curlNoise3d(p).x.mul(0.5).add(0.5)
|
|
41
|
+
const blended = mix(prev.xyz, colorNode, curl.mul(opacityNode))
|
|
42
|
+
return vec4(blended, prev.w)
|
|
43
|
+
},
|
|
44
|
+
props.order,
|
|
45
|
+
)
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color } from 'three'
|
|
4
|
+
import { mix, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
import { blendDarken } 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) => vec4(mix(prev.xyz, blendDarken(prev.xyz, colorNode), opacityNode), prev.w),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, mix, pow, abs, sign, float, vec4 } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sinusoidal interpolation between a light and dark colour over time.
|
|
8
|
+
* The phobon easing: t = (sign(cycle) * pow(abs(cycle), 0.6) + 1) / 2
|
|
9
|
+
* where cycle = sin(time * speed).
|
|
10
|
+
*/
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
/** Colour during the light phase */
|
|
13
|
+
colorLight?: string
|
|
14
|
+
/** Colour during the dark phase */
|
|
15
|
+
colorDark?: string
|
|
16
|
+
/** Cycle speed in radians per second */
|
|
17
|
+
speed?: number
|
|
18
|
+
/** Mix opacity over prev colour */
|
|
19
|
+
opacity?: number
|
|
20
|
+
order?: number
|
|
21
|
+
}>(), { colorLight: '#ffffff', colorDark: '#000033', speed: 0.3, opacity: 1, order: 0 })
|
|
22
|
+
|
|
23
|
+
function toVec3Node(hex: string) {
|
|
24
|
+
const c = new Color(hex)
|
|
25
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const lightNode = toVec3Node(props.colorLight)
|
|
29
|
+
const darkNode = toVec3Node(props.colorDark)
|
|
30
|
+
const speedNode = uniform(props.speed)
|
|
31
|
+
const opacityNode = uniform(props.opacity)
|
|
32
|
+
watch(() => props.colorLight, v => { const c = new Color(v); lightNode.value.set(c.r, c.g, c.b) })
|
|
33
|
+
watch(() => props.colorDark, v => { const c = new Color(v); darkNode.value.set(c.r, c.g, c.b) })
|
|
34
|
+
watch(() => props.speed, v => { speedNode.value = v })
|
|
35
|
+
watch(() => props.opacity, v => { opacityNode.value = v })
|
|
36
|
+
|
|
37
|
+
useShaderStage(
|
|
38
|
+
(prev) => {
|
|
39
|
+
const cycle = time.mul(speedNode).sin()
|
|
40
|
+
const t = sign(cycle).mul(pow(abs(cycle), float(0.6))).add(1).div(2)
|
|
41
|
+
const colour = mix(darkNode, lightNode, t)
|
|
42
|
+
return vec4(mix(prev.xyz, colour, opacityNode), prev.w)
|
|
43
|
+
},
|
|
44
|
+
props.order,
|
|
45
|
+
)
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, vec4 } from 'three/tsl'
|
|
4
|
+
import { desaturate } from '../../shaders/common/blend'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
/** 0 = original colour, 1 = full greyscale */
|
|
8
|
+
amount?: number
|
|
9
|
+
order?: number
|
|
10
|
+
}>(), { amount: 1, order: 0 })
|
|
11
|
+
|
|
12
|
+
const amountNode = uniform(props.amount)
|
|
13
|
+
watch(() => props.amount, v => { amountNode.value = v })
|
|
14
|
+
|
|
15
|
+
useShaderStage(
|
|
16
|
+
(prev) => vec4(desaturate(prev.xyz, amountNode), prev.w),
|
|
17
|
+
props.order,
|
|
18
|
+
)
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template><!-- --></template>
|