@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.
- package/dist/ReactGpuShader.d.ts +18 -0
- package/dist/ReactGpuShader.d.ts.map +1 -0
- package/dist/example/examples/mandelbrot.d.ts +2 -0
- package/dist/example/examples/mandelbrot.d.ts.map +1 -0
- package/dist/example/examples/mandelbrot2.d.ts +2 -0
- package/dist/example/examples/mandelbrot2.d.ts.map +1 -0
- package/dist/example/examples/webgl.d.ts +2 -0
- package/dist/example/examples/webgl.d.ts.map +1 -0
- package/dist/example/examples/webgpu.d.ts +2 -0
- package/dist/example/examples/webgpu.d.ts.map +1 -0
- package/dist/example/frontend.d.ts.map +1 -1
- package/dist/hooks/useWebGPU.d.ts +20 -0
- package/dist/hooks/useWebGPU.d.ts.map +1 -0
- package/dist/index.cjs +882 -54
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +882 -54
- package/dist/shaders/color-palette-gpu.d.ts +2 -0
- package/dist/shaders/color-palette-gpu.d.ts.map +1 -0
- package/dist/shaders/distortion-ripple-gpu.d.ts +16 -0
- package/dist/shaders/distortion-ripple-gpu.d.ts.map +1 -0
- package/dist/shaders/scene-circles-gpu.d.ts +21 -0
- package/dist/shaders/scene-circles-gpu.d.ts.map +1 -0
- package/dist/shaders/simplex-noise-gpu.d.ts +2 -0
- package/dist/shaders/simplex-noise-gpu.d.ts.map +1 -0
- package/package.json +2 -1
- package/dist/example/glsl/cartesian-to-polar.glsl.d.ts +0 -2
- package/dist/example/glsl/cartesian-to-polar.glsl.d.ts.map +0 -1
- package/dist/example/glsl/circles.glsl.d.ts +0 -3
- package/dist/example/glsl/circles.glsl.d.ts.map +0 -1
- package/dist/example/glsl/noise.glsl.d.ts +0 -3
- package/dist/example/glsl/noise.glsl.d.ts.map +0 -1
- package/dist/example/glsl/palette.glsl.d.ts +0 -2
- package/dist/example/glsl/palette.glsl.d.ts.map +0 -1
- package/dist/example/glsl/texture-demo.glsl.d.ts +0 -2
- package/dist/example/glsl/texture-demo.glsl.d.ts.map +0 -1
- package/dist/example/lib/logger.d.ts +0 -2
- package/dist/example/lib/logger.d.ts.map +0 -1
- package/dist/example/shader.d.ts +0 -2
- 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/
|
|
255
|
+
// src/ReactGpuShader.tsx
|
|
251
256
|
var import_react3 = require("react");
|
|
252
257
|
|
|
253
|
-
// src/hooks/
|
|
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
|
|
1022
|
+
function isVec22(value) {
|
|
444
1023
|
return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
|
|
445
1024
|
}
|
|
446
|
-
function
|
|
1025
|
+
function isVec32(value) {
|
|
447
1026
|
return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
|
|
448
1027
|
}
|
|
449
|
-
function
|
|
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
|
|
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) ||
|
|
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 (
|
|
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 (
|
|
1060
|
+
} else if (isVec42(value)) {
|
|
482
1061
|
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
483
|
-
} else if (
|
|
1062
|
+
} else if (isVec32(value)) {
|
|
484
1063
|
gl.uniform3f(location, value[0], value[1], value[2]);
|
|
485
|
-
} else if (
|
|
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 (
|
|
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 (
|
|
1120
|
+
if (isVec42(value)) {
|
|
542
1121
|
return "vec4";
|
|
543
1122
|
}
|
|
544
|
-
if (
|
|
1123
|
+
if (isVec32(value)) {
|
|
545
1124
|
return "vec3";
|
|
546
1125
|
}
|
|
547
|
-
if (
|
|
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 =
|
|
620
|
-
const stateRef =
|
|
621
|
-
const animationFrameRef =
|
|
622
|
-
const elapsedTimeRef =
|
|
623
|
-
const lastFrameTimeRef =
|
|
624
|
-
const mouseRef =
|
|
625
|
-
const mouseNormalizedRef =
|
|
626
|
-
const mouseLeftDownRef =
|
|
627
|
-
const canvasRectRef =
|
|
628
|
-
const contextLostRef =
|
|
629
|
-
const uniformsRef =
|
|
630
|
-
const onErrorRef =
|
|
631
|
-
const onFrameRef =
|
|
632
|
-
const onClickRef =
|
|
633
|
-
const onMouseDownRef =
|
|
634
|
-
const onMouseUpRef =
|
|
635
|
-
const onMouseMoveRef =
|
|
636
|
-
const onMouseWheelRef =
|
|
637
|
-
const timeScaleRef =
|
|
638
|
-
const vertexRef =
|
|
639
|
-
const fragmentRef =
|
|
640
|
-
const dprRef =
|
|
641
|
-
const defaultUniformsRef =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
1455
|
+
var DEFAULT_CONTAINER_STYLE2 = {
|
|
877
1456
|
position: "relative",
|
|
878
1457
|
width: "100%",
|
|
879
1458
|
height: "100%"
|
|
880
1459
|
};
|
|
881
|
-
var
|
|
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] =
|
|
901
|
-
const handleError =
|
|
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
|
-
|
|
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 =
|
|
1500
|
+
const containerStyle = import_react5.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE2 : DEFAULT_CONTAINER_STYLE2, [fullscreen]);
|
|
922
1501
|
if (error) {
|
|
923
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1525
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("canvas", {
|
|
947
1526
|
ref: canvasRef,
|
|
948
1527
|
className,
|
|
949
|
-
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 `
|