@fjandin/react-shader 0.0.16 → 0.0.18

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.
Files changed (40) hide show
  1. package/dist/ReactGpuShader.d.ts +18 -0
  2. package/dist/ReactGpuShader.d.ts.map +1 -0
  3. package/dist/example/examples/mandelbrot.d.ts +2 -0
  4. package/dist/example/examples/mandelbrot.d.ts.map +1 -0
  5. package/dist/example/examples/mandelbrot2.d.ts +2 -0
  6. package/dist/example/examples/mandelbrot2.d.ts.map +1 -0
  7. package/dist/example/examples/webgl.d.ts +2 -0
  8. package/dist/example/examples/webgl.d.ts.map +1 -0
  9. package/dist/example/examples/webgpu.d.ts +2 -0
  10. package/dist/example/examples/webgpu.d.ts.map +1 -0
  11. package/dist/example/frontend.d.ts.map +1 -1
  12. package/dist/hooks/useWebGPU.d.ts +20 -0
  13. package/dist/hooks/useWebGPU.d.ts.map +1 -0
  14. package/dist/index.cjs +882 -54
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +882 -54
  18. package/dist/shaders/color-palette-gpu.d.ts +2 -0
  19. package/dist/shaders/color-palette-gpu.d.ts.map +1 -0
  20. package/dist/shaders/distortion-ripple-gpu.d.ts +16 -0
  21. package/dist/shaders/distortion-ripple-gpu.d.ts.map +1 -0
  22. package/dist/shaders/scene-circles-gpu.d.ts +21 -0
  23. package/dist/shaders/scene-circles-gpu.d.ts.map +1 -0
  24. package/dist/shaders/simplex-noise-gpu.d.ts +2 -0
  25. package/dist/shaders/simplex-noise-gpu.d.ts.map +1 -0
  26. package/package.json +2 -1
  27. package/dist/example/glsl/cartesian-to-polar.glsl.d.ts +0 -2
  28. package/dist/example/glsl/cartesian-to-polar.glsl.d.ts.map +0 -1
  29. package/dist/example/glsl/circles.glsl.d.ts +0 -3
  30. package/dist/example/glsl/circles.glsl.d.ts.map +0 -1
  31. package/dist/example/glsl/noise.glsl.d.ts +0 -3
  32. package/dist/example/glsl/noise.glsl.d.ts.map +0 -1
  33. package/dist/example/glsl/palette.glsl.d.ts +0 -2
  34. package/dist/example/glsl/palette.glsl.d.ts.map +0 -1
  35. package/dist/example/glsl/texture-demo.glsl.d.ts +0 -2
  36. package/dist/example/glsl/texture-demo.glsl.d.ts.map +0 -1
  37. package/dist/example/lib/logger.d.ts +0 -2
  38. package/dist/example/lib/logger.d.ts.map +0 -1
  39. package/dist/example/shader.d.ts +0 -2
  40. package/dist/example/shader.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -31,11 +31,16 @@ var exports_src = {};
31
31
  __export(exports_src, {
32
32
  useAudio: () => useAudio,
33
33
  generateUtilsFunction: () => generateUtilsFunction,
34
+ generateSimplexNoiseFunctionGpu: () => generateSimplexNoiseFunctionGpu,
34
35
  generateSimplexNoiseFunction: () => generateSimplexNoiseFunction,
36
+ generateSceneCirclesFunctionGpu: () => generateSceneCirclesFunctionGpu,
35
37
  generateSceneCirclesFunction: () => generateSceneCirclesFunction,
38
+ generateDistortionRippleFunctionGpu: () => generateDistortionRippleFunctionGpu,
36
39
  generateDistortionRippleFunction: () => generateDistortionRippleFunction,
40
+ generateColorPaletteFunctionGpu: () => generateColorPaletteFunctionGpu,
37
41
  generateColorPaletteFunction: () => generateColorPaletteFunction,
38
- ReactShader: () => ReactShader
42
+ ReactShader: () => ReactShader,
43
+ ReactGpuShader: () => ReactGpuShader
39
44
  });
40
45
  module.exports = __toCommonJS(exports_src);
41
46
 
@@ -247,11 +252,585 @@ function useAudio(options = {}) {
247
252
  isRunning
248
253
  };
249
254
  }
250
- // src/ReactShader.tsx
255
+ // src/ReactGpuShader.tsx
251
256
  var import_react3 = require("react");
252
257
 
253
- // src/hooks/useWebGL.ts
258
+ // src/hooks/useWebGPU.ts
254
259
  var import_react2 = require("react");
