@wave3d/core 0.3.0 → 0.4.1
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/dist/config/model.d.ts +34 -0
- package/dist/config/model.js +33 -0
- package/dist/config/model.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +25 -0
- package/dist/renderer/WaveRenderer.js +190 -1
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/shaders.js +213 -6
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +367 -219
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js +2 -2
- package/dist/studio/randomize.d.ts +4 -1
- package/dist/studio/randomize.js +48 -1
- package/dist/studio/randomize.js.map +1 -1
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +40 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ensureStudioConfig } from "../config/model.js";
|
|
2
|
-
import { fragmentShader, lineFragmentShader, postFragmentShader, postVertexShader, vertexShader } from "./shaders.js";
|
|
2
|
+
import { ditherFragmentShader, fragmentShader, halftoneCmykFragmentShader, halftoneFragmentShader, heatmapFragmentShader, innerLightFragmentShader, lineFragmentShader, paperTextureFragmentShader, postFragmentShader, postVertexShader, vertexShader } from "./shaders.js";
|
|
3
3
|
import { WaveGeometry } from "./WaveGeometry.js";
|
|
4
4
|
import { InteractionController, SCENE_APPLIERS, WAVE_APPLIERS, anyPointerFxActive, interactionActive, wavePointerFxActive, waveRipplesActive } from "./interaction.js";
|
|
5
5
|
import { PALETTE_MAPS, buildBackgroundGradientCanvas, buildBackgroundImageCanvas, buildBackgroundMeshCanvas, buildPaletteTexture, canvasToTexture, configurePaletteTexture, drawBackgroundMediaFrame, loadPaletteImage, paletteMapCanvas, paletteSignature } from "./palette.js";
|
|
@@ -149,6 +149,12 @@ var WaveRenderer = class {
|
|
|
149
149
|
postPass;
|
|
150
150
|
/** Optional bloom pass — created lazily when bloomStrength first goes >0, removed at 0. */
|
|
151
151
|
bloomPass;
|
|
152
|
+
ditherPass;
|
|
153
|
+
innerLightPass;
|
|
154
|
+
halftonePass;
|
|
155
|
+
heatmapPass;
|
|
156
|
+
paperTexturePass;
|
|
157
|
+
halftoneCmykPass;
|
|
152
158
|
container;
|
|
153
159
|
respectReducedMotion;
|
|
154
160
|
skipIntroRamp;
|
|
@@ -372,6 +378,7 @@ var WaveRenderer = class {
|
|
|
372
378
|
uPointerPush: { value: 0 },
|
|
373
379
|
uPointerWake: { value: 0 },
|
|
374
380
|
uPointerVel: { value: new THREE.Vector2(0, 0) },
|
|
381
|
+
uShapeFlow: { value: 0 },
|
|
375
382
|
uPointerThin: { value: 0 },
|
|
376
383
|
uPointerHue: { value: 0 },
|
|
377
384
|
uPointerLighten: { value: 0 },
|
|
@@ -605,6 +612,7 @@ var WaveRenderer = class {
|
|
|
605
612
|
u.uOpacity.value = sc.opacity;
|
|
606
613
|
});
|
|
607
614
|
const sharedRadius = (this.config.interaction?.radius ?? .3) * 2;
|
|
615
|
+
const sharedFlow = this.config.interaction?.ribbonFlow ?? .8;
|
|
608
616
|
this.waves.forEach((wave, i) => {
|
|
609
617
|
const sc = this.config.waves[i] ?? this.config.waves[this.config.waves.length - 1];
|
|
610
618
|
if (!wavePointerFxActive(this.config, sc)) return;
|
|
@@ -618,6 +626,7 @@ var WaveRenderer = class {
|
|
|
618
626
|
u.uPointerHue.value = h?.hueShift ?? 0;
|
|
619
627
|
u.uPointerLighten.value = h?.lighten ?? 0;
|
|
620
628
|
u.uPointerRipple.value = sc.interaction?.press?.ripple ?? 0;
|
|
629
|
+
u.uShapeFlow.value = sharedFlow;
|
|
621
630
|
});
|
|
622
631
|
this.updatePaletteTextures();
|
|
623
632
|
this.syncVideoPlayback();
|
|
@@ -890,6 +899,12 @@ var WaveRenderer = class {
|
|
|
890
899
|
u.uGrainAmount.value = this.config.grain;
|
|
891
900
|
u.uBlurSamples.value = Math.round(this.config.blurSamples ?? 6);
|
|
892
901
|
this.applyBloom();
|
|
902
|
+
this.applyInnerLight();
|
|
903
|
+
this.applyHalftone();
|
|
904
|
+
this.applyHeatmap();
|
|
905
|
+
this.applyHalftoneCmyk();
|
|
906
|
+
this.applyPaperTexture();
|
|
907
|
+
this.applyDither();
|
|
893
908
|
}
|
|
894
909
|
/** Insert / tune / remove the bloom pass. strength 0 removes it from the composer entirely, so
|
|
895
910
|
* cost and pixels are identical to bloom-off; the pass (and its mip-chain render targets) is
|
|
@@ -912,6 +927,172 @@ var WaveRenderer = class {
|
|
|
912
927
|
this.bloomPass = void 0;
|
|
913
928
|
}
|
|
914
929
|
}
|
|
930
|
+
/** Insert / tune / remove the dithering pass — a self-contained "layered" post shader (an ordered
|
|
931
|
+
* Bayer dither, in the spirit of paper-design/shaders). Like bloom, dither 0 removes the pass
|
|
932
|
+
* entirely so cost and pixels match dither-off, and it's created lazily on first enable. It is
|
|
933
|
+
* appended AFTER OutputPass so it runs last and quantizes display-space colour (tone-mapped +
|
|
934
|
+
* sRGB) — dithering the linear composer buffer would crush the steps in the shadows. */
|
|
935
|
+
applyDither() {
|
|
936
|
+
const strength = this.config.dither ?? 0;
|
|
937
|
+
if (strength > 0) {
|
|
938
|
+
if (!this.ditherPass) {
|
|
939
|
+
this.ditherPass = new ShaderPass({
|
|
940
|
+
uniforms: {
|
|
941
|
+
tDiffuse: { value: null },
|
|
942
|
+
uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },
|
|
943
|
+
uDitherStrength: { value: strength },
|
|
944
|
+
uDitherScale: { value: this.config.ditherScale ?? 2 },
|
|
945
|
+
uDitherSteps: { value: this.config.ditherSteps ?? 4 }
|
|
946
|
+
},
|
|
947
|
+
vertexShader: postVertexShader,
|
|
948
|
+
fragmentShader: ditherFragmentShader
|
|
949
|
+
});
|
|
950
|
+
this.composer.addPass(this.ditherPass);
|
|
951
|
+
}
|
|
952
|
+
const u = this.ditherPass.uniforms;
|
|
953
|
+
u.uDitherStrength.value = strength;
|
|
954
|
+
u.uDitherScale.value = Math.max(1, this.config.ditherScale ?? 2);
|
|
955
|
+
u.uDitherSteps.value = Math.max(2, Math.round(this.config.ditherSteps ?? 4));
|
|
956
|
+
} else if (this.ditherPass) {
|
|
957
|
+
this.composer.removePass(this.ditherPass);
|
|
958
|
+
this.ditherPass.dispose();
|
|
959
|
+
this.ditherPass = void 0;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
/** Insert / tune / remove the innerLight pass — volumetric light streaks scattered from the bright
|
|
963
|
+
* wave toward a light point (innerLightX/Y in UV). Scene zone (index 1) so it scatters the raw wave
|
|
964
|
+
* like bloom. innerLight 0 removes the pass entirely; created lazily on first enable. */
|
|
965
|
+
applyInnerLight() {
|
|
966
|
+
const strength = this.config.innerLight ?? 0;
|
|
967
|
+
if (strength > 0) {
|
|
968
|
+
const cx = this.config.innerLightX ?? .5;
|
|
969
|
+
const cy = this.config.innerLightY ?? .15;
|
|
970
|
+
if (!this.innerLightPass) {
|
|
971
|
+
this.innerLightPass = new ShaderPass({
|
|
972
|
+
uniforms: {
|
|
973
|
+
tDiffuse: { value: null },
|
|
974
|
+
uInnerLight: { value: strength },
|
|
975
|
+
uInnerLightDensity: { value: this.config.innerLightDensity ?? .5 },
|
|
976
|
+
uInnerLightDecay: { value: this.config.innerLightDecay ?? .95 },
|
|
977
|
+
uInnerLightCenter: { value: new THREE.Vector2(cx, cy) }
|
|
978
|
+
},
|
|
979
|
+
vertexShader: postVertexShader,
|
|
980
|
+
fragmentShader: innerLightFragmentShader
|
|
981
|
+
});
|
|
982
|
+
this.composer.insertPass(this.innerLightPass, 1);
|
|
983
|
+
}
|
|
984
|
+
const u = this.innerLightPass.uniforms;
|
|
985
|
+
u.uInnerLight.value = strength;
|
|
986
|
+
u.uInnerLightDensity.value = this.config.innerLightDensity ?? .5;
|
|
987
|
+
u.uInnerLightDecay.value = this.config.innerLightDecay ?? .95;
|
|
988
|
+
u.uInnerLightCenter.value.set(cx, cy);
|
|
989
|
+
} else if (this.innerLightPass) {
|
|
990
|
+
this.composer.removePass(this.innerLightPass);
|
|
991
|
+
this.innerLightPass.dispose();
|
|
992
|
+
this.innerLightPass = void 0;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
/** Insert / tune / remove the halftone pass — a rotated dot screen (dot size scales with local
|
|
996
|
+
* brightness) over the finished image. halftone 0 removes the pass; created lazily on enable. */
|
|
997
|
+
applyHalftone() {
|
|
998
|
+
const strength = this.config.halftone ?? 0;
|
|
999
|
+
if (strength > 0) {
|
|
1000
|
+
if (!this.halftonePass) {
|
|
1001
|
+
this.halftonePass = new ShaderPass({
|
|
1002
|
+
uniforms: {
|
|
1003
|
+
tDiffuse: { value: null },
|
|
1004
|
+
uResolution: { value: this.renderer.getDrawingBufferSize(new THREE.Vector2()) },
|
|
1005
|
+
uHalftone: { value: strength },
|
|
1006
|
+
uHalftoneCell: { value: this.config.halftoneCell ?? 6 },
|
|
1007
|
+
uHalftoneAngle: { value: this.config.halftoneAngle ?? .4 }
|
|
1008
|
+
},
|
|
1009
|
+
vertexShader: postVertexShader,
|
|
1010
|
+
fragmentShader: halftoneFragmentShader
|
|
1011
|
+
});
|
|
1012
|
+
this.composer.addPass(this.halftonePass);
|
|
1013
|
+
}
|
|
1014
|
+
const u = this.halftonePass.uniforms;
|
|
1015
|
+
u.uHalftone.value = strength;
|
|
1016
|
+
u.uHalftoneCell.value = Math.max(2, this.config.halftoneCell ?? 6);
|
|
1017
|
+
u.uHalftoneAngle.value = this.config.halftoneAngle ?? .4;
|
|
1018
|
+
} else if (this.halftonePass) {
|
|
1019
|
+
this.composer.removePass(this.halftonePass);
|
|
1020
|
+
this.halftonePass.dispose();
|
|
1021
|
+
this.halftonePass = void 0;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/** Heatmap: recolour the final image by luminance → thermal palette. Finish zone. */
|
|
1025
|
+
applyHeatmap() {
|
|
1026
|
+
const strength = this.config.heatmap ?? 0;
|
|
1027
|
+
if (strength > 0) {
|
|
1028
|
+
if (!this.heatmapPass) {
|
|
1029
|
+
this.heatmapPass = new ShaderPass({
|
|
1030
|
+
uniforms: {
|
|
1031
|
+
tDiffuse: { value: null },
|
|
1032
|
+
uHeatmap: { value: strength }
|
|
1033
|
+
},
|
|
1034
|
+
vertexShader: postVertexShader,
|
|
1035
|
+
fragmentShader: heatmapFragmentShader
|
|
1036
|
+
});
|
|
1037
|
+
this.composer.addPass(this.heatmapPass);
|
|
1038
|
+
}
|
|
1039
|
+
this.heatmapPass.uniforms.uHeatmap.value = strength;
|
|
1040
|
+
} else if (this.heatmapPass) {
|
|
1041
|
+
this.composer.removePass(this.heatmapPass);
|
|
1042
|
+
this.heatmapPass.dispose();
|
|
1043
|
+
this.heatmapPass = void 0;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/** Paper texture: fibrous substrate shading multiplied over the image. Finish zone. */
|
|
1047
|
+
applyPaperTexture() {
|
|
1048
|
+
const strength = this.config.paperTexture ?? 0;
|
|
1049
|
+
if (strength > 0) {
|
|
1050
|
+
if (!this.paperTexturePass) {
|
|
1051
|
+
this.paperTexturePass = new ShaderPass({
|
|
1052
|
+
uniforms: {
|
|
1053
|
+
tDiffuse: { value: null },
|
|
1054
|
+
uPaper: { value: strength },
|
|
1055
|
+
uPaperScale: { value: this.config.paperTextureScale ?? 2 }
|
|
1056
|
+
},
|
|
1057
|
+
vertexShader: postVertexShader,
|
|
1058
|
+
fragmentShader: paperTextureFragmentShader
|
|
1059
|
+
});
|
|
1060
|
+
this.composer.addPass(this.paperTexturePass);
|
|
1061
|
+
}
|
|
1062
|
+
const u = this.paperTexturePass.uniforms;
|
|
1063
|
+
u.uPaper.value = strength;
|
|
1064
|
+
u.uPaperScale.value = Math.max(.5, this.config.paperTextureScale ?? 2);
|
|
1065
|
+
} else if (this.paperTexturePass) {
|
|
1066
|
+
this.composer.removePass(this.paperTexturePass);
|
|
1067
|
+
this.paperTexturePass.dispose();
|
|
1068
|
+
this.paperTexturePass = void 0;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
/** CMYK halftone: four rotated dot screens (cyan/magenta/yellow/black). Finish zone. */
|
|
1072
|
+
applyHalftoneCmyk() {
|
|
1073
|
+
const strength = this.config.halftoneCmyk ?? 0;
|
|
1074
|
+
if (strength > 0) {
|
|
1075
|
+
if (!this.halftoneCmykPass) {
|
|
1076
|
+
this.halftoneCmykPass = new ShaderPass({
|
|
1077
|
+
uniforms: {
|
|
1078
|
+
tDiffuse: { value: null },
|
|
1079
|
+
uHalftoneCmyk: { value: strength },
|
|
1080
|
+
uHalftoneCmykCell: { value: this.config.halftoneCmykCell ?? 6 }
|
|
1081
|
+
},
|
|
1082
|
+
vertexShader: postVertexShader,
|
|
1083
|
+
fragmentShader: halftoneCmykFragmentShader
|
|
1084
|
+
});
|
|
1085
|
+
this.composer.addPass(this.halftoneCmykPass);
|
|
1086
|
+
}
|
|
1087
|
+
const u = this.halftoneCmykPass.uniforms;
|
|
1088
|
+
u.uHalftoneCmyk.value = strength;
|
|
1089
|
+
u.uHalftoneCmykCell.value = Math.max(2, this.config.halftoneCmykCell ?? 6);
|
|
1090
|
+
} else if (this.halftoneCmykPass) {
|
|
1091
|
+
this.composer.removePass(this.halftoneCmykPass);
|
|
1092
|
+
this.halftoneCmykPass.dispose();
|
|
1093
|
+
this.halftoneCmykPass = void 0;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
915
1096
|
onResize = () => {
|
|
916
1097
|
this.resize();
|
|
917
1098
|
};
|
|
@@ -944,6 +1125,8 @@ var WaveRenderer = class {
|
|
|
944
1125
|
const dw = w * dpr;
|
|
945
1126
|
const dh = h * dpr;
|
|
946
1127
|
this.postPass.uniforms.uResolution.value.set(dw, dh);
|
|
1128
|
+
if (this.ditherPass) this.ditherPass.uniforms.uResolution.value.set(dw, dh);
|
|
1129
|
+
if (this.halftonePass) this.halftonePass.uniforms.uResolution.value.set(dw, dh);
|
|
947
1130
|
for (const s of this.waves) s.material.uniforms.uResolution.value.set(dw, dh);
|
|
948
1131
|
this.camera.left = -dw / 2;
|
|
949
1132
|
this.camera.right = dw / 2;
|
|
@@ -1343,6 +1526,12 @@ var WaveRenderer = class {
|
|
|
1343
1526
|
s.palette.dispose();
|
|
1344
1527
|
}
|
|
1345
1528
|
this.bloomPass?.dispose();
|
|
1529
|
+
this.ditherPass?.dispose();
|
|
1530
|
+
this.innerLightPass?.dispose();
|
|
1531
|
+
this.halftonePass?.dispose();
|
|
1532
|
+
this.heatmapPass?.dispose();
|
|
1533
|
+
this.paperTexturePass?.dispose();
|
|
1534
|
+
this.halftoneCmykPass?.dispose();
|
|
1346
1535
|
this.composer.dispose();
|
|
1347
1536
|
this.renderer.dispose();
|
|
1348
1537
|
this.renderer.domElement.remove();
|