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
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck - Three.js WebGPU types + TresJS renderer factory
|
|
2
3
|
import { TresCanvas } from '@tresjs/core'
|
|
3
4
|
import type { ToneMapping } from 'three'
|
|
4
5
|
import {
|
|
5
6
|
ACESFilmicToneMapping,
|
|
6
7
|
CineonToneMapping,
|
|
8
|
+
Color,
|
|
7
9
|
LinearToneMapping,
|
|
8
10
|
ReinhardToneMapping,
|
|
9
11
|
SRGBColorSpace,
|
|
10
12
|
} from 'three'
|
|
13
|
+
import { WebGPURenderer } from 'three/webgpu'
|
|
11
14
|
|
|
12
15
|
const props = withDefaults(
|
|
13
16
|
defineProps<{
|
|
@@ -20,6 +23,8 @@ const props = withDefaults(
|
|
|
20
23
|
preserveDrawingBuffer?: boolean
|
|
21
24
|
powerPreference?: 'default' | 'high-performance' | 'low-power'
|
|
22
25
|
windowSize?: boolean
|
|
26
|
+
/** Use WebGPURenderer (required for node materials / TSL shaders) */
|
|
27
|
+
webgpu?: boolean
|
|
23
28
|
}>(),
|
|
24
29
|
{
|
|
25
30
|
clearColor: '#000000',
|
|
@@ -31,6 +36,7 @@ const props = withDefaults(
|
|
|
31
36
|
preserveDrawingBuffer: false,
|
|
32
37
|
powerPreference: 'high-performance',
|
|
33
38
|
windowSize: true,
|
|
39
|
+
webgpu: false,
|
|
34
40
|
}
|
|
35
41
|
)
|
|
36
42
|
|
|
@@ -65,6 +71,23 @@ const maxDpr = computed(() => {
|
|
|
65
71
|
return Math.min(window.devicePixelRatio, configDpr)
|
|
66
72
|
})
|
|
67
73
|
|
|
74
|
+
// WebGPU renderer factory — TresJS v5 calls renderer.init() automatically
|
|
75
|
+
// when the returned object has isRenderer === true (three/webgpu Renderer base class)
|
|
76
|
+
const webgpuRendererFactory = props.webgpu
|
|
77
|
+
? ({ canvas }: { canvas: any }) => {
|
|
78
|
+
const r = new WebGPURenderer({
|
|
79
|
+
canvas: unref(canvas),
|
|
80
|
+
antialias: props.antialias,
|
|
81
|
+
powerPreference: props.powerPreference,
|
|
82
|
+
})
|
|
83
|
+
r.setPixelRatio(Math.min(window.devicePixelRatio, maxDpr.value))
|
|
84
|
+
r.setClearColor(new Color(props.clearColor))
|
|
85
|
+
r.toneMapping = resolvedToneMapping.value
|
|
86
|
+
r.outputColorSpace = resolvedColorSpace.value
|
|
87
|
+
return r
|
|
88
|
+
}
|
|
89
|
+
: undefined
|
|
90
|
+
|
|
68
91
|
function onReady(context: any) {
|
|
69
92
|
emit('ready', context)
|
|
70
93
|
}
|
|
@@ -82,6 +105,7 @@ function onReady(context: any) {
|
|
|
82
105
|
:power-preference
|
|
83
106
|
:window-size
|
|
84
107
|
:dpr="maxDpr"
|
|
108
|
+
:renderer="webgpuRendererFactory"
|
|
85
109
|
class="shader-canvas"
|
|
86
110
|
@ready="onReady"
|
|
87
111
|
>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// @ts-nocheck - TSL types are complex
|
|
3
|
+
import { DoubleSide } from 'three'
|
|
4
|
+
import { MeshBasicNodeMaterial } from 'three/webgpu'
|
|
5
|
+
import { float, positionLocal, uv, vec3, vec4 } from 'three/tsl'
|
|
6
|
+
|
|
7
|
+
const { transparent = false } = defineProps<{
|
|
8
|
+
transparent?: boolean
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const pipeline = useShaderPipelineContext()
|
|
12
|
+
|
|
13
|
+
const material = new MeshBasicNodeMaterial()
|
|
14
|
+
material.side = DoubleSide
|
|
15
|
+
if (transparent) {
|
|
16
|
+
material.transparent = true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Starting node for the ray pipeline: centred screen coord → normalised ray direction.
|
|
20
|
+
// Ray transformer blocks (Phase 4) refine this — FisheyeRay, RayTiltBasis, etc.
|
|
21
|
+
const screenRayNode = vec3(uv().sub(0.5).mul(2.0), float(1.0)).normalize()
|
|
22
|
+
|
|
23
|
+
watch(
|
|
24
|
+
pipeline.version,
|
|
25
|
+
() => {
|
|
26
|
+
const uvStages = pipeline.stagesFor('uv')
|
|
27
|
+
const fragment = pipeline.stagesFor('fragment')
|
|
28
|
+
const vertex = pipeline.stagesFor('vertex')
|
|
29
|
+
const ray = pipeline.stagesFor('ray')
|
|
30
|
+
|
|
31
|
+
// UV chain — generators read pipeline.uvNode.value in their stage fn closures
|
|
32
|
+
pipeline.uvNode.value = uvStages.reduce((node, { fn }) => fn(node), uv())
|
|
33
|
+
|
|
34
|
+
// Ray chain — sky/tunnel generators read pipeline.rayNode.value in their closures
|
|
35
|
+
pipeline.rayNode.value = ray.reduce((node, { fn }) => fn(node), screenRayNode)
|
|
36
|
+
|
|
37
|
+
// Fragment chain
|
|
38
|
+
material.colorNode = fragment.reduce((node, { fn }) => fn(node), vec4(0, 0, 0, 1))
|
|
39
|
+
material.needsUpdate = true
|
|
40
|
+
|
|
41
|
+
if (vertex.length > 0) {
|
|
42
|
+
material.positionNode = vertex.reduce((node, { fn }) => fn(node), positionLocal)
|
|
43
|
+
material.needsUpdate = true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{ immediate: true },
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
onUnmounted(() => {
|
|
50
|
+
try { material.dispose() } catch { /* renderer already torn down */ }
|
|
51
|
+
})
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<template>
|
|
55
|
+
<primitive :object="material" attach="material" />
|
|
56
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Provides a ShaderPipeline context to its slot via the standard Vue DOM renderer.
|
|
4
|
+
* Block components (which render <template />) must be placed inside this component,
|
|
5
|
+
* NOT inside TresCanvas — TresJS's custom renderer crashes on null VNodes.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* <ShaderPipelineContext>
|
|
9
|
+
* <div class="hidden"><PipelineBlock ... /></div> ← blocks in DOM
|
|
10
|
+
* <ShaderCanvas><TresMesh><ShaderPipeline /></TresMesh></ShaderCanvas>
|
|
11
|
+
* </ShaderPipelineContext>
|
|
12
|
+
*/
|
|
13
|
+
useShaderPipeline()
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<slot />
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Color, Vector3 } from 'three'
|
|
2
|
+
import { uniform } from 'three/tsl'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a TSL vec3 uniform that tracks a CSS custom property (colour).
|
|
6
|
+
* Syncs on mount and whenever :root's class or style changes (theme switches).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const primaryNode = useCSSColourUniform('--ui-color-primary-500')
|
|
10
|
+
* // Use primaryNode directly inside useShaderStage closures.
|
|
11
|
+
*/
|
|
12
|
+
export function useCSSColourUniform(varName: string) {
|
|
13
|
+
// @ts-ignore — TSL uniform types
|
|
14
|
+
const node = uniform(new Vector3(0, 0, 0))
|
|
15
|
+
const c = new Color()
|
|
16
|
+
|
|
17
|
+
function sync() {
|
|
18
|
+
const raw = getComputedStyle(document.documentElement)
|
|
19
|
+
.getPropertyValue(varName)
|
|
20
|
+
.trim()
|
|
21
|
+
if (!raw) return
|
|
22
|
+
try {
|
|
23
|
+
c.set(raw)
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
node.value.set(c.r, c.g, c.b)
|
|
26
|
+
}
|
|
27
|
+
catch { /* unsupported format — leave previous value */ }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onMounted(() => {
|
|
31
|
+
sync()
|
|
32
|
+
const observer = new MutationObserver(sync)
|
|
33
|
+
observer.observe(document.documentElement, {
|
|
34
|
+
attributes: true,
|
|
35
|
+
attributeFilter: ['style', 'class'],
|
|
36
|
+
})
|
|
37
|
+
onUnmounted(() => observer.disconnect())
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return node
|
|
41
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { uniform } from 'three/tsl'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a TSL float uniform that tracks a numeric CSS custom property.
|
|
5
|
+
* Syncs on mount and whenever :root's class or style changes (theme switches).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const spacingNode = useCSSFloatUniform('--spacing-scale')
|
|
9
|
+
*/
|
|
10
|
+
export function useCSSFloatUniform(varName: string) {
|
|
11
|
+
// @ts-ignore — TSL uniform types
|
|
12
|
+
const node = uniform(0)
|
|
13
|
+
|
|
14
|
+
function sync() {
|
|
15
|
+
const raw = getComputedStyle(document.documentElement)
|
|
16
|
+
.getPropertyValue(varName)
|
|
17
|
+
.trim()
|
|
18
|
+
const val = parseFloat(raw)
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
if (!isNaN(val)) node.value = val
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onMounted(() => {
|
|
24
|
+
sync()
|
|
25
|
+
const observer = new MutationObserver(sync)
|
|
26
|
+
observer.observe(document.documentElement, {
|
|
27
|
+
attributes: true,
|
|
28
|
+
attributeFilter: ['style', 'class'],
|
|
29
|
+
})
|
|
30
|
+
onUnmounted(() => observer.disconnect())
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return node
|
|
34
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useShaderPerf — lightweight performance monitor for shader pipelines.
|
|
3
|
+
*
|
|
4
|
+
* Tracks frame time, frame rate, and stage count for a named pipeline.
|
|
5
|
+
* Designed to be used in development to identify bottlenecks.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const perf = useShaderPerf('myScene')
|
|
9
|
+
* // call perf.tick() each frame (e.g. from useRenderLoop or requestAnimationFrame)
|
|
10
|
+
* // read perf.fps, perf.frameMs, perf.stageCount
|
|
11
|
+
*/
|
|
12
|
+
export function useShaderPerf(label = 'pipeline') {
|
|
13
|
+
const fps = ref(0)
|
|
14
|
+
const frameMs = ref(0)
|
|
15
|
+
const stageCount = ref(0)
|
|
16
|
+
|
|
17
|
+
let lastTime = performance.now()
|
|
18
|
+
let frames = 0
|
|
19
|
+
let accumMs = 0
|
|
20
|
+
let rafId: number | null = null
|
|
21
|
+
let running = false
|
|
22
|
+
|
|
23
|
+
const pipeline = useShaderPipelineContext()
|
|
24
|
+
|
|
25
|
+
function tick() {
|
|
26
|
+
if (!running) return
|
|
27
|
+
const now = performance.now()
|
|
28
|
+
const delta = now - lastTime
|
|
29
|
+
lastTime = now
|
|
30
|
+
|
|
31
|
+
frames++
|
|
32
|
+
accumMs += delta
|
|
33
|
+
|
|
34
|
+
if (accumMs >= 500) {
|
|
35
|
+
fps.value = Math.round((frames / accumMs) * 1000)
|
|
36
|
+
frameMs.value = Math.round(accumMs / frames * 10) / 10
|
|
37
|
+
frames = 0
|
|
38
|
+
accumMs = 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
stageCount.value = pipeline
|
|
42
|
+
? (pipeline.stagesFor('uv').length + pipeline.stagesFor('fragment').length)
|
|
43
|
+
: 0
|
|
44
|
+
rafId = requestAnimationFrame(tick)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function start() {
|
|
48
|
+
if (running) return
|
|
49
|
+
running = true
|
|
50
|
+
lastTime = performance.now()
|
|
51
|
+
rafId = requestAnimationFrame(tick)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function stop() {
|
|
55
|
+
running = false
|
|
56
|
+
if (rafId !== null) {
|
|
57
|
+
cancelAnimationFrame(rafId)
|
|
58
|
+
rafId = null
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onMounted(start)
|
|
63
|
+
onUnmounted(stop)
|
|
64
|
+
|
|
65
|
+
if (import.meta.dev) {
|
|
66
|
+
watch([fps, frameMs, stageCount], ([f, ms, s]) => {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.debug(`[ShaderPerf:${label}] ${f}fps ${ms}ms/frame ${s} stages`)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { fps, frameMs, stageCount, start, stop }
|
|
73
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { InjectionKey, Ref, ShallowRef } from 'vue'
|
|
2
|
+
import type { TSLNode } from '../types'
|
|
3
|
+
|
|
4
|
+
export type ShaderStage = 'fragment' | 'vertex' | 'ray' | 'uv'
|
|
5
|
+
|
|
6
|
+
export interface ShaderStageEntry {
|
|
7
|
+
fn: (input: TSLNode) => TSLNode
|
|
8
|
+
order: number
|
|
9
|
+
stage: ShaderStage
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ShaderPipelineContext {
|
|
13
|
+
register: (fn: ShaderStageEntry['fn'], order?: number, stage?: ShaderStage) => void
|
|
14
|
+
unregister: (fn: ShaderStageEntry['fn']) => void
|
|
15
|
+
stagesFor: (stage: ShaderStage) => ShaderStageEntry[]
|
|
16
|
+
/** Increments on topology changes (add/remove) to trigger material recompilation */
|
|
17
|
+
version: Ref<number>
|
|
18
|
+
/** Current UV node — starts as uv(), updated by UV transformer blocks before generators run.
|
|
19
|
+
* Generators read this via useShaderPipelineContext().uvNode to get the transformed UV. */
|
|
20
|
+
uvNode: ShallowRef<TSLNode | null>
|
|
21
|
+
/** Built ray direction node — populated by ShaderPipeline after ray transformers reduce.
|
|
22
|
+
* Terminal generators (SkyAtmosphere, RaymarchTunnel, etc.) read this in Phase 4. */
|
|
23
|
+
rayNode: ShallowRef<TSLNode | null>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const SHADER_PIPELINE_KEY: InjectionKey<ShaderPipelineContext> = Symbol('shader-pipeline')
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a shader pipeline context and provides it to child components.
|
|
30
|
+
* Call this inside <ShaderPipeline> — it provides the context that useShaderStage() injects.
|
|
31
|
+
*/
|
|
32
|
+
export function useShaderPipeline(): ShaderPipelineContext {
|
|
33
|
+
const entries: ShaderStageEntry[] = []
|
|
34
|
+
const version = ref(0)
|
|
35
|
+
const uvNode = shallowRef<TSLNode | null>(null)
|
|
36
|
+
const rayNode = shallowRef<TSLNode | null>(null)
|
|
37
|
+
|
|
38
|
+
function register(fn: ShaderStageEntry['fn'], order = 0, stage: ShaderStage = 'fragment') {
|
|
39
|
+
entries.push({ fn, order, stage })
|
|
40
|
+
version.value++
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function unregister(fn: ShaderStageEntry['fn']) {
|
|
44
|
+
const i = entries.findIndex(e => e.fn === fn)
|
|
45
|
+
if (i !== -1) {
|
|
46
|
+
entries.splice(i, 1)
|
|
47
|
+
version.value++
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function stagesFor(stage: ShaderStage): ShaderStageEntry[] {
|
|
52
|
+
return entries.filter(e => e.stage === stage).sort((a, b) => a.order - b.order)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const context: ShaderPipelineContext = { register, unregister, stagesFor, version, uvNode, rayNode }
|
|
56
|
+
provide(SHADER_PIPELINE_KEY, context)
|
|
57
|
+
return context
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Registers a stage function into the nearest parent ShaderPipeline.
|
|
62
|
+
* The function receives the current pipeline node and returns the transformed node.
|
|
63
|
+
*
|
|
64
|
+
* @param stageFn - Transform function: (input: TSLNode) => TSLNode
|
|
65
|
+
* @param order - Execution order within the stage (lower runs first)
|
|
66
|
+
* @param stage - 'fragment' (default), 'vertex', or 'ray'
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // Inside a block component's <script setup>:
|
|
70
|
+
* useShaderStage((_prev) => vec4(colorNode, 1.0), props.order ?? 0)
|
|
71
|
+
*
|
|
72
|
+
* // Vertex deformation:
|
|
73
|
+
* useShaderStage((pos) => pos.add(normalNode.mul(offset)), 0, 'vertex')
|
|
74
|
+
*/
|
|
75
|
+
export function useShaderStage(
|
|
76
|
+
stageFn: ShaderStageEntry['fn'],
|
|
77
|
+
order = 0,
|
|
78
|
+
stage: ShaderStage = 'fragment',
|
|
79
|
+
): void {
|
|
80
|
+
const pipeline = inject(SHADER_PIPELINE_KEY)
|
|
81
|
+
if (!pipeline) {
|
|
82
|
+
throw new Error('useShaderStage() must be called inside a <ShaderPipeline> component')
|
|
83
|
+
}
|
|
84
|
+
onMounted(() => pipeline.register(stageFn, order, stage))
|
|
85
|
+
onUnmounted(() => pipeline.unregister(stageFn))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Injects the pipeline context from a parent ShaderPipeline.
|
|
90
|
+
* Use this in advanced block components that need direct context access
|
|
91
|
+
* (e.g. terminal ray generators that read pipeline.rayNode).
|
|
92
|
+
*/
|
|
93
|
+
export function useShaderPipelineContext(): ShaderPipelineContext {
|
|
94
|
+
const pipeline = inject(SHADER_PIPELINE_KEY)
|
|
95
|
+
if (!pipeline) {
|
|
96
|
+
throw new Error('useShaderPipelineContext() must be called inside a <ShaderPipeline> component')
|
|
97
|
+
}
|
|
98
|
+
return pipeline
|
|
99
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Vector2 } from 'three'
|
|
2
|
+
import { float, time, uniform, vec3 } from 'three/tsl'
|
|
3
|
+
|
|
4
|
+
export interface SunDirectionOptions {
|
|
5
|
+
/** Animation speed (radians/second). Only used when useMouseY is false. */
|
|
6
|
+
speed?: number
|
|
7
|
+
/** Drive elevation from mouse Y instead of time. */
|
|
8
|
+
useMouseY?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Returns a live TSL vec3 node representing a normalised sun direction.
|
|
13
|
+
*
|
|
14
|
+
* Animated mode (default): elevation oscillates via sin(time * speed).
|
|
15
|
+
* Mouse mode: elevation tracks mouse Y position.
|
|
16
|
+
*
|
|
17
|
+
* The guide formula: rotX(vec3(0,0,1), angle * 5 + 0.5)
|
|
18
|
+
* → vec3(0, -sin(a), cos(a)) where a = angle * 5 + 0.5
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const sunDir = useSunDirectionUniform({ speed: 0.05 })
|
|
22
|
+
* // Pass sunDir directly to SkyAtmosphere or any shader that needs sun position.
|
|
23
|
+
*/
|
|
24
|
+
export function useSunDirectionUniform(options: SunDirectionOptions = {}) {
|
|
25
|
+
const { speed = 0.05, useMouseY = false } = options
|
|
26
|
+
|
|
27
|
+
if (useMouseY) {
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
const mouse = uniform(new Vector2(0.5, 0.5))
|
|
30
|
+
|
|
31
|
+
onMounted(() => {
|
|
32
|
+
const onMove = (e: MouseEvent) => {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
mouse.value.set(e.clientX / window.innerWidth, e.clientY / window.innerHeight)
|
|
35
|
+
}
|
|
36
|
+
window.addEventListener('mousemove', onMove)
|
|
37
|
+
onUnmounted(() => window.removeEventListener('mousemove', onMove))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// angle = -(mouseY - 0.5) — top of screen = sun high, bottom = sun low
|
|
41
|
+
const angle = mouse.y.oneMinus().sub(0.5)
|
|
42
|
+
const a = angle.mul(5).add(0.5)
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
return vec3(float(0), a.sin().negate(), a.cos())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Animated mode — pure TSL expression, no JS uniform needed
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
const speedNode = uniform(speed)
|
|
50
|
+
const angle = time.mul(speedNode).sin().div(8).sub(0.2)
|
|
51
|
+
const a = angle.mul(5).add(0.5)
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
return vec3(float(0), a.sin().negate(), a.cos())
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// @ts-nocheck - TSL types are complex and not fully exported from three/tsl
|
|
2
|
+
/**
|
|
3
|
+
* Complex number arithmetic in TSL
|
|
4
|
+
* Both functions operate on vec2 (x = real, y = imaginary).
|
|
5
|
+
*/
|
|
6
|
+
import { vec2, dot } from 'three/tsl'
|
|
7
|
+
import type { TSLNode } from '../types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Complex division: a / b
|
|
11
|
+
* (a.x*b.x + a.y*b.y) / |b|² , (a.y*b.x - a.x*b.y) / |b|²
|
|
12
|
+
* Used in imaginary2 for the Möbius transformation (z-p)/(z-q).
|
|
13
|
+
*/
|
|
14
|
+
export function complexDiv(a: TSLNode, b: TSLNode): TSLNode {
|
|
15
|
+
const denom = dot(b, b)
|
|
16
|
+
return vec2(
|
|
17
|
+
a.x.mul(b.x).add(a.y.mul(b.y)).div(denom),
|
|
18
|
+
a.y.mul(b.x).sub(a.x.mul(b.y)).div(denom),
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Complex natural logarithm: ln(z)
|
|
24
|
+
* Produces (ln|z|, arg(z)) — magnitude and winding angle.
|
|
25
|
+
* Used in imaginary2 to extract the winding angle between two poles.
|
|
26
|
+
*/
|
|
27
|
+
export function complexLog(z: TSLNode): TSLNode {
|
|
28
|
+
return vec2(z.length().log(), z.y.atan(z.x))
|
|
29
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
abs,
|
|
8
|
-
|
|
8
|
+
atan,
|
|
9
9
|
cos,
|
|
10
10
|
float,
|
|
11
11
|
floor,
|
|
@@ -281,7 +281,7 @@ export function star(
|
|
|
281
281
|
const outer = typeof outerRadius === 'number' ? float(outerRadius) : outerRadius
|
|
282
282
|
|
|
283
283
|
const p = uv.sub(c)
|
|
284
|
-
const angle =
|
|
284
|
+
const angle = atan(p.y, p.x)
|
|
285
285
|
const dist = length(p)
|
|
286
286
|
|
|
287
287
|
const segmentAngle = float((Math.PI * 2) / points)
|
|
@@ -310,7 +310,7 @@ export function radialLines(
|
|
|
310
310
|
const t = typeof thickness === 'number' ? float(thickness) : thickness
|
|
311
311
|
|
|
312
312
|
const p = uv.sub(c)
|
|
313
|
-
const angle =
|
|
313
|
+
const angle = atan(p.y, p.x).add(Math.PI)
|
|
314
314
|
const segment = float((Math.PI * 2) / numLines)
|
|
315
315
|
|
|
316
316
|
const foldedAngle = fract(angle.div(segment))
|
|
@@ -353,7 +353,7 @@ export function polygon(
|
|
|
353
353
|
const rot = typeof rotation === 'number' ? float(rotation) : rotation
|
|
354
354
|
|
|
355
355
|
const p = uv.sub(c)
|
|
356
|
-
const angle =
|
|
356
|
+
const angle = atan(p.y, p.x).add(rot)
|
|
357
357
|
const dist = length(p)
|
|
358
358
|
|
|
359
359
|
const segmentAngle = float((Math.PI * 2) / sides)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Modular TSL UV Manipulation Utilities
|
|
4
4
|
* Provides composable UV transformation functions
|
|
5
5
|
*/
|
|
6
|
-
import { abs,
|
|
6
|
+
import { abs, atan, cos, float, floor, fract, length, pow, sin, smoothstep, vec2 } from 'three/tsl'
|
|
7
7
|
import type { TSLNode } from '../types'
|
|
8
8
|
|
|
9
9
|
// ============================================
|
|
@@ -96,7 +96,7 @@ export function toPolar(uv: TSLNode, center: TSLNode | [number, number] = [0.5,
|
|
|
96
96
|
const centered = uv.sub(c)
|
|
97
97
|
|
|
98
98
|
const radius = length(centered)
|
|
99
|
-
const angle =
|
|
99
|
+
const angle = atan(centered.y, centered.x)
|
|
100
100
|
.add(Math.PI)
|
|
101
101
|
.div(Math.PI * 2)
|
|
102
102
|
|
|
@@ -126,7 +126,7 @@ export const radialLines = Fn(
|
|
|
126
126
|
([uv, center, count, width]: [TSLNode, TSLNode, TSLNode, TSLNode]) => {
|
|
127
127
|
const centered = uv.sub(center)
|
|
128
128
|
const angle = centered.y
|
|
129
|
-
.
|
|
129
|
+
.atan(centered.x)
|
|
130
130
|
.div(Math.PI * 2)
|
|
131
131
|
.add(0.5)
|
|
132
132
|
const segment = fract(angle.mul(count))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { atan, cos, float, Fn, fract, length, sin, vec2 } from 'three/tsl'
|
|
2
2
|
import type { TSLNode } from '../../types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -34,7 +34,7 @@ export const distortUV = Fn(([uv, noiseValue, strength]: [TSLNode, TSLNode, TSLN
|
|
|
34
34
|
*/
|
|
35
35
|
export const polarUV = Fn(([uv, center]: [TSLNode, TSLNode]) => {
|
|
36
36
|
const centered = uv.sub(center)
|
|
37
|
-
const angle =
|
|
37
|
+
const angle = atan(centered.y, centered.x)
|
|
38
38
|
.div(Math.PI * 2)
|
|
39
39
|
.add(0.5)
|
|
40
40
|
const radius = length(centered)
|
|
@@ -94,7 +94,7 @@ export const mirrorUV = Fn(([uv]: [TSLNode]) => {
|
|
|
94
94
|
*/
|
|
95
95
|
export const kaleidoscopeUV = Fn(([uv, center, segments]: [TSLNode, TSLNode, TSLNode]) => {
|
|
96
96
|
const centered = uv.sub(center)
|
|
97
|
-
const angle =
|
|
97
|
+
const angle = atan(centered.y, centered.x)
|
|
98
98
|
const radius = length(centered)
|
|
99
99
|
|
|
100
100
|
const segmentAngle = float(Math.PI * 2).div(segments)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kmcom-nuxt-layers",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.6",
|
|
5
5
|
"description": "Composable Nuxt 4 layers for building scalable Vue applications",
|
|
6
6
|
"files": [
|
|
7
7
|
"layers/*/nuxt.config.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"nuxt": "^4.4.2",
|
|
39
39
|
"nuxt-studio": "^1.4.0",
|
|
40
40
|
"pinia": "^3.0.4",
|
|
41
|
-
"tailwindcss": "^4.2.
|
|
41
|
+
"tailwindcss": "^4.2.4",
|
|
42
42
|
"three": "^0.183.2",
|
|
43
43
|
"v-gsap-nuxt": ">=1.0.0",
|
|
44
44
|
"zod": "^4.3.6"
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@eslint/markdown": "^7.5.1",
|
|
99
99
|
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
|
100
100
|
"@iconify-json/lucide": "^1.2.102",
|
|
101
|
+
"@netlify/nuxt": "0.2.41",
|
|
101
102
|
"@nuxt/eslint": "^1.15.2",
|
|
102
103
|
"@nuxt/fonts": "^0.14.0",
|
|
103
104
|
"@nuxt/image": "^2.0.0",
|
|
@@ -124,7 +125,7 @@
|
|
|
124
125
|
"eslint-plugin-unicorn": "^64.0.0",
|
|
125
126
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
126
127
|
"eslint-plugin-vue": "^10.8.0",
|
|
127
|
-
"npm-check-updates": "^
|
|
128
|
+
"npm-check-updates": "^21.0.3",
|
|
128
129
|
"nuxt": "latest",
|
|
129
130
|
"pinia": "^3.0.4",
|
|
130
131
|
"playwright": "^1.58.2",
|
|
@@ -142,11 +143,11 @@
|
|
|
142
143
|
"stylelint-config-tailwindcss": "^1.0.1",
|
|
143
144
|
"stylelint-no-unsupported-browser-features": "^8.1.1",
|
|
144
145
|
"stylelint-prettier": "^5.0.3",
|
|
145
|
-
"tailwindcss": "^4.2.
|
|
146
|
-
"turbo": "^2.
|
|
146
|
+
"tailwindcss": "^4.2.4",
|
|
147
|
+
"turbo": "^2.9.6",
|
|
147
148
|
"typescript": "^6.0.2",
|
|
148
|
-
"vite-plugin-checker": "^0.
|
|
149
|
-
"vitest": "^4.
|
|
149
|
+
"vite-plugin-checker": "^0.13.0",
|
|
150
|
+
"vitest": "^4.1.5",
|
|
150
151
|
"vue": "latest",
|
|
151
152
|
"vue-tsc": "^3.2.6",
|
|
152
153
|
"zod": "^4.3.6",
|
|
@@ -159,6 +160,7 @@
|
|
|
159
160
|
"last 2 Edge major versions"
|
|
160
161
|
],
|
|
161
162
|
"dependencies": {
|
|
163
|
+
"node-gyp": "^12.3.0",
|
|
162
164
|
"skills": "^1.5.0"
|
|
163
165
|
},
|
|
164
166
|
"engines": {
|