260
+ function isVec2(value) {
261
+ return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
262
+ }
263
+ function isVec3(value) {
264
+ return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
265
+ }
266
+ function isVec4(value) {
267
+ return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
268
+ }
269
+ function isVec4Array(value) {
270
+ return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
271
+ }
272
+ function inferWgslType(value) {
273
+ if (typeof value === "number") {
274
+ return { wgslType: "f32", baseType: "f32", isArray: false, arrayLength: 0 };
275
+ }
276
+ if (isVec4Array(value)) {
277
+ return {
278
+ wgslType: `array<vec4f, 100>`,
279
+ baseType: "vec4f",
280
+ isArray: true,
281
+ arrayLength: 100
282
+ };
283
+ }
284
+ if (isVec4(value)) {
285
+ return { wgslType: "vec4f", baseType: "vec4f", isArray: false, arrayLength: 0 };
286
+ }
287
+ if (isVec3(value)) {
288
+ return { wgslType: "vec3f", baseType: "vec3f", isArray: false, arrayLength: 0 };
289
+ }
290
+ if (isVec2(value)) {
291
+ return { wgslType: "vec2f", baseType: "vec2f", isArray: false, arrayLength: 0 };
292
+ }
293
+ throw new Error(`Unsupported uniform value type: ${typeof value}`);
294
+ }
295
+ function getTypeAlignment(type) {
296
+ switch (type) {
297
+ case "f32":
298
+ return 4;
299
+ case "vec2f":
300
+ return 8;
301
+ case "vec3f":
302
+ case "vec4f":
303
+ return 16;
304
+ }
305
+ }
306
+ function getTypeSize(type) {
307
+ switch (type) {
308
+ case "f32":
309
+ return 4;
310
+ case "vec2f":
311
+ return 8;
312
+ case "vec3f":
313
+ return 12;
314
+ case "vec4f":
315
+ return 16;
316
+ }
317
+ }
318
+ var UNIFORM_ARRAY_STRIDE = 16;
319
+ var DEFAULT_UNIFORMS = [
320
+ { name: "iTime", baseType: "f32" },
321
+ { name: "iMouseLeftDown", baseType: "f32" },
322
+ { name: "iResolution", baseType: "vec2f" },
323
+ { name: "iMouse", baseType: "vec2f" },
324
+ { name: "iMouseNormalized", baseType: "vec2f" }
325
+ ];
326
+ function calculateUniformLayout(customUniforms) {
327
+ const fields = [];
328
+ let offset = 0;
329
+ const addField = (name, baseType) => {
330
+ const alignment = getTypeAlignment(baseType);
331
+ const size = getTypeSize(baseType);
332
+ offset = Math.ceil(offset / alignment) * alignment;
333
+ fields.push({ name, type: baseType, offset, size, isArray: false });
334
+ offset += size;
335
+ };
336
+ const addArrayField = (name, baseType, arrayLength) => {
337
+ offset = Math.ceil(offset / 16) * 16;
338
+ const totalSize = arrayLength * UNIFORM_ARRAY_STRIDE;
339
+ fields.push({
340
+ name,
341
+ type: `array<${baseType}, ${arrayLength}>`,
342
+ offset,
343
+ size: totalSize,
344
+ isArray: true,
345
+ arrayLength,
346
+ elementStride: UNIFORM_ARRAY_STRIDE
347
+ });
348
+ offset += totalSize;
349
+ };
350
+ for (const u of DEFAULT_UNIFORMS) {
351
+ addField(u.name, u.baseType);
352
+ }
353
+ if (customUniforms) {
354
+ const scalarEntries = [];
355
+ const arrayEntries = [];
356
+ for (const [name, value] of Object.entries(customUniforms)) {
357
+ const inferred = inferWgslType(value);
358
+ if (inferred.isArray) {
359
+ arrayEntries.push({ name, inferred });
360
+ } else {
361
+ scalarEntries.push({ name, inferred });
362
+ }
363
+ }
364
+ for (const { name } of arrayEntries) {
365
+ scalarEntries.push({
366
+ name: `${name}_count`,
367
+ inferred: { wgslType: "f32", baseType: "f32", isArray: false, arrayLength: 0 }
368
+ });
369
+ }
370
+ scalarEntries.sort((a, b) => getTypeAlignment(b.inferred.baseType) - getTypeAlignment(a.inferred.baseType));
371
+ for (const { name, inferred } of scalarEntries) {
372
+ addField(name, inferred.baseType);
373
+ }
374
+ for (const { name, inferred } of arrayEntries) {
375
+ addArrayField(name, inferred.baseType, inferred.arrayLength);
376
+ }
377
+ }
378
+ const bufferSize = Math.ceil(offset / 16) * 16;
379
+ return { fields, bufferSize };
380
+ }
381
+ function generateUniformStruct(layout) {
382
+ const members = layout.fields.map((f) => ` ${f.name}: ${f.type},`).join(`
383
+ `);
384
+ return `struct Uniforms {
385
+ ${members}
386
+ }`;
387
+ }
388
+ function packUniformValue(field, value, floatData) {
389
+ const floatOffset = field.offset / 4;
390
+ if (field.isArray && field.elementStride && field.arrayLength) {
391
+ const stride = field.elementStride / 4;
392
+ const maxLen = field.arrayLength;
393
+ if (isVec4Array(value)) {
394
+ for (let i = 0;i < value.length && i < maxLen; i++) {
395
+ const elemOffset = floatOffset + i * stride;
396
+ floatData[elemOffset] = value[i][0];
397
+ floatData[elemOffset + 1] = value[i][1];
398
+ floatData[elemOffset + 2] = value[i][2];
399
+ floatData[elemOffset + 3] = value[i][3];
400
+ }
401
+ }
402
+ } else if (typeof value === "number") {
403
+ floatData[floatOffset] = value;
404
+ } else if (Array.isArray(value) && typeof value[0] === "number") {
405
+ for (let i = 0;i < value.length; i++) {
406
+ floatData[floatOffset + i] = value[i];
407
+ }
408
+ }
409
+ }
410
+ var VERTEX_SHADER = `
411
+ struct VertexOutput {
412
+ @builtin(position) position: vec4f,
413
+ @location(0) uv: vec2f,
414
+ }
415
+
416
+ @vertex
417
+ fn main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
418
+ // Full-screen triangle (covers clip space with a single triangle)
419
+ var pos = array<vec2f, 3>(
420
+ vec2f(-1.0, -1.0),
421
+ vec2f(3.0, -1.0),
422
+ vec2f(-1.0, 3.0)
423
+ );
424
+
425
+ var output: VertexOutput;
426
+ output.position = vec4f(pos[vertexIndex], 0.0, 1.0);
427
+ // UV coordinates: (0,0) at bottom-left, (1,1) at top-right
428
+ output.uv = (pos[vertexIndex] + 1.0) * 0.5;
429
+ return output;
430
+ }
431
+ `;
432
+ function createFragmentShader(userCode, layout) {
433
+ return `
434
+ ${generateUniformStruct(layout)}
435
+
436
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
437
+
438
+ ${userCode}
439
+
440
+ @fragment
441
+ fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
442
+ let fragCoord = uv * uniforms.iResolution;
443
+ let correctedUv = ((uv * uniforms.iResolution) - 0.5 * uniforms.iResolution) / uniforms.iResolution.y;
444
+
445
+ return mainImage(correctedUv);
446
+ }
447
+ `;
448
+ }
449
+ async function initializeWebGPU(canvas, fragmentSource, customUniforms) {
450
+ if (!navigator.gpu) {
451
+ throw new Error("WebGPU not supported in this browser");
452
+ }
453
+ const adapter = await navigator.gpu.requestAdapter();
454
+ if (!adapter) {
455
+ throw new Error("Failed to get WebGPU adapter");
456
+ }
457
+ const device = await adapter.requestDevice();
458
+ const context = canvas.getContext("webgpu");
459
+ if (!context) {
460
+ throw new Error("Failed to get WebGPU context");
461
+ }
462
+ const format = navigator.gpu.getPreferredCanvasFormat();
463
+ context.configure({
464
+ device,
465
+ format,
466
+ alphaMode: "premultiplied"
467
+ });
468
+ const uniformLayout = calculateUniformLayout(customUniforms);
469
+ const vertexModule = device.createShaderModule({
470
+ label: "vertex shader",
471
+ code: VERTEX_SHADER
472
+ });
473
+ const fragmentModule = device.createShaderModule({
474
+ label: "fragment shader",
475
+ code: createFragmentShader(fragmentSource, uniformLayout)
476
+ });
477
+ const uniformBuffer = device.createBuffer({
478
+ label: "uniforms",
479
+ size: uniformLayout.bufferSize,
480
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
481
+ });
482
+ const bindGroupLayout = device.createBindGroupLayout({
483
+ entries: [
484
+ {
485
+ binding: 0,
486
+ visibility: GPUShaderStage.FRAGMENT,
487
+ buffer: { type: "uniform" }
488
+ }
489
+ ]
490
+ });
491
+ const uniformBindGroup = device.createBindGroup({
492
+ layout: bindGroupLayout,
493
+ entries: [
494
+ {
495
+ binding: 0,
496
+ resource: { buffer: uniformBuffer }
497
+ }
498
+ ]
499
+ });
500
+ const pipelineLayout = device.createPipelineLayout({
501
+ bindGroupLayouts: [bindGroupLayout]
502
+ });
503
+ const pipeline = device.createRenderPipeline({
504
+ label: "render pipeline",
505
+ layout: pipelineLayout,
506
+ vertex: {
507
+ module: vertexModule,
508
+ entryPoint: "main"
509
+ },
510
+ fragment: {
511
+ module: fragmentModule,
512
+ entryPoint: "main",
513
+ targets: [{ format }]
514
+ }
515
+ });
516
+ return {
517
+ device,
518
+ context,
519
+ pipeline,
520
+ uniformBuffer,
521
+ uniformBindGroup,
522
+ uniformLayout
523
+ };
524
+ }
525
+ function cleanupWebGPU(state) {
526
+ state.uniformBuffer.destroy();
527
+ state.device.destroy();
528
+ }
529
+ function useWebGPU(options) {
530
+ const canvasRef = import_react2.useRef(null);
531
+ const stateRef = import_react2.useRef(null);
532
+ const animationFrameRef = import_react2.useRef(0);
533
+ const elapsedTimeRef = import_react2.useRef(0);
534
+ const lastFrameTimeRef = import_react2.useRef(0);
535
+ const mouseRef = import_react2.useRef([0, 0]);
536
+ const mouseNormalizedRef = import_react2.useRef([0, 0]);
537
+ const mouseLeftDownRef = import_react2.useRef(false);
538
+ const canvasRectRef = import_react2.useRef(null);
539
+ const onErrorRef = import_react2.useRef(options.onError);
540
+ const onFrameRef = import_react2.useRef(options.onFrame);
541
+ const onClickRef = import_react2.useRef(options.onClick);
542
+ const onMouseDownRef = import_react2.useRef(options.onMouseDown);
543
+ const onMouseUpRef = import_react2.useRef(options.onMouseUp);
544
+ const onMouseMoveRef = import_react2.useRef(options.onMouseMove);
545
+ const onMouseWheelRef = import_react2.useRef(options.onMouseWheel);
546
+ const timeScaleRef = import_react2.useRef(options.timeScale ?? 1);
547
+ const fragmentRef = import_react2.useRef(options.fragment);
548
+ const uniformsRef = import_react2.useRef(options.uniforms);
549
+ const dprRef = import_react2.useRef(window.devicePixelRatio || 1);
550
+ onErrorRef.current = options.onError;
551
+ onFrameRef.current = options.onFrame;
552
+ onClickRef.current = options.onClick;
553
+ onMouseDownRef.current = options.onMouseDown;
554
+ onMouseUpRef.current = options.onMouseUp;
555
+ onMouseMoveRef.current = options.onMouseMove;
556
+ onMouseWheelRef.current = options.onMouseWheel;
557
+ timeScaleRef.current = options.timeScale ?? 1;
558
+ fragmentRef.current = options.fragment;
559
+ uniformsRef.current = options.uniforms;
560
+ const buildFrameInfo = import_react2.useCallback((deltaTime) => {
561
+ const canvas = canvasRef.current;
562
+ return {
563
+ deltaTime,
564
+ time: elapsedTimeRef.current,
565
+ resolution: [canvas?.width ?? 0, canvas?.height ?? 0],
566
+ mouse: mouseRef.current,
567
+ mouseNormalized: mouseNormalizedRef.current,
568
+ mouseLeftDown: mouseLeftDownRef.current
569
+ };
570
+ }, []);
571
+ const render = import_react2.useCallback((time) => {
572
+ const state = stateRef.current;
573
+ const canvas = canvasRef.current;
574
+ if (!state || !canvas) {
575
+ return;
576
+ }
577
+ const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
578
+ lastFrameTimeRef.current = time;
579
+ elapsedTimeRef.current += deltaTime * timeScaleRef.current;
580
+ if (onFrameRef.current) {
581
+ onFrameRef.current(buildFrameInfo(deltaTime));
582
+ }
583
+ const { device, context, pipeline, uniformBuffer, uniformBindGroup, uniformLayout } = state;
584
+ const dpr = dprRef.current;
585
+ const displayWidth = canvas.clientWidth;
586
+ const displayHeight = canvas.clientHeight;
587
+ if (displayWidth === 0 || displayHeight === 0) {
588
+ animationFrameRef.current = requestAnimationFrame(render);
589
+ return;
590
+ }
591
+ const bufferWidth = Math.round(displayWidth * dpr);
592
+ const bufferHeight = Math.round(displayHeight * dpr);
593
+ if (canvas.width !== bufferWidth || canvas.height !== bufferHeight) {
594
+ canvas.width = bufferWidth;
595
+ canvas.height = bufferHeight;
596
+ }
597
+ const defaultValues = {
598
+ iTime: elapsedTimeRef.current,
599
+ iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
600
+ iResolution: [canvas.width, canvas.height],
601
+ iMouse: mouseRef.current,
602
+ iMouseNormalized: mouseNormalizedRef.current
603
+ };
604
+ const allValues = { ...defaultValues, ...uniformsRef.current };
605
+ if (uniformsRef.current) {
606
+ for (const [name, value] of Object.entries(uniformsRef.current)) {
607
+ if (isVec4Array(value)) {
608
+ allValues[`${name}_count`] = value.length;
609
+ }
610
+ }
611
+ }
612
+ const uniformData = new Float32Array(uniformLayout.bufferSize / 4);
613
+ for (const field of uniformLayout.fields) {
614
+ const value = allValues[field.name];
615
+ if (value === undefined) {
616
+ continue;
617
+ }
618
+ packUniformValue(field, value, uniformData);
619
+ }
620
+ device.queue.writeBuffer(uniformBuffer, 0, uniformData);
621
+ const commandEncoder = device.createCommandEncoder();
622
+ const textureView = context.getCurrentTexture().createView();
623
+ const renderPass = commandEncoder.beginRenderPass({
624
+ colorAttachments: [
625
+ {
626
+ view: textureView,
627
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
628
+ loadOp: "clear",
629
+ storeOp: "store"
630
+ }
631
+ ]
632
+ });
633
+ renderPass.setPipeline(pipeline);
634
+ renderPass.setBindGroup(0, uniformBindGroup);
635
+ renderPass.draw(3);
636
+ renderPass.end();
637
+ device.queue.submit([commandEncoder.finish()]);
638
+ animationFrameRef.current = requestAnimationFrame(render);
639
+ }, [buildFrameInfo]);
640
+ import_react2.useEffect(() => {
641
+ const canvas = canvasRef.current;
642
+ if (!canvas)
643
+ return;
644
+ let mounted = true;
645
+ const initialize = async () => {
646
+ try {
647
+ const state = await initializeWebGPU(canvas, fragmentRef.current, uniformsRef.current);
648
+ if (!mounted) {
649
+ cleanupWebGPU(state);
650
+ return;
651
+ }
652
+ stateRef.current = state;
653
+ elapsedTimeRef.current = 0;
654
+ lastFrameTimeRef.current = 0;
655
+ animationFrameRef.current = requestAnimationFrame(render);
656
+ } catch (err) {
657
+ if (!mounted)
658
+ return;
659
+ const error = err instanceof Error ? err : new Error(String(err));
660
+ if (onErrorRef.current) {
661
+ onErrorRef.current(error);
662
+ } else {
663
+ console.error("WebGPU initialization failed:", error);
664
+ }
665
+ }
666
+ };
667
+ const dprMediaQuery = window.matchMedia(`(resolution: ${dprRef.current}dppx)`);
668
+ const handleDprChange = () => {
669
+ dprRef.current = window.devicePixelRatio || 1;
670
+ };
671
+ dprMediaQuery.addEventListener("change", handleDprChange);
672
+ initialize();
673
+ return () => {
674
+ mounted = false;
675
+ dprMediaQuery.removeEventListener("change", handleDprChange);
676
+ cancelAnimationFrame(animationFrameRef.current);
677
+ if (stateRef.current) {
678
+ cleanupWebGPU(stateRef.current);
679
+ stateRef.current = null;
680
+ }
681
+ };
682
+ }, [render]);
683
+ import_react2.useEffect(() => {
684
+ const canvas = canvasRef.current;
685
+ if (!canvas)
686
+ return;
687
+ const updateRect = () => {
688
+ canvasRectRef.current = canvas.getBoundingClientRect();
689
+ };
690
+ updateRect();
691
+ const resizeObserver = new ResizeObserver(updateRect);
692
+ resizeObserver.observe(canvas);
693
+ window.addEventListener("scroll", updateRect, { passive: true });
694
+ const handleMouseMove = (event) => {
695
+ const rect = canvasRectRef.current;
696
+ if (!rect)
697
+ return;
698
+ const dpr = dprRef.current;
699
+ const x = (event.clientX - rect.left) * dpr;
700
+ const y = (rect.height - (event.clientY - rect.top)) * dpr;
701
+ mouseRef.current = [x, y];
702
+ const minDimension = Math.min(canvas.width, canvas.height) || 1;
703
+ mouseNormalizedRef.current = [
704
+ (mouseRef.current[0] - canvas.width / 2) / minDimension,
705
+ (mouseRef.current[1] - canvas.height / 2) / minDimension
706
+ ];
707
+ onMouseMoveRef.current?.(buildFrameInfo(0));
708
+ };
709
+ const handleMouseDown = (event) => {
710
+ if (event.button === 0) {
711
+ mouseLeftDownRef.current = true;
712
+ }
713
+ onMouseDownRef.current?.(buildFrameInfo(0));
714
+ };
715
+ const handleMouseUp = (event) => {
716
+ if (event.button === 0) {
717
+ mouseLeftDownRef.current = false;
718
+ }
719
+ onMouseUpRef.current?.(buildFrameInfo(0));
720
+ };
721
+ const handleClick = () => {
722
+ onClickRef.current?.(buildFrameInfo(0));
723
+ };
724
+ const handleMouseWheel = (event) => {
725
+ onMouseWheelRef.current?.(buildFrameInfo(0), event.deltaY);
726
+ };
727
+ window.addEventListener("mousemove", handleMouseMove);
728
+ window.addEventListener("mousedown", handleMouseDown);
729
+ window.addEventListener("mouseup", handleMouseUp);
730
+ canvas.addEventListener("click", handleClick);
731
+ window.addEventListener("wheel", handleMouseWheel);
732
+ return () => {
733
+ resizeObserver.disconnect();
734
+ window.removeEventListener("scroll", updateRect);
735
+ window.removeEventListener("mousemove", handleMouseMove);
736
+ window.removeEventListener("mousedown", handleMouseDown);
737
+ window.removeEventListener("mouseup", handleMouseUp);
738
+ canvas.removeEventListener("click", handleClick);
739
+ window.removeEventListener("wheel", handleMouseWheel);
740
+ };
741
+ }, [buildFrameInfo]);
742
+ return { canvasRef, mouseRef };
743
+ }
744
+
745
+ // src/ReactGpuShader.tsx
746
+ var jsx_dev_runtime = require("react/jsx-dev-runtime");
747
+ var FULLSCREEN_CONTAINER_STYLE = {
748
+ position: "fixed",
749
+ top: 0,
750
+ left: 0,
751
+ width: "100vw",
752
+ height: "100vh",
753
+ zIndex: 9000
754
+ };
755
+ var DEFAULT_CONTAINER_STYLE = {
756
+ position: "relative",
757
+ width: "100%",
758
+ height: "100%"
759
+ };
760
+ var CANVAS_STYLE = {
761
+ display: "block",
762
+ width: "100%",
763
+ height: "100%"
764
+ };
765
+ function ReactGpuShader({
766
+ className,
767
+ fragment,
768
+ uniforms,
769
+ fullscreen = false,
770
+ timeScale,
771
+ onFrame,
772
+ onClick,
773
+ onMouseMove,
774
+ onMouseDown,
775
+ onMouseUp,
776
+ onMouseWheel
777
+ }) {
778
+ const [error, setError] = import_react3.useState(null);
779
+ const handleError = import_react3.useCallback((err) => {
780
+ setError(err.message);
781
+ console.error("ReactGpuShader error:", err);
782
+ }, []);
783
+ import_react3.useEffect(() => {
784
+ setError(null);
785
+ }, [fragment]);
786
+ const { canvasRef } = useWebGPU({
787
+ fragment,
788
+ uniforms,
789
+ onError: handleError,
790
+ timeScale,
791
+ onFrame,
792
+ onClick,
793
+ onMouseMove,
794
+ onMouseDown,
795
+ onMouseUp,
796
+ onMouseWheel
797
+ });
798
+ const containerStyle = import_react3.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE : DEFAULT_CONTAINER_STYLE, [fullscreen]);
799
+ if (error) {
800
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
801
+ className,
802
+ style: {
803
+ ...containerStyle,
804
+ display: "flex",
805
+ alignItems: "center",
806
+ justifyContent: "center",
807
+ backgroundColor: "#1a1a1a",
808
+ color: "#ff6b6b",
809
+ fontFamily: "monospace",
810
+ fontSize: "12px",
811
+ padding: "16px",
812
+ overflow: "auto",
813
+ boxSizing: "border-box",
814
+ width: "100%",
815
+ height: "100%"
816
+ },
817
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
818
+ style: { margin: 0, whiteSpace: "pre-wrap" },
819
+ children: error
820
+ }, undefined, false, undefined, this)
821
+ }, undefined, false, undefined, this);
822
+ }
823
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("canvas", {
824
+ ref: canvasRef,
825
+ className,
826
+ style: CANVAS_STYLE
827
+ }, undefined, false, undefined, this);
828
+ }
829
+ // src/ReactShader.tsx
830
+ var import_react5 = require("react");
831
+
832
+ // src/hooks/useWebGL.ts
833
+ var import_react4 = require("react");
255
834
 
