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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector2, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec2, vec3, vec4, smoothstep, sin, cos, float } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Radial god rays — procedural volumetric light shafts emanating from a source point.
|
|
8
|
+
* Uses angular spokes in UV space to approximate crepuscular rays.
|
|
9
|
+
*/
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
/** Light source UV position */
|
|
12
|
+
position?: [number, number]
|
|
13
|
+
/** Ray colour */
|
|
14
|
+
color?: string
|
|
15
|
+
/** Ray brightness */
|
|
16
|
+
intensity?: number
|
|
17
|
+
/** Number of ray spokes */
|
|
18
|
+
rayCount?: number
|
|
19
|
+
/** Radial decay rate */
|
|
20
|
+
decay?: number
|
|
21
|
+
order?: number
|
|
22
|
+
}>(), { position: () => [0.5, 0.9], color: '#fff9e0', intensity: 0.35, rayCount: 12, decay: 2.0, order: 0 })
|
|
23
|
+
|
|
24
|
+
function toVec3Node(hex: string) {
|
|
25
|
+
const c = new Color(hex)
|
|
26
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const posNode = uniform(new Vector2(...props.position))
|
|
30
|
+
const colorNode = toVec3Node(props.color)
|
|
31
|
+
const intensityNode = uniform(props.intensity)
|
|
32
|
+
const rayCountNode = uniform(props.rayCount)
|
|
33
|
+
const decayNode = uniform(props.decay)
|
|
34
|
+
watch(() => props.position, ([x, y]) => { posNode.value.set(x, y) })
|
|
35
|
+
watch(() => props.color, v => { const c = new Color(v); colorNode.value.set(c.r, c.g, c.b) })
|
|
36
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
37
|
+
watch(() => props.rayCount, v => { rayCountNode.value = v })
|
|
38
|
+
watch(() => props.decay, v => { decayNode.value = v })
|
|
39
|
+
|
|
40
|
+
const pipeline = useShaderPipelineContext()
|
|
41
|
+
|
|
42
|
+
useShaderStage(
|
|
43
|
+
(prev) => {
|
|
44
|
+
const uv = pipeline.uvNode.value
|
|
45
|
+
const src = vec2(posNode.x, posNode.y)
|
|
46
|
+
const dir = uv.sub(src)
|
|
47
|
+
const dist = dir.length()
|
|
48
|
+
|
|
49
|
+
// Angular spokes: cos(angle * rayCount) drives streak pattern
|
|
50
|
+
const angle = dir.y.atan(dir.x)
|
|
51
|
+
const spoke = cos(angle.mul(rayCountNode)).mul(0.5).add(0.5)
|
|
52
|
+
spoke.pow(3)
|
|
53
|
+
|
|
54
|
+
// Radial falloff from source
|
|
55
|
+
const radialFade = smoothstep(decayNode, float(0), dist)
|
|
56
|
+
|
|
57
|
+
const rays = spoke.pow(3).mul(radialFade).mul(intensityNode)
|
|
58
|
+
return vec4(prev.xyz.add(colorNode.mul(rays)), prev.w)
|
|
59
|
+
},
|
|
60
|
+
props.order,
|
|
61
|
+
)
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, time, vec3, vec4, mix, float } from 'three/tsl'
|
|
5
|
+
import { gradientNoise3D } 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: '#000000', colorB: '#ffffff', scale: 3, speed: 0.2, 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 t = time.mul(speedNode)
|
|
35
|
+
const n = gradientNoise3D(vec3(uv.mul(scaleNode), t)).mul(0.5).add(0.5)
|
|
36
|
+
return vec4(mix(colorANode, colorBNode, n), float(1))
|
|
37
|
+
},
|
|
38
|
+
props.order,
|
|
39
|
+
)
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { clamp, dot, float, floor, fract, mix, pow, sin, step, time, uniform, vec2, vec4 } from 'three/tsl'
|
|
4
|
+
import { blendOverlay, blendScreen, blendSoftLight } from '../../shaders/common/blend'
|
|
5
|
+
|
|
6
|
+
type GrainBlendMode = 'add' | 'sub' | 'screen' | 'overlay' | 'soft-light'
|
|
7
|
+
type GrainStyle = 'smooth' | 'dotted' | 'coarse'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
intensity?: number
|
|
11
|
+
opacity?: number
|
|
12
|
+
/** UV scale — higher = finer grain, lower = bigger dots */
|
|
13
|
+
size?: number
|
|
14
|
+
/** smooth = continuous noise, dotted = binary on/off specks, coarse = concentrated bright grit */
|
|
15
|
+
style?: GrainStyle
|
|
16
|
+
animated?: boolean
|
|
17
|
+
fps?: number
|
|
18
|
+
blendMode?: GrainBlendMode
|
|
19
|
+
order?: number
|
|
20
|
+
}>(), {
|
|
21
|
+
intensity: 0.08,
|
|
22
|
+
opacity: 1.0,
|
|
23
|
+
size: 1.0,
|
|
24
|
+
style: 'smooth',
|
|
25
|
+
animated: true,
|
|
26
|
+
fps: 24,
|
|
27
|
+
blendMode: 'add',
|
|
28
|
+
order: 0,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const intensityNode = uniform(props.intensity)
|
|
32
|
+
const opacityNode = uniform(props.opacity)
|
|
33
|
+
const sizeNode = uniform(props.size)
|
|
34
|
+
const fpsNode = uniform(props.fps)
|
|
35
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
36
|
+
watch(() => props.opacity, v => { opacityNode.value = v })
|
|
37
|
+
watch(() => props.size, v => { sizeNode.value = v })
|
|
38
|
+
watch(() => props.fps, v => { fpsNode.value = v })
|
|
39
|
+
|
|
40
|
+
const { uvNode } = useShaderPipelineContext()
|
|
41
|
+
|
|
42
|
+
useShaderStage(
|
|
43
|
+
(prev) => {
|
|
44
|
+
const uvScaled = uvNode.value.mul(sizeNode)
|
|
45
|
+
const seed = props.animated ? floor(time.mul(fpsNode)) : float(0)
|
|
46
|
+
|
|
47
|
+
// Raw [0,1] hash noise
|
|
48
|
+
const raw = fract(sin(dot(uvScaled.add(seed), vec2(12.9898, 78.233))).mul(43758.5453))
|
|
49
|
+
|
|
50
|
+
// Shape the noise based on style
|
|
51
|
+
const shaped = props.style === 'dotted'
|
|
52
|
+
? step(float(0.65), raw) // binary: sparse bright specks
|
|
53
|
+
: props.style === 'coarse'
|
|
54
|
+
? pow(raw, float(4.0)).mul(2.5) // concentrated bright grit, rest dark
|
|
55
|
+
: raw // smooth: continuous noise
|
|
56
|
+
|
|
57
|
+
const mixFactor = intensityNode.mul(opacityNode)
|
|
58
|
+
let blended
|
|
59
|
+
switch (props.blendMode) {
|
|
60
|
+
case 'sub':
|
|
61
|
+
blended = prev.xyz.sub(shaped.mul(intensityNode).mul(opacityNode))
|
|
62
|
+
break
|
|
63
|
+
case 'screen':
|
|
64
|
+
blended = mix(prev.xyz, blendScreen(prev.xyz, shaped), mixFactor)
|
|
65
|
+
break
|
|
66
|
+
case 'overlay':
|
|
67
|
+
blended = mix(prev.xyz, blendOverlay(prev.xyz, shaped), mixFactor)
|
|
68
|
+
break
|
|
69
|
+
case 'soft-light':
|
|
70
|
+
blended = mix(prev.xyz, blendSoftLight(prev.xyz, shaped), mixFactor)
|
|
71
|
+
break
|
|
72
|
+
default: // 'add'
|
|
73
|
+
blended = prev.xyz.add(shaped.sub(0.5).mul(intensityNode).mul(opacityNode))
|
|
74
|
+
}
|
|
75
|
+
return clamp(vec4(blended, prev.w), 0, 1)
|
|
76
|
+
},
|
|
77
|
+
props.order,
|
|
78
|
+
)
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec4, mix, float } from 'three/tsl'
|
|
5
|
+
import { grid } from '../../shaders/common/shapes'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
/** Background colour */
|
|
9
|
+
colorA?: string
|
|
10
|
+
/** Grid line colour */
|
|
11
|
+
colorB?: string
|
|
12
|
+
cellSize?: number
|
|
13
|
+
lineWidth?: number
|
|
14
|
+
order?: number
|
|
15
|
+
}>(), { colorA: '#000000', colorB: '#ffffff', cellSize: 0.1, lineWidth: 0.005, 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 cellNode = uniform(props.cellSize)
|
|
25
|
+
const lineNode = uniform(props.lineWidth)
|
|
26
|
+
watch(() => props.colorA, v => { const c = new Color(v); colorANode.value.set(c.r, c.g, c.b) })
|
|
27
|
+
watch(() => props.colorB, v => { const c = new Color(v); colorBNode.value.set(c.r, c.g, c.b) })
|
|
28
|
+
watch(() => props.cellSize, v => { cellNode.value = v })
|
|
29
|
+
watch(() => props.lineWidth, v => { lineNode.value = v })
|
|
30
|
+
|
|
31
|
+
const pipeline = useShaderPipelineContext()
|
|
32
|
+
|
|
33
|
+
useShaderStage(
|
|
34
|
+
() => {
|
|
35
|
+
const uv = pipeline.uvNode.value
|
|
36
|
+
const mask = grid(uv, cellNode, lineNode)
|
|
37
|
+
return vec4(mix(colorANode, colorBNode, mask), float(1))
|
|
38
|
+
},
|
|
39
|
+
props.order,
|
|
40
|
+
)
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
* Red/warm glow bleed around highlight areas — the analogue film halation effect.
|
|
9
|
+
* Bright areas bleed a coloured halo into their surroundings.
|
|
10
|
+
*/
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
/** Glow colour */
|
|
13
|
+
color?: string
|
|
14
|
+
/** Luminance threshold above which glow starts */
|
|
15
|
+
threshold?: number
|
|
16
|
+
/** Glow intensity */
|
|
17
|
+
intensity?: number
|
|
18
|
+
order?: number
|
|
19
|
+
}>(), { color: '#ff2200', threshold: 0.7, intensity: 0.4, order: 0 })
|
|
20
|
+
|
|
21
|
+
function toVec3Node(hex: string) {
|
|
22
|
+
const c = new Color(hex)
|
|
23
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const colorNode = toVec3Node(props.color)
|
|
27
|
+
const thresholdNode = uniform(props.threshold)
|
|
28
|
+
const intensityNode = uniform(props.intensity)
|
|
29
|
+
watch(() => props.color, v => { const c = new Color(v); colorNode.value.set(c.r, c.g, c.b) })
|
|
30
|
+
watch(() => props.threshold, v => { thresholdNode.value = v })
|
|
31
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
32
|
+
|
|
33
|
+
useShaderStage(
|
|
34
|
+
(prev) => {
|
|
35
|
+
const lum = luminance(prev.xyz)
|
|
36
|
+
const glow = smoothstep(thresholdNode, float(1), lum).mul(intensityNode)
|
|
37
|
+
return vec4(prev.xyz.add(colorNode.mul(glow)), prev.w)
|
|
38
|
+
},
|
|
39
|
+
props.order,
|
|
40
|
+
)
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, vec4 } from 'three/tsl'
|
|
4
|
+
import { halftone } from '../../shaders/common/grain'
|
|
5
|
+
import { luminance } from '../../shaders/common/blend'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
/** Dot density — higher = finer dots */
|
|
9
|
+
scale?: number
|
|
10
|
+
/** Screen angle in radians */
|
|
11
|
+
angle?: number
|
|
12
|
+
order?: number
|
|
13
|
+
}>(), { scale: 50, angle: 0, order: 0 })
|
|
14
|
+
|
|
15
|
+
const scaleNode = uniform(props.scale)
|
|
16
|
+
const angleNode = uniform(props.angle)
|
|
17
|
+
watch(() => props.scale, v => { scaleNode.value = v })
|
|
18
|
+
watch(() => props.angle, v => { angleNode.value = v })
|
|
19
|
+
|
|
20
|
+
const pipeline = useShaderPipelineContext()
|
|
21
|
+
|
|
22
|
+
useShaderStage(
|
|
23
|
+
(prev) => {
|
|
24
|
+
const uvCurrent = pipeline.uvNode.value
|
|
25
|
+
const lum = luminance(prev.xyz)
|
|
26
|
+
const dot = halftone(uvCurrent, lum, scaleNode, angleNode)
|
|
27
|
+
return vec4(prev.xyz.mul(dot), prev.w)
|
|
28
|
+
},
|
|
29
|
+
props.order,
|
|
30
|
+
)
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<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 { blendHardLight } 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, blendHardLight(prev.xyz, colorNode), opacityNode), prev.w),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec3, vec4, smoothstep, float } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Edge haze — additive fog/bloom at screen boundaries.
|
|
8
|
+
* Useful for dreamlike or atmospheric borders.
|
|
9
|
+
*/
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
/** Haze tint colour */
|
|
12
|
+
color?: string
|
|
13
|
+
/** How far inward the haze reaches (0 = edge only, 1 = full screen) */
|
|
14
|
+
reach?: number
|
|
15
|
+
/** Haze brightness */
|
|
16
|
+
intensity?: number
|
|
17
|
+
order?: number
|
|
18
|
+
}>(), { color: '#ffffff', reach: 0.4, intensity: 0.3, order: 0 })
|
|
19
|
+
|
|
20
|
+
function toVec3Node(hex: string) {
|
|
21
|
+
const c = new Color(hex)
|
|
22
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const colorNode = toVec3Node(props.color)
|
|
26
|
+
const reachNode = uniform(props.reach)
|
|
27
|
+
const intensityNode = uniform(props.intensity)
|
|
28
|
+
watch(() => props.color, v => { const c = new Color(v); colorNode.value.set(c.r, c.g, c.b) })
|
|
29
|
+
watch(() => props.reach, v => { reachNode.value = v })
|
|
30
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
31
|
+
|
|
32
|
+
const pipeline = useShaderPipelineContext()
|
|
33
|
+
|
|
34
|
+
useShaderStage(
|
|
35
|
+
(prev) => {
|
|
36
|
+
const uv = pipeline.uvNode.value
|
|
37
|
+
|
|
38
|
+
// Horizontal edge proximity
|
|
39
|
+
const edgeX = smoothstep(reachNode, float(0), uv.x).add(smoothstep(float(1).sub(reachNode), float(1), uv.x))
|
|
40
|
+
// Vertical edge proximity
|
|
41
|
+
const edgeY = smoothstep(reachNode, float(0), uv.y).add(smoothstep(float(1).sub(reachNode), float(1), uv.y))
|
|
42
|
+
|
|
43
|
+
const edgeMask = edgeX.add(edgeY).clamp(0, 1).mul(intensityNode)
|
|
44
|
+
return vec4(prev.xyz.add(colorNode.mul(edgeMask)), prev.w)
|
|
45
|
+
},
|
|
46
|
+
props.order,
|
|
47
|
+
)
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,42 @@
|
|
|
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 { sdHexagon } from '../../shaders/common/sdf'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
colorA?: string
|
|
9
|
+
colorB?: string
|
|
10
|
+
radius?: number
|
|
11
|
+
softness?: number
|
|
12
|
+
order?: number
|
|
13
|
+
}>(), { colorA: '#000000', colorB: '#ffffff', radius: 0.35, softness: 0.01, 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 radiusNode = uniform(props.radius)
|
|
23
|
+
const softnessNode = uniform(props.softness)
|
|
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.radius, v => { radiusNode.value = v })
|
|
27
|
+
watch(() => props.softness, v => { softnessNode.value = v })
|
|
28
|
+
|
|
29
|
+
const pipeline = useShaderPipelineContext()
|
|
30
|
+
|
|
31
|
+
useShaderStage(
|
|
32
|
+
() => {
|
|
33
|
+
const uv = pipeline.uvNode.value
|
|
34
|
+
const d = sdHexagon(uv.sub(0.5), radiusNode)
|
|
35
|
+
const mask = smoothstep(softnessNode, softnessNode.negate(), d)
|
|
36
|
+
return vec4(mix(colorANode, colorBNode, mask), float(1))
|
|
37
|
+
},
|
|
38
|
+
props.order,
|
|
39
|
+
)
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { uniform, vec4 } from 'three/tsl'
|
|
4
|
+
import { hueShift } from '../../shaders/common/palette'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
/** Hue shift amount in [0, 1] (1 = full rotation) */
|
|
8
|
+
shift?: number
|
|
9
|
+
order?: number
|
|
10
|
+
}>(), { shift: 0, order: 0 })
|
|
11
|
+
|
|
12
|
+
const shiftNode = uniform(props.shift)
|
|
13
|
+
watch(() => props.shift, v => { shiftNode.value = v })
|
|
14
|
+
|
|
15
|
+
useShaderStage(
|
|
16
|
+
(prev) => vec4(hueShift(prev.xyz, shiftNode), prev.w),
|
|
17
|
+
props.order,
|
|
18
|
+
)
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { vec4 } from 'three/tsl'
|
|
4
|
+
import { invert } from '../../shaders/common/blend'
|
|
5
|
+
|
|
6
|
+
const { order = 0 } = defineProps<{ order?: number }>()
|
|
7
|
+
|
|
8
|
+
useShaderStage(
|
|
9
|
+
(prev) => vec4(invert(prev.xyz), prev.w),
|
|
10
|
+
order,
|
|
11
|
+
)
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector2, Vector3 } from 'three'
|
|
4
|
+
import { uniform, vec2, vec3, vec4, smoothstep, float } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Procedural lens flare — starburst + ghost circle artefacts along the flare axis.
|
|
8
|
+
*/
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
/** Light source position in UV space */
|
|
11
|
+
position?: [number, number]
|
|
12
|
+
/** Flare colour */
|
|
13
|
+
color?: string
|
|
14
|
+
/** Overall brightness */
|
|
15
|
+
intensity?: number
|
|
16
|
+
/** How many ghost circles along the axis */
|
|
17
|
+
ghostCount?: number
|
|
18
|
+
order?: number
|
|
19
|
+
}>(), { position: () => [0.7, 0.8], color: '#fffde0', intensity: 0.6, ghostCount: 4, order: 0 })
|
|
20
|
+
|
|
21
|
+
function toVec3Node(hex: string) {
|
|
22
|
+
const c = new Color(hex)
|
|
23
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const posNode = uniform(new Vector2(...props.position))
|
|
27
|
+
const colorNode = toVec3Node(props.color)
|
|
28
|
+
const intensityNode = uniform(props.intensity)
|
|
29
|
+
const ghostCountNode = uniform(props.ghostCount)
|
|
30
|
+
watch(() => props.position, ([x, y]) => { posNode.value.set(x, y) })
|
|
31
|
+
watch(() => props.color, v => { const c = new Color(v); colorNode.value.set(c.r, c.g, c.b) })
|
|
32
|
+
watch(() => props.intensity, v => { intensityNode.value = v })
|
|
33
|
+
watch(() => props.ghostCount, v => { ghostCountNode.value = v })
|
|
34
|
+
|
|
35
|
+
const pipeline = useShaderPipelineContext()
|
|
36
|
+
|
|
37
|
+
useShaderStage(
|
|
38
|
+
(prev) => {
|
|
39
|
+
const uv = pipeline.uvNode.value
|
|
40
|
+
const src = vec2(posNode.x, posNode.y)
|
|
41
|
+
|
|
42
|
+
// Starburst: bright disc at source
|
|
43
|
+
const distToSrc = uv.sub(src).length()
|
|
44
|
+
const burst = smoothstep(float(0.08), float(0), distToSrc).mul(intensityNode)
|
|
45
|
+
|
|
46
|
+
// Ghost circles: evenly spaced along the axis toward screen centre
|
|
47
|
+
const axis = vec2(0.5, 0.5).sub(src)
|
|
48
|
+
const ghost1 = uv.sub(src.add(axis.mul(0.4))).length()
|
|
49
|
+
const ghost2 = uv.sub(src.add(axis.mul(0.7))).length()
|
|
50
|
+
const ghost3 = uv.sub(src.add(axis.mul(1.1))).length()
|
|
51
|
+
const ghost4 = uv.sub(src.add(axis.mul(1.5))).length()
|
|
52
|
+
|
|
53
|
+
const g1 = smoothstep(float(0.05), float(0), ghost1).mul(0.4)
|
|
54
|
+
const g2 = smoothstep(float(0.03), float(0), ghost2).mul(0.3)
|
|
55
|
+
const g3 = smoothstep(float(0.04), float(0), ghost3).mul(0.2)
|
|
56
|
+
const g4 = smoothstep(float(0.06), float(0), ghost4).mul(0.15)
|
|
57
|
+
|
|
58
|
+
const ghosts = g1.add(g2).add(g3).add(g4).mul(intensityNode)
|
|
59
|
+
const flare = colorNode.mul(burst.add(ghosts))
|
|
60
|
+
|
|
61
|
+
return vec4(prev.xyz.add(flare), prev.w)
|
|
62
|
+
},
|
|
63
|
+
props.order,
|
|
64
|
+
)
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<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 { blendLighten } 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, blendLighten(prev.xyz, colorNode), opacityNode), prev.w),
|
|
20
|
+
props.order,
|
|
21
|
+
)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color } from 'three'
|
|
4
|
+
import { mix, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
colorA?: string
|
|
8
|
+
colorB?: string
|
|
9
|
+
axis?: 'x' | 'y'
|
|
10
|
+
order?: number
|
|
11
|
+
}>(), {
|
|
12
|
+
colorA: '#000000',
|
|
13
|
+
colorB: '#ffffff',
|
|
14
|
+
axis: 'y',
|
|
15
|
+
order: 0,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const colorAVal = new Color(props.colorA)
|
|
19
|
+
const colorBVal = new Color(props.colorB)
|
|
20
|
+
const colorANode = uniform(colorAVal)
|
|
21
|
+
const colorBNode = uniform(colorBVal)
|
|
22
|
+
watch(() => props.colorA, v => { colorANode.value.set(v) })
|
|
23
|
+
watch(() => props.colorB, v => { colorBNode.value.set(v) })
|
|
24
|
+
|
|
25
|
+
const { uvNode } = useShaderPipelineContext()
|
|
26
|
+
|
|
27
|
+
useShaderStage(
|
|
28
|
+
(_prev) => {
|
|
29
|
+
const t = props.axis === 'x' ? uvNode.value.x : uvNode.value.y
|
|
30
|
+
return vec4(mix(colorANode, colorBNode, t), 1.0)
|
|
31
|
+
},
|
|
32
|
+
props.order,
|
|
33
|
+
)
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template><!-- --></template>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { Color, Vector3 } from 'three'
|
|
4
|
+
import { float, mix, step, uniform, vec4 } from 'three/tsl'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
colorA?: string
|
|
8
|
+
colorB?: string
|
|
9
|
+
/** Optional third stop (defaults to colorB when omitted → 2-stop gradient) */
|
|
10
|
+
colorC?: string
|
|
11
|
+
/** Optional fourth stop (defaults to colorC when omitted → 3-stop gradient) */
|
|
12
|
+
colorD?: string
|
|
13
|
+
axis?: 'x' | 'y'
|
|
14
|
+
order?: number
|
|
15
|
+
}>(), {
|
|
16
|
+
colorA: '#000000',
|
|
17
|
+
colorB: '#ffffff',
|
|
18
|
+
colorC: undefined,
|
|
19
|
+
colorD: undefined,
|
|
20
|
+
axis: 'y',
|
|
21
|
+
order: 0,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
function toVec3U(hex: string) {
|
|
25
|
+
const c = new Color(hex)
|
|
26
|
+
return uniform(new Vector3(c.r, c.g, c.b))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cA = toVec3U(props.colorA)
|
|
30
|
+
const cB = toVec3U(props.colorB)
|
|
31
|
+
const cC = toVec3U(props.colorC ?? props.colorB)
|
|
32
|
+
const cD = toVec3U(props.colorD ?? props.colorC ?? props.colorB)
|
|
33
|
+
|
|
34
|
+
watch(() => props.colorA, v => { const c = new Color(v); cA.value.set(c.r, c.g, c.b) })
|
|
35
|
+
watch(() => props.colorB, v => { const c = new Color(v); cB.value.set(c.r, c.g, c.b) })
|
|
36
|
+
watch(() => props.colorC, v => { const c = new Color(v ?? props.colorB); cC.value.set(c.r, c.g, c.b) })
|
|
37
|
+
watch(() => props.colorD, v => { const c = new Color(v ?? props.colorC ?? props.colorB); cD.value.set(c.r, c.g, c.b) })
|
|
38
|
+
|
|
39
|
+
const pipeline = useShaderPipelineContext()
|
|
40
|
+
|
|
41
|
+
useShaderStage(
|
|
42
|
+
() => {
|
|
43
|
+
const t = props.axis === 'x' ? pipeline.uvNode.value.x : pipeline.uvNode.value.y
|
|
44
|
+
|
|
45
|
+
// Map t [0,1] to 3 equal segments, clamping so t=1 maps to lt=1 not lt=0
|
|
46
|
+
const tN = t.mul(float(3)).min(float(2.9999))
|
|
47
|
+
const seg = tN.floor()
|
|
48
|
+
const lt = tN.sub(seg) // local t within segment [0,1)
|
|
49
|
+
|
|
50
|
+
// All three segment interpolations share the same local t
|
|
51
|
+
const c01 = mix(cA, cB, lt)
|
|
52
|
+
const c12 = mix(cB, cC, lt)
|
|
53
|
+
const c23 = mix(cC, cD, lt)
|
|
54
|
+
|
|
55
|
+
// step(edge, x): 0 if x < edge, 1 if x >= edge
|
|
56
|
+
const inSeg1 = step(float(1), tN) // 1 when in segments 1 or 2
|
|
57
|
+
const inSeg2 = step(float(2), tN) // 1 when in segment 2
|
|
58
|
+
|
|
59
|
+
// Cascade: start with c01, replace with c12 at seg>=1, replace with c23 at seg>=2
|
|
60
|
+
const col = mix(mix(c01, c12, inSeg1), c23, inSeg2)
|
|
61
|
+
return vec4(col, float(1))
|
|
62
|
+
},
|
|
63
|
+
props.order,
|
|
64
|
+
)
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template><!-- --></template>
|