256
835
  // src/utils/shader.ts
257
836
  function compileShader(gl, type, source) {
@@ -440,13 +1019,13 @@ function cleanupTextures(gl, manager) {
440
1019
 
441
1020
  // src/utils/uniforms.ts
442
1021
  var MAX_ARRAY_LENGTH = 100;
443
- function isVec2(value) {
1022
+ function isVec22(value) {
444
1023
  return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
445
1024
  }
446
- function isVec3(value) {
1025
+ function isVec32(value) {
447
1026
  return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
448
1027
  }
449
- function isVec4(value) {
1028
+ function isVec42(value) {
450
1029
  return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
451
1030
  }
452
1031
  function isFloatArray(value) {
@@ -458,11 +1037,11 @@ function isVec2Array(value) {
458
1037
  function isVec3Array(value) {
459
1038
  return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
460
1039
  }
461
- function isVec4Array(value) {
1040
+ function isVec4Array2(value) {
462
1041
  return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
463
1042
  }
464
1043
  function isArrayUniform(value) {
465
- return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
1044
+ return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array2(value);
466
1045
  }
467
1046
  function setUniform(gl, location, value) {
468
1047
  if (location === null) {
@@ -470,7 +1049,7 @@ function setUniform(gl, location, value) {
470
1049
  }
471
1050
  if (typeof value === "number") {
472
1051
  gl.uniform1f(location, value);
473
- } else if (isVec4Array(value)) {
1052
+ } else if (isVec4Array2(value)) {
474
1053
  gl.uniform4fv(location, value.flat());
475
1054
  } else if (isVec3Array(value)) {
476
1055
  gl.uniform3fv(location, value.flat());
@@ -478,11 +1057,11 @@ function setUniform(gl, location, value) {
478
1057
  gl.uniform2fv(location, value.flat());
479
1058
  } else if (isFloatArray(value)) {
480
1059
  gl.uniform1fv(location, value);
481
- } else if (isVec4(value)) {
1060
+ } else if (isVec42(value)) {
482
1061
  gl.uniform4f(location, value[0], value[1], value[2], value[3]);
483
- } else if (isVec3(value)) {
1062
+ } else if (isVec32(value)) {
484
1063
  gl.uniform3f(location, value[0], value[1], value[2]);
485
- } else if (isVec2(value)) {
1064
+ } else if (isVec22(value)) {
486
1065
  gl.uniform2f(location, value[0], value[1]);
487
1066
  }
488
1067
  }
@@ -526,7 +1105,7 @@ function getUniformType(value) {
526
1105
  if (typeof value === "number") {
527
1106
  return "float";
528
1107
  }
529
- if (isVec4Array(value)) {
1108
+ if (isVec4Array2(value)) {
530
1109
  return `vec4[${MAX_ARRAY_LENGTH}]`;
531
1110
  }
532
1111
  if (isVec3Array(value)) {
@@ -538,13 +1117,13 @@ function getUniformType(value) {
538
1117
  if (isFloatArray(value)) {
539
1118
  return `float[${MAX_ARRAY_LENGTH}]`;
540
1119
  }
541
- if (isVec4(value)) {
1120
+ if (isVec42(value)) {
542
1121
  return "vec4";
543
1122
  }
544
- if (isVec3(value)) {
1123
+ if (isVec32(value)) {
545
1124
  return "vec3";
546
1125
  }
547
- if (isVec2(value)) {
1126
+ if (isVec22(value)) {
548
1127
  return "vec2";
549
1128
  }
550
1129
  return "float";
@@ -616,29 +1195,29 @@ function cleanupWebGL(gl, state) {
616
1195
  gl.deleteProgram(state.program);
617
1196
  }
618
1197
  function useWebGL(options) {
619
- const canvasRef = import_react2.useRef(null);
620
- const stateRef = import_react2.useRef(null);
621
- const animationFrameRef = import_react2.useRef(0);
622
- const elapsedTimeRef = import_react2.useRef(0);
623
- const lastFrameTimeRef = import_react2.useRef(0);
624
- const mouseRef = import_react2.useRef([0, 0]);
625
- const mouseNormalizedRef = import_react2.useRef([0, 0]);
626
- const mouseLeftDownRef = import_react2.useRef(false);
627
- const canvasRectRef = import_react2.useRef(null);
628
- const contextLostRef = import_react2.useRef(false);
629
- const uniformsRef = import_react2.useRef(options.uniforms);
630
- const onErrorRef = import_react2.useRef(options.onError);
631
- const onFrameRef = import_react2.useRef(options.onFrame);
632
- const onClickRef = import_react2.useRef(options.onClick);
633
- const onMouseDownRef = import_react2.useRef(options.onMouseDown);
634
- const onMouseUpRef = import_react2.useRef(options.onMouseUp);
635
- const onMouseMoveRef = import_react2.useRef(options.onMouseMove);
636
- const onMouseWheelRef = import_react2.useRef(options.onMouseWheel);
637
- const timeScaleRef = import_react2.useRef(options.timeScale ?? 1);
638
- const vertexRef = import_react2.useRef(options.vertex);
639
- const fragmentRef = import_react2.useRef(options.fragment);
640
- const dprRef = import_react2.useRef(window.devicePixelRatio || 1);
641
- const defaultUniformsRef = import_react2.useRef({
1198
+ const canvasRef = import_react4.useRef(null);
1199
+ const stateRef = import_react4.useRef(null);
1200
+ const animationFrameRef = import_react4.useRef(0);
1201
+ const elapsedTimeRef = import_react4.useRef(0);
1202
+ const lastFrameTimeRef = import_react4.useRef(0);
1203
+ const mouseRef = import_react4.useRef([0, 0]);
1204
+ const mouseNormalizedRef = import_react4.useRef([0, 0]);
1205
+ const mouseLeftDownRef = import_react4.useRef(false);
1206
+ const canvasRectRef = import_react4.useRef(null);
1207
+ const contextLostRef = import_react4.useRef(false);
1208
+ const uniformsRef = import_react4.useRef(options.uniforms);
1209
+ const onErrorRef = import_react4.useRef(options.onError);
1210
+ const onFrameRef = import_react4.useRef(options.onFrame);
1211
+ const onClickRef = import_react4.useRef(options.onClick);
1212
+ const onMouseDownRef = import_react4.useRef(options.onMouseDown);
1213
+ const onMouseUpRef = import_react4.useRef(options.onMouseUp);
1214
+ const onMouseMoveRef = import_react4.useRef(options.onMouseMove);
1215
+ const onMouseWheelRef = import_react4.useRef(options.onMouseWheel);
1216
+ const timeScaleRef = import_react4.useRef(options.timeScale ?? 1);
1217
+ const vertexRef = import_react4.useRef(options.vertex);
1218
+ const fragmentRef = import_react4.useRef(options.fragment);
1219
+ const dprRef = import_react4.useRef(window.devicePixelRatio || 1);
1220
+ const defaultUniformsRef = import_react4.useRef({
642
1221
  iTime: 0,
643
1222
  iMouse: [0, 0],
644
1223
  iMouseNormalized: [0, 0],
@@ -656,7 +1235,7 @@ function useWebGL(options) {
656
1235
  timeScaleRef.current = options.timeScale ?? 1;
657
1236
  vertexRef.current = options.vertex;
658
1237
  fragmentRef.current = options.fragment;
659
- const render = import_react2.useCallback((time) => {
1238
+ const render = import_react4.useCallback((time) => {
660
1239
  if (contextLostRef.current)
661
1240
  return;
662
1241
  const state = stateRef.current;
@@ -708,7 +1287,7 @@ function useWebGL(options) {
708
1287
  }
709
1288
  animationFrameRef.current = requestAnimationFrame(render);
710
1289
  }, []);
711
- import_react2.useEffect(() => {
1290
+ import_react4.useEffect(() => {
712
1291
  const canvas = canvasRef.current;
713
1292
  if (!canvas)
714
1293
  return;
@@ -756,7 +1335,7 @@ function useWebGL(options) {
756
1335
  }
757
1336
  };
758
1337
  }, [render]);
759
- import_react2.useEffect(() => {
1338
+ import_react4.useEffect(() => {
760
1339
  const canvas = canvasRef.current;
761
1340
  if (!canvas)
762
1341
  return;
@@ -856,7 +1435,7 @@ function useWebGL(options) {
856
1435
  }
857
1436
 
858
1437
  // src/ReactShader.tsx
859
- var jsx_dev_runtime = require("react/jsx-dev-runtime");
1438
+ var jsx_dev_runtime2 = require("react/jsx-dev-runtime");
860
1439
  var DEFAULT_VERTEX = `#version 300 es
861
1440
  precision highp float;
862
1441
  in vec2 a_position;
@@ -865,7 +1444,7 @@ void main() {
865
1444
  gl_Position = vec4(a_position, 0.0, 1.0);
866
1445
  }
867
1446
  `;
868
- var FULLSCREEN_CONTAINER_STYLE = {
1447
+ var FULLSCREEN_CONTAINER_STYLE2 = {
869
1448
  position: "fixed",
870
1449
  top: 0,
871
1450
  left: 0,
@@ -873,12 +1452,12 @@ var FULLSCREEN_CONTAINER_STYLE = {
873
1452
  height: "100vh",
874
1453
  zIndex: 9000
875
1454
  };
876
- var DEFAULT_CONTAINER_STYLE = {
1455
+ var DEFAULT_CONTAINER_STYLE2 = {
877
1456
  position: "relative",
878
1457
  width: "100%",
879
1458
  height: "100%"
880
1459
  };
881
- var CANVAS_STYLE = {
1460
+ var CANVAS_STYLE2 = {
882
1461
  display: "block",
883
1462
  width: "100%",
884
1463
  height: "100%"
@@ -897,12 +1476,12 @@ function ReactShader({
897
1476
  onMouseUp,
898
1477
  onMouseWheel
899
1478
  }) {
900
- const [error, setError] = import_react3.useState(null);
901
- const handleError = import_react3.useCallback((err) => {
1479
+ const [error, setError] = import_react5.useState(null);
1480
+ const handleError = import_react5.useCallback((err) => {
902
1481
  setError(err.message);
903
1482
  console.error("ReactShader error:", err);
904
1483
  }, []);
905
- import_react3.useEffect(() => {
1484
+ import_react5.useEffect(() => {
906
1485
  setError(null);
907
1486
  }, [fragment, vertex]);
908
1487
  const { canvasRef } = useWebGL({
@@ -918,9 +1497,9 @@ function ReactShader({
918
1497
  onMouseWheel,
919
1498
  timeScale
920
1499
  });
921
- const containerStyle = import_react3.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE : DEFAULT_CONTAINER_STYLE, [fullscreen]);
1500
+ const containerStyle = import_react5.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE2 : DEFAULT_CONTAINER_STYLE2, [fullscreen]);
922
1501
  if (error) {
923
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("div", {
1502
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("div", {
924
1503
  className,
925
1504
  style: {
926
1505
  ...containerStyle,
@@ -937,16 +1516,16 @@ function ReactShader({
937
1516
  width: "100%",
938
1517
  height: "100%"
939
1518
  },
940
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV("pre", {
1519
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("pre", {
941
1520
  style: { margin: 0, whiteSpace: "pre-wrap" },
942
1521
  children: error
943
1522
  }, undefined, false, undefined, this)
944
1523
  }, undefined, false, undefined, this);
945
1524
  }
946
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV("canvas", {
1525
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("canvas", {
947
1526
  ref: canvasRef,
948
1527
  className,
949
- style: CANVAS_STYLE
1528
+ style: CANVAS_STYLE2
950
1529
  }, undefined, false, undefined, this);
951
1530
  }
952
1531
  // src/shaders/color-palette.ts
@@ -962,6 +1541,19 @@ function generateColorPaletteFunction(name, paletteString) {
962
1541
  }
963
1542
  `;
964
1543
  }
1544
+ // src/shaders/color-palette-gpu.ts
1545
+ function generateColorPaletteFunctionGpu(name, paletteString) {
1546
+ const paletteArray = paletteString.replace("[[", "").replace("]]", "").split("] [").map((s) => s.split(" "));
1547
+ return `
1548
+ fn ${name}(t: f32) -> vec3f {
1549
+ let a = vec3f(${paletteArray[0].join(", ")});
1550
+ let b = vec3f(${paletteArray[1].join(", ")});
1551
+ let c = vec3f(${paletteArray[2].join(", ")});
1552
+ let d = vec3f(${paletteArray[3].join(", ")});
1553
+ return a + b * cos(6.28318 * (c * t + d));
1554
+ }
1555
+ `;
1556
+ }
965
1557
  // src/shaders/distortion-ripple.ts
966
1558
  function generateDistortionRippleFunction() {
967
1559
  return `
@@ -985,6 +1577,29 @@ function generateDistortionRippleFunction() {
985
1577
  }
986
1578
  `;
987
1579
  }
1580
+ // src/shaders/distortion-ripple-gpu.ts
1581
+ function generateDistortionRippleFunctionGpu() {
1582
+ return `
1583
+ fn DistortionRipple(uv: vec2f, center: vec2f, radius: f32, intensity: f32, thickness: f32) -> vec2f {
1584
+ // 1. Calculate vector and distance from center
1585
+ let dir = uv - center;
1586
+ let dist = length(dir);
1587
+
1588
+ // 2. Create a mask so the ripple only exists near the radius
1589
+ // Using smoothstep creates a soft edge for the ripple
1590
+ let mask = smoothstep(radius + thickness, radius, dist) * smoothstep(radius - thickness, radius, dist);
1591
+
1592
+ // 3. Calculate the displacement amount using a Sine wave
1593
+ // We subtract dist from radius to orient the wave correctly
1594
+ let wave = sin((dist - radius) * 20.0);
1595
+
1596
+ // 4. Apply intensity and mask, then offset the UV
1597
+ let offset = normalize(dir) * wave * intensity * mask;
1598
+
1599
+ return offset;
1600
+ }
1601
+ `;
1602
+ }
988
1603
  // src/shaders/scene-circles.ts
989
1604
  function generateSceneCirclesFunction(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0.5] [1.0 1.0 1.0] [0.263 0.416 0.557]]") {
990
1605
  return `
@@ -1019,6 +1634,44 @@ function generateSceneCirclesFunction(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0
1019
1634
  }
1020
1635
  `;
1021
1636
  }
1637
+ // src/shaders/scene-circles-gpu.ts
1638
+ function generateSceneCirclesFunctionGpu(paletteString = "[[0.5 0.5 0.5] [0.5 0.5 0.5] [1.0 1.0 1.0] [0.263 0.416 0.557]]") {
1639
+ return `
1640
+ ${generateColorPaletteFunctionGpu("circlesPalette", paletteString)}
1641
+
1642
+ fn glsl_fract(x: vec2f) -> vec2f {
1643
+ return x - floor(x);
1644
+ }
1645
+
1646
+ fn SceneCircles(
1647
+ uv0: vec2f,
1648
+ iterations: f32,
1649
+ fractMultiplier: f32,
1650
+ time: f32,
1651
+ waveLength: f32,
1652
+ edgeBlur: f32,
1653
+ contrast: f32
1654
+ ) -> vec3f {
1655
+ var col = vec3f(0.0);
1656
+ var uv = uv0;
1657
+
1658
+ for (var i = 0.0; i < iterations; i += 1.0) {
1659
+ uv = fract(uv * fractMultiplier) - 0.5;
1660
+
1661
+ var d = length(uv) * exp(-length(uv0));
1662
+
1663
+ let color = circlesPalette(length(uv0) + i * 0.4 + time * 0.4);
1664
+
1665
+ d = sin(d * waveLength + time) / waveLength;
1666
+ d = abs(d);
1667
+ d = pow(edgeBlur / d, contrast);
1668
+
1669
+ col += color * d;
1670
+ }
1671
+ return col;
1672
+ }
1673
+ `;
1674
+ }
1022
1675
  // src/shaders/simplex-noise.ts
1023
1676
  function generateSimplexNoiseFunction() {
1024
1677
  return `
@@ -1203,6 +1856,181 @@ function generateSimplexNoiseFunction() {
1203
1856
  }
1204
1857
  `;
1205
1858
  }
1859
+ // src/shaders/simplex-noise-gpu.ts
1860
+ function generateSimplexNoiseFunctionGpu() {
1861
+ return `
1862
+
1863
+ fn mod289_4(x: vec4f) -> vec4f { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1864
+
1865
+ fn mod289_3(x: vec3f) -> vec3f { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1866
+
1867
+ fn mod289_1(x: f32) -> f32 { return x - floor(x * (1.0 / 289.0)) * 289.0; }
1868
+
1869
+ fn permute_4(x: vec4f) -> vec4f { return mod289_4(((x * 34.0) + 10.0) * x); }
1870
+
1871
+ fn permute_1(x: f32) -> f32 { return mod289_1(((x * 34.0) + 10.0) * x); }
1872
+
1873
+ fn taylorInvSqrt_4(r: vec4f) -> vec4f { return 1.79284291400159 - 0.85373472095314 * r; }
1874
+
1875
+ fn taylorInvSqrt_1(r: f32) -> f32 { return 1.79284291400159 - 0.85373472095314 * r; }
1876
+
1877
+ fn grad4(j: f32, ip: vec4f) -> vec4f {
1878
+ let ones = vec4f(1.0, 1.0, 1.0, -1.0);
1879
+ var p: vec4f;
1880
+ var s: vec4f;
1881
+
1882
+ p = vec4f(
1883
+ floor(fract(vec3f(j) * ip.xyz) * 7.0) * ip.z - 1.0,
1884
+ 0.0
1885
+ );
1886
+ p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
1887
+ s = select(vec4f(0.0), vec4f(1.0), p < vec4f(0.0));
1888
+ p = vec4f(p.xyz + (s.xyz * 2.0 - 1.0) * s.www, p.w);
1889
+
1890
+ return p;
1891
+ }
1892
+
1893
+ // (sqrt(5) - 1)/4 = F4
1894
+ const F4: f32 = 0.309016994374947451;
1895
+
1896
+ fn SimplexNoise4D(v: vec4f) -> f32 {
1897
+ let C = vec4f(
1898
+ 0.138196601125011, // (5 - sqrt(5))/20 G4
1899
+ 0.276393202250021, // 2 * G4
1900
+ 0.414589803375032, // 3 * G4
1901
+ -0.447213595499958 // -1 + 4 * G4
1902
+ );
1903
+
1904
+ // First corner
1905
+ var i = floor(v + dot(v, vec4f(F4)));
1906
+ let x0 = v - i + dot(i, C.xxxx);
1907
+
1908
+ // Other corners
1909
+ // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
1910
+ var i0: vec4f;
1911
+ let isX = step(x0.yzw, x0.xxx);
1912
+ let isYZ = step(x0.zww, x0.yyz);
1913
+ i0.x = isX.x + isX.y + isX.z;
1914
+ i0 = vec4f(i0.x, 1.0 - isX);
1915
+ i0.y = i0.y + isYZ.x + isYZ.y;
1916
+ i0 = vec4f(i0.x, i0.y, i0.zw + 1.0 - isYZ.xy);
1917
+ i0.z = i0.z + isYZ.z;
1918
+ i0.w = i0.w + 1.0 - isYZ.z;
1919
+
1920
+ // i0 now contains the unique values 0,1,2,3 in each channel
1921
+ let i3 = clamp(i0, vec4f(0.0), vec4f(1.0));
1922
+ let i2 = clamp(i0 - 1.0, vec4f(0.0), vec4f(1.0));
1923
+ let i1 = clamp(i0 - 2.0, vec4f(0.0), vec4f(1.0));
1924
+
1925
+ let x1 = x0 - i1 + C.xxxx;
1926
+ let x2 = x0 - i2 + C.yyyy;
1927
+ let x3 = x0 - i3 + C.zzzz;
1928
+ let x4 = x0 + C.wwww;
1929
+
1930
+ // Permutations
1931
+ i = mod289_4(i);
1932
+ let j0 = permute_1(permute_1(permute_1(permute_1(i.w) + i.z) + i.y) + i.x);
1933
+ let j1 = permute_4(permute_4(permute_4(permute_4(
1934
+ i.w + vec4f(i1.w, i2.w, i3.w, 1.0))
1935
+ + i.z + vec4f(i1.z, i2.z, i3.z, 1.0))
1936
+ + i.y + vec4f(i1.y, i2.y, i3.y, 1.0))
1937
+ + i.x + vec4f(i1.x, i2.x, i3.x, 1.0));
1938
+
1939
+ // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
1940
+ // 7*7*6 = 294, which is close to the ring size 17*17 = 289.
1941
+ let ip = vec4f(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
1942
+
1943
+ var p0 = grad4(j0, ip);
1944
+ var p1 = grad4(j1.x, ip);
1945
+ var p2 = grad4(j1.y, ip);
1946
+ var p3 = grad4(j1.z, ip);
1947
+ var p4 = grad4(j1.w, ip);
1948
+
1949
+ // Normalise gradients
1950
+ let norm = taylorInvSqrt_4(vec4f(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
1951
+ p0 = p0 * norm.x;
1952
+ p1 = p1 * norm.y;
1953
+ p2 = p2 * norm.z;
1954
+ p3 = p3 * norm.w;
1955
+ p4 = p4 * taylorInvSqrt_1(dot(p4, p4));
1956
+
1957
+ // Mix contributions from the five corners
1958
+ var m0 = max(0.6 - vec3f(dot(x0, x0), dot(x1, x1), dot(x2, x2)), vec3f(0.0));
1959
+ var m1 = max(0.6 - vec2f(dot(x3, x3), dot(x4, x4)), vec2f(0.0));
1960
+ m0 = m0 * m0;
1961
+ m1 = m1 * m1;
1962
+ return 49.0 * (dot(m0 * m0, vec3f(dot(p0, x0), dot(p1, x1), dot(p2, x2)))
1963
+ + dot(m1 * m1, vec2f(dot(p3, x3), dot(p4, x4))));
1964
+ }
1965
+
1966
+ fn SimplexNoise3D(v: vec3f) -> f32 {
1967
+ let C = vec2f(1.0 / 6.0, 1.0 / 3.0);
1968
+ let D = vec4f(0.0, 0.5, 1.0, 2.0);
1969
+
1970
+ // First corner
1971
+ var i = floor(v + dot(v, C.yyy));
1972
+ let x0 = v - i + dot(i, C.xxx);
1973
+
1974
+ // Other corners
1975
+ let g = step(x0.yzx, x0.xyz);
1976
+ let l = 1.0 - g;
1977
+ let i1 = min(g.xyz, l.zxy);
1978
+ let i2 = max(g.xyz, l.zxy);
1979
+
1980
+ let x1 = x0 - i1 + C.xxx;
1981
+ let x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
1982
+ let x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
1983
+
1984
+ // Permutations
1985
+ i = mod289_3(i);
1986
+ let p = permute_4(permute_4(permute_4(
1987
+ i.z + vec4f(0.0, i1.z, i2.z, 1.0))
1988
+ + i.y + vec4f(0.0, i1.y, i2.y, 1.0))
1989
+ + i.x + vec4f(0.0, i1.x, i2.x, 1.0));
1990
+
1991
+ // Gradients: 7x7 points over a square, mapped onto an octahedron.
1992
+ // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
1993
+ let n_ = 0.142857142857; // 1.0/7.0
1994
+ let ns = n_ * D.wyz - D.xzx;
1995
+
1996
+ let j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
1997
+
1998
+ let x_ = floor(j * ns.z);
1999
+ let y_ = floor(j - 7.0 * x_); // mod(j,N)
2000
+
2001
+ let x = x_ * ns.x + ns.yyyy;
2002
+ let y = y_ * ns.x + ns.yyyy;
2003
+ let h = 1.0 - abs(x) - abs(y);
2004
+
2005
+ let b0 = vec4f(x.xy, y.xy);
2006
+ let b1 = vec4f(x.zw, y.zw);
2007
+
2008
+ let s0 = floor(b0) * 2.0 + 1.0;
2009
+ let s1 = floor(b1) * 2.0 + 1.0;
2010
+ let sh = -step(h, vec4f(0.0));
2011
+
2012
+ let a0 = b0.xzyw + s0.xzyw * sh.xxyy;
2013
+ let a1 = b1.xzyw + s1.xzyw * sh.zzww;
2014
+
2015
+ var p0 = vec3f(a0.xy, h.x);
2016
+ var p1 = vec3f(a0.zw, h.y);
2017
+ var p2 = vec3f(a1.xy, h.z);
2018
+ var p3 = vec3f(a1.zw, h.w);
2019
+
2020
+ // Normalise gradients
2021
+ let norm = taylorInvSqrt_4(vec4f(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
2022
+ p0 = p0 * norm.x;
2023
+ p1 = p1 * norm.y;
2024
+ p2 = p2 * norm.z;
2025
+ p3 = p3 * norm.w;
2026
+
2027
+ // Mix final noise value
2028
+ var m = max(0.5 - vec4f(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec4f(0.0));
2029
+ m = m * m;
2030
+ return 105.0 * dot(m * m, vec4f(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
2031
+ }
2032
+ `;
2033
+ }
1206
2034
  // src/shaders/utils.ts
1207
2035
  function generateUtilsFunction() {
1208
2036
  return `