@fjandin/react-shader 0.0.20 → 0.0.30
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/README.md +21 -213
- package/dist/example/frontend.d.ts.map +1 -1
- package/dist/hooks/useWebGPU.d.ts.map +1 -1
- package/dist/index.cjs +24 -1007
- package/dist/index.d.ts +1 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1007
- package/dist/types.d.ts +0 -28
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/ReactShader.d.ts +0 -3
- package/dist/ReactShader.d.ts.map +0 -1
- package/dist/example/examples/webgl.d.ts +0 -2
- package/dist/example/examples/webgl.d.ts.map +0 -1
- package/dist/hooks/useWebGL.d.ts +0 -20
- package/dist/hooks/useWebGL.d.ts.map +0 -1
- package/dist/shaders/color-palette.d.ts +0 -2
- package/dist/shaders/color-palette.d.ts.map +0 -1
- package/dist/shaders/distortion-ripple.d.ts +0 -16
- package/dist/shaders/distortion-ripple.d.ts.map +0 -1
- package/dist/shaders/scene-circles.d.ts +0 -21
- package/dist/shaders/scene-circles.d.ts.map +0 -1
- package/dist/shaders/simplex-noise.d.ts +0 -2
- package/dist/shaders/simplex-noise.d.ts.map +0 -1
- package/dist/shaders/utils.d.ts +0 -2
- package/dist/shaders/utils.d.ts.map +0 -1
- package/dist/utils/shader.d.ts +0 -6
- package/dist/utils/shader.d.ts.map +0 -1
- package/dist/utils/textures.d.ts +0 -23
- package/dist/utils/textures.d.ts.map +0 -1
- package/dist/utils/uniforms.d.ts +0 -12
- package/dist/utils/uniforms.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -30,16 +30,10 @@ var __export = (target, all) => {
|
|
|
30
30
|
var exports_src = {};
|
|
31
31
|
__export(exports_src, {
|
|
32
32
|
useAudio: () => useAudio,
|
|
33
|
-
generateUtilsFunction: () => generateUtilsFunction,
|
|
34
33
|
generateSimplexNoiseFunctionGpu: () => generateSimplexNoiseFunctionGpu,
|
|
35
|
-
generateSimplexNoiseFunction: () => generateSimplexNoiseFunction,
|
|
36
34
|
generateSceneCirclesFunctionGpu: () => generateSceneCirclesFunctionGpu,
|
|
37
|
-
generateSceneCirclesFunction: () => generateSceneCirclesFunction,
|
|
38
35
|
generateDistortionRippleFunctionGpu: () => generateDistortionRippleFunctionGpu,
|
|
39
|
-
generateDistortionRippleFunction: () => generateDistortionRippleFunction,
|
|
40
36
|
generateColorPaletteFunctionGpu: () => generateColorPaletteFunctionGpu,
|
|
41
|
-
generateColorPaletteFunction: () => generateColorPaletteFunction,
|
|
42
|
-
ReactShader: () => ReactShader,
|
|
43
37
|
ReactGpuShader: () => ReactGpuShader
|
|
44
38
|
});
|
|
45
39
|
module.exports = __toCommonJS(exports_src);
|
|
@@ -394,10 +388,11 @@ function createStorageBuffer(device, name, binding, data) {
|
|
|
394
388
|
packingArray[off + 3] = data[i][3];
|
|
395
389
|
}
|
|
396
390
|
device.queue.writeBuffer(buffer, 0, packingArray);
|
|
397
|
-
return { name, binding, buffer, currentLength: length,
|
|
391
|
+
return { name, binding, buffer, currentLength: length, packingArray };
|
|
398
392
|
}
|
|
399
393
|
function packAndUploadStorageBuffer(device, entry, data) {
|
|
400
394
|
const arr = entry.packingArray;
|
|
395
|
+
arr.fill(0);
|
|
401
396
|
for (let i = 0;i < data.length; i++) {
|
|
402
397
|
const off = i * 4;
|
|
403
398
|
arr[off] = data[i][0];
|
|
@@ -405,8 +400,7 @@ function packAndUploadStorageBuffer(device, entry, data) {
|
|
|
405
400
|
arr[off + 2] = data[i][2];
|
|
406
401
|
arr[off + 3] = data[i][3];
|
|
407
402
|
}
|
|
408
|
-
|
|
409
|
-
device.queue.writeBuffer(entry.buffer, 0, arr, 0, uploadLength * 4);
|
|
403
|
+
device.queue.writeBuffer(entry.buffer, 0, arr);
|
|
410
404
|
}
|
|
411
405
|
function rebuildBindGroup(state) {
|
|
412
406
|
const entries = [{ binding: 0, resource: { buffer: state.uniformBuffer } }];
|
|
@@ -523,18 +517,7 @@ async function initializeWebGPU(canvas, fragmentSource, customUniforms, storageB
|
|
|
523
517
|
uniformBindGroup,
|
|
524
518
|
uniformLayout,
|
|
525
519
|
bindGroupLayout,
|
|
526
|
-
storageBuffers
|
|
527
|
-
renderPassDescriptor: {
|
|
528
|
-
colorAttachments: [
|
|
529
|
-
{
|
|
530
|
-
view: undefined,
|
|
531
|
-
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
532
|
-
loadOp: "clear",
|
|
533
|
-
storeOp: "store"
|
|
534
|
-
}
|
|
535
|
-
]
|
|
536
|
-
},
|
|
537
|
-
submitArray: [null]
|
|
520
|
+
storageBuffers
|
|
538
521
|
};
|
|
539
522
|
}
|
|
540
523
|
function cleanupWebGPU(state) {
|
|
@@ -552,7 +535,6 @@ function useWebGPU(options) {
|
|
|
552
535
|
const lastFrameTimeRef = import_react2.useRef(0);
|
|
553
536
|
const mouseRef = import_react2.useRef([0, 0]);
|
|
554
537
|
const mouseNormalizedRef = import_react2.useRef([0, 0]);
|
|
555
|
-
const resolutionRef = import_react2.useRef([0, 0]);
|
|
556
538
|
const mouseLeftDownRef = import_react2.useRef(false);
|
|
557
539
|
const canvasRectRef = import_react2.useRef(null);
|
|
558
540
|
const onErrorRef = import_react2.useRef(options.onError);
|
|
@@ -625,18 +607,16 @@ function useWebGPU(options) {
|
|
|
625
607
|
const allValues = allValuesRef.current;
|
|
626
608
|
allValues.iTime = elapsedTimeRef.current;
|
|
627
609
|
allValues.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
|
|
628
|
-
|
|
629
|
-
resolutionRef.current[1] = canvas.height;
|
|
630
|
-
allValues.iResolution = resolutionRef.current;
|
|
610
|
+
allValues.iResolution = [canvas.width, canvas.height];
|
|
631
611
|
allValues.iMouse = mouseRef.current;
|
|
632
612
|
allValues.iMouseNormalized = mouseNormalizedRef.current;
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
allValues[name] = customs[name];
|
|
613
|
+
if (uniformsRef.current) {
|
|
614
|
+
for (const [name, value] of Object.entries(uniformsRef.current)) {
|
|
615
|
+
allValues[name] = value;
|
|
637
616
|
}
|
|
638
617
|
}
|
|
639
618
|
const uniformData = uniformDataRef.current;
|
|
619
|
+
uniformData.fill(0);
|
|
640
620
|
for (const field of uniformLayout.fields) {
|
|
641
621
|
const value = allValues[field.name];
|
|
642
622
|
if (value === undefined) {
|
|
@@ -651,20 +631,18 @@ function useWebGPU(options) {
|
|
|
651
631
|
if (!data)
|
|
652
632
|
continue;
|
|
653
633
|
const requiredLength = Math.max(data.length, 1);
|
|
654
|
-
if (requiredLength
|
|
655
|
-
const allocLength = Math.max(Math.ceil(requiredLength * 1.5), 1);
|
|
634
|
+
if (requiredLength !== entry.currentLength) {
|
|
656
635
|
entry.buffer.destroy();
|
|
657
|
-
const byteSize =
|
|
636
|
+
const byteSize = requiredLength * 16;
|
|
658
637
|
entry.buffer = device.createBuffer({
|
|
659
638
|
label: `storage: ${entry.name}`,
|
|
660
639
|
size: byteSize,
|
|
661
640
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
662
641
|
});
|
|
663
|
-
entry.packingArray = new Float32Array(
|
|
664
|
-
entry.currentLength =
|
|
642
|
+
entry.packingArray = new Float32Array(requiredLength * 4);
|
|
643
|
+
entry.currentLength = requiredLength;
|
|
665
644
|
needsBindGroupRebuild = true;
|
|
666
645
|
}
|
|
667
|
-
entry.dataLength = data.length;
|
|
668
646
|
packAndUploadStorageBuffer(device, entry, data);
|
|
669
647
|
}
|
|
670
648
|
if (needsBindGroupRebuild) {
|
|
@@ -672,14 +650,21 @@ function useWebGPU(options) {
|
|
|
672
650
|
}
|
|
673
651
|
const commandEncoder = device.createCommandEncoder();
|
|
674
652
|
const textureView = context.getCurrentTexture().createView();
|
|
675
|
-
|
|
676
|
-
|
|
653
|
+
const renderPass = commandEncoder.beginRenderPass({
|
|
654
|
+
colorAttachments: [
|
|
655
|
+
{
|
|
656
|
+
view: textureView,
|
|
657
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
658
|
+
loadOp: "clear",
|
|
659
|
+
storeOp: "store"
|
|
660
|
+
}
|
|
661
|
+
]
|
|
662
|
+
});
|
|
677
663
|
renderPass.setPipeline(pipeline);
|
|
678
664
|
renderPass.setBindGroup(0, state.uniformBindGroup);
|
|
679
665
|
renderPass.draw(3);
|
|
680
666
|
renderPass.end();
|
|
681
|
-
|
|
682
|
-
device.queue.submit(state.submitArray);
|
|
667
|
+
device.queue.submit([commandEncoder.finish()]);
|
|
683
668
|
animationFrameRef.current = requestAnimationFrame(render);
|
|
684
669
|
}, []);
|
|
685
670
|
import_react2.useEffect(() => {
|
|
@@ -874,721 +859,6 @@ function ReactGpuShader({
|
|
|
874
859
|
style: CANVAS_STYLE
|
|
875
860
|
}, undefined, false, undefined, this);
|
|
876
861
|
}
|
|
877
|
-
// src/ReactShader.tsx
|
|
878
|
-
var import_react5 = require("react");
|
|
879
|
-
|
|
880
|
-
// src/hooks/useWebGL.ts
|
|
881
|
-
var import_react4 = require("react");
|
|
882
|
-
|
|
883
|
-
// src/utils/shader.ts
|
|
884
|
-
function compileShader(gl, type, source) {
|
|
885
|
-
const shader = gl.createShader(type);
|
|
886
|
-
if (!shader) {
|
|
887
|
-
throw new Error(`Failed to create shader of type ${type === gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT"}`);
|
|
888
|
-
}
|
|
889
|
-
gl.shaderSource(shader, source);
|
|
890
|
-
gl.compileShader(shader);
|
|
891
|
-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
892
|
-
const info = gl.getShaderInfoLog(shader);
|
|
893
|
-
gl.deleteShader(shader);
|
|
894
|
-
const shaderType = type === gl.VERTEX_SHADER ? "Vertex" : "Fragment";
|
|
895
|
-
throw new Error(`${shaderType} shader compilation failed:
|
|
896
|
-
${info}`);
|
|
897
|
-
}
|
|
898
|
-
return shader;
|
|
899
|
-
}
|
|
900
|
-
function createProgram(gl, vertexShader, fragmentShader) {
|
|
901
|
-
const program = gl.createProgram();
|
|
902
|
-
if (!program) {
|
|
903
|
-
throw new Error("Failed to create WebGL program");
|
|
904
|
-
}
|
|
905
|
-
gl.attachShader(program, vertexShader);
|
|
906
|
-
gl.attachShader(program, fragmentShader);
|
|
907
|
-
gl.linkProgram(program);
|
|
908
|
-
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
909
|
-
const info = gl.getProgramInfoLog(program);
|
|
910
|
-
gl.deleteProgram(program);
|
|
911
|
-
throw new Error(`Program linking failed:
|
|
912
|
-
${info}`);
|
|
913
|
-
}
|
|
914
|
-
return program;
|
|
915
|
-
}
|
|
916
|
-
function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
917
|
-
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
918
|
-
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
919
|
-
try {
|
|
920
|
-
return createProgram(gl, vertexShader, fragmentShader);
|
|
921
|
-
} finally {
|
|
922
|
-
gl.deleteShader(vertexShader);
|
|
923
|
-
gl.deleteShader(fragmentShader);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// src/utils/textures.ts
|
|
928
|
-
function isTextureSource(value) {
|
|
929
|
-
if (typeof window === "undefined")
|
|
930
|
-
return false;
|
|
931
|
-
return value instanceof HTMLImageElement || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement || typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap || value instanceof ImageData || typeof OffscreenCanvas !== "undefined" && value instanceof OffscreenCanvas;
|
|
932
|
-
}
|
|
933
|
-
function isTextureOptions(value) {
|
|
934
|
-
return typeof value === "object" && value !== null && "source" in value && isTextureSource(value.source);
|
|
935
|
-
}
|
|
936
|
-
function isTexture(value) {
|
|
937
|
-
return isTextureSource(value) || isTextureOptions(value);
|
|
938
|
-
}
|
|
939
|
-
function getWrapMode(gl, wrap) {
|
|
940
|
-
switch (wrap) {
|
|
941
|
-
case "repeat":
|
|
942
|
-
return gl.REPEAT;
|
|
943
|
-
case "mirror":
|
|
944
|
-
return gl.MIRRORED_REPEAT;
|
|
945
|
-
default:
|
|
946
|
-
return gl.CLAMP_TO_EDGE;
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
function getMinFilter(gl, filter) {
|
|
950
|
-
switch (filter) {
|
|
951
|
-
case "nearest":
|
|
952
|
-
return gl.NEAREST;
|
|
953
|
-
case "mipmap":
|
|
954
|
-
return gl.LINEAR_MIPMAP_LINEAR;
|
|
955
|
-
default:
|
|
956
|
-
return gl.LINEAR;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
function getMagFilter(gl, filter) {
|
|
960
|
-
switch (filter) {
|
|
961
|
-
case "nearest":
|
|
962
|
-
return gl.NEAREST;
|
|
963
|
-
default:
|
|
964
|
-
return gl.LINEAR;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
function isPowerOfTwo(value) {
|
|
968
|
-
return (value & value - 1) === 0 && value !== 0;
|
|
969
|
-
}
|
|
970
|
-
function getSourceDimensions(source) {
|
|
971
|
-
if (source instanceof HTMLImageElement) {
|
|
972
|
-
return { width: source.naturalWidth, height: source.naturalHeight };
|
|
973
|
-
}
|
|
974
|
-
if (source instanceof HTMLVideoElement) {
|
|
975
|
-
return { width: source.videoWidth, height: source.videoHeight };
|
|
976
|
-
}
|
|
977
|
-
if (source instanceof ImageData) {
|
|
978
|
-
return { width: source.width, height: source.height };
|
|
979
|
-
}
|
|
980
|
-
return { width: source.width, height: source.height };
|
|
981
|
-
}
|
|
982
|
-
function createTextureManager(gl) {
|
|
983
|
-
const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
984
|
-
return {
|
|
985
|
-
cache: new Map,
|
|
986
|
-
nextUnit: 0,
|
|
987
|
-
maxUnits
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
function createTexture(gl, source, options = {}) {
|
|
991
|
-
const texture = gl.createTexture();
|
|
992
|
-
if (!texture) {
|
|
993
|
-
throw new Error("Failed to create WebGL texture");
|
|
994
|
-
}
|
|
995
|
-
const { wrapS = "clamp", wrapT = "clamp", minFilter = "linear", magFilter = "linear", flipY = true } = options;
|
|
996
|
-
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
997
|
-
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
998
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
999
|
-
const { width, height } = getSourceDimensions(source);
|
|
1000
|
-
const pot = isPowerOfTwo(width) && isPowerOfTwo(height);
|
|
1001
|
-
const isWebGL2 = "texStorage2D" in gl;
|
|
1002
|
-
const actualMinFilter = minFilter === "mipmap" && (pot || isWebGL2) ? minFilter : minFilter === "mipmap" ? "linear" : minFilter;
|
|
1003
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, getWrapMode(gl, wrapS));
|
|
1004
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, getWrapMode(gl, wrapT));
|
|
1005
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, getMinFilter(gl, actualMinFilter));
|
|
1006
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, getMagFilter(gl, magFilter));
|
|
1007
|
-
if (actualMinFilter === "mipmap") {
|
|
1008
|
-
gl.generateMipmap(gl.TEXTURE_2D);
|
|
1009
|
-
}
|
|
1010
|
-
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1011
|
-
return texture;
|
|
1012
|
-
}
|
|
1013
|
-
function updateTexture(gl, texture, source, flipY = true) {
|
|
1014
|
-
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
1015
|
-
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
1016
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
1017
|
-
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1018
|
-
}
|
|
1019
|
-
function needsVideoUpdate(source) {
|
|
1020
|
-
if (!(source instanceof HTMLVideoElement))
|
|
1021
|
-
return false;
|
|
1022
|
-
return !source.paused && !source.ended && source.readyState >= 2;
|
|
1023
|
-
}
|
|
1024
|
-
function bindTextureUniform(gl, program, name, value, manager, locationCache) {
|
|
1025
|
-
const source = isTextureOptions(value) ? value.source : value;
|
|
1026
|
-
const options = isTextureOptions(value) ? value : {};
|
|
1027
|
-
let entry = manager.cache.get(name);
|
|
1028
|
-
if (entry && entry.source !== source) {
|
|
1029
|
-
gl.deleteTexture(entry.texture);
|
|
1030
|
-
entry = undefined;
|
|
1031
|
-
}
|
|
1032
|
-
if (!entry) {
|
|
1033
|
-
if (manager.nextUnit >= manager.maxUnits) {
|
|
1034
|
-
console.warn(`Maximum texture units (${manager.maxUnits}) exceeded for uniform "${name}"`);
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
const texture = createTexture(gl, source, options);
|
|
1038
|
-
entry = {
|
|
1039
|
-
texture,
|
|
1040
|
-
unit: manager.nextUnit++,
|
|
1041
|
-
source
|
|
1042
|
-
};
|
|
1043
|
-
manager.cache.set(name, entry);
|
|
1044
|
-
}
|
|
1045
|
-
if (needsVideoUpdate(source)) {
|
|
1046
|
-
const flipY = isTextureOptions(value) ? value.flipY ?? true : true;
|
|
1047
|
-
updateTexture(gl, entry.texture, source, flipY);
|
|
1048
|
-
}
|
|
1049
|
-
gl.activeTexture(gl.TEXTURE0 + entry.unit);
|
|
1050
|
-
gl.bindTexture(gl.TEXTURE_2D, entry.texture);
|
|
1051
|
-
let location = locationCache.get(name);
|
|
1052
|
-
if (location === undefined) {
|
|
1053
|
-
location = gl.getUniformLocation(program, name);
|
|
1054
|
-
locationCache.set(name, location);
|
|
1055
|
-
}
|
|
1056
|
-
if (location !== null) {
|
|
1057
|
-
gl.uniform1i(location, entry.unit);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
function cleanupTextures(gl, manager) {
|
|
1061
|
-
for (const entry of manager.cache.values()) {
|
|
1062
|
-
gl.deleteTexture(entry.texture);
|
|
1063
|
-
}
|
|
1064
|
-
manager.cache.clear();
|
|
1065
|
-
manager.nextUnit = 0;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// src/utils/uniforms.ts
|
|
1069
|
-
var MAX_ARRAY_LENGTH = 100;
|
|
1070
|
-
function isVec22(value) {
|
|
1071
|
-
return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
|
|
1072
|
-
}
|
|
1073
|
-
function isVec32(value) {
|
|
1074
|
-
return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
|
|
1075
|
-
}
|
|
1076
|
-
function isVec42(value) {
|
|
1077
|
-
return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
|
|
1078
|
-
}
|
|
1079
|
-
function isFloatArray(value) {
|
|
1080
|
-
return Array.isArray(value) && value.length > 4 && typeof value[0] === "number";
|
|
1081
|
-
}
|
|
1082
|
-
function isVec2Array(value) {
|
|
1083
|
-
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 2;
|
|
1084
|
-
}
|
|
1085
|
-
function isVec3Array(value) {
|
|
1086
|
-
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
|
|
1087
|
-
}
|
|
1088
|
-
function isVec4Array(value) {
|
|
1089
|
-
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
|
|
1090
|
-
}
|
|
1091
|
-
function isArrayUniform(value) {
|
|
1092
|
-
return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
|
|
1093
|
-
}
|
|
1094
|
-
function setUniform(gl, location, value) {
|
|
1095
|
-
if (location === null) {
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
if (typeof value === "number") {
|
|
1099
|
-
gl.uniform1f(location, value);
|
|
1100
|
-
} else if (isVec4Array(value)) {
|
|
1101
|
-
gl.uniform4fv(location, value.flat());
|
|
1102
|
-
} else if (isVec3Array(value)) {
|
|
1103
|
-
gl.uniform3fv(location, value.flat());
|
|
1104
|
-
} else if (isVec2Array(value)) {
|
|
1105
|
-
gl.uniform2fv(location, value.flat());
|
|
1106
|
-
} else if (isFloatArray(value)) {
|
|
1107
|
-
gl.uniform1fv(location, value);
|
|
1108
|
-
} else if (isVec42(value)) {
|
|
1109
|
-
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
1110
|
-
} else if (isVec32(value)) {
|
|
1111
|
-
gl.uniform3f(location, value[0], value[1], value[2]);
|
|
1112
|
-
} else if (isVec22(value)) {
|
|
1113
|
-
gl.uniform2f(location, value[0], value[1]);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
function getUniformLocation(gl, program, name) {
|
|
1117
|
-
return gl.getUniformLocation(program, name);
|
|
1118
|
-
}
|
|
1119
|
-
function setUniforms(gl, program, uniforms, locationCache, textureManager) {
|
|
1120
|
-
for (const [name, value] of Object.entries(uniforms)) {
|
|
1121
|
-
if (isTexture(value)) {
|
|
1122
|
-
if (textureManager) {
|
|
1123
|
-
bindTextureUniform(gl, program, name, value, textureManager, locationCache);
|
|
1124
|
-
}
|
|
1125
|
-
continue;
|
|
1126
|
-
}
|
|
1127
|
-
let location = locationCache.get(name);
|
|
1128
|
-
if (location === undefined) {
|
|
1129
|
-
location = getUniformLocation(gl, program, name);
|
|
1130
|
-
locationCache.set(name, location);
|
|
1131
|
-
}
|
|
1132
|
-
setUniform(gl, location, value);
|
|
1133
|
-
if (isArrayUniform(value)) {
|
|
1134
|
-
const countName = `${name}_count`;
|
|
1135
|
-
let countLocation = locationCache.get(countName);
|
|
1136
|
-
if (countLocation === undefined) {
|
|
1137
|
-
countLocation = getUniformLocation(gl, program, countName);
|
|
1138
|
-
locationCache.set(countName, countLocation);
|
|
1139
|
-
}
|
|
1140
|
-
if (countLocation !== null) {
|
|
1141
|
-
gl.uniform1i(countLocation, value.length);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
function createUniformLocationCache() {
|
|
1147
|
-
return new Map;
|
|
1148
|
-
}
|
|
1149
|
-
function getUniformType(value) {
|
|
1150
|
-
if (isTexture(value)) {
|
|
1151
|
-
return "sampler2D";
|
|
1152
|
-
}
|
|
1153
|
-
if (typeof value === "number") {
|
|
1154
|
-
return "float";
|
|
1155
|
-
}
|
|
1156
|
-
if (isVec4Array(value)) {
|
|
1157
|
-
return `vec4[${MAX_ARRAY_LENGTH}]`;
|
|
1158
|
-
}
|
|
1159
|
-
if (isVec3Array(value)) {
|
|
1160
|
-
return `vec3[${MAX_ARRAY_LENGTH}]`;
|
|
1161
|
-
}
|
|
1162
|
-
if (isVec2Array(value)) {
|
|
1163
|
-
return `vec2[${MAX_ARRAY_LENGTH}]`;
|
|
1164
|
-
}
|
|
1165
|
-
if (isFloatArray(value)) {
|
|
1166
|
-
return `float[${MAX_ARRAY_LENGTH}]`;
|
|
1167
|
-
}
|
|
1168
|
-
if (isVec42(value)) {
|
|
1169
|
-
return "vec4";
|
|
1170
|
-
}
|
|
1171
|
-
if (isVec32(value)) {
|
|
1172
|
-
return "vec3";
|
|
1173
|
-
}
|
|
1174
|
-
if (isVec22(value)) {
|
|
1175
|
-
return "vec2";
|
|
1176
|
-
}
|
|
1177
|
-
return "float";
|
|
1178
|
-
}
|
|
1179
|
-
function generateUniformDeclarations(uniforms) {
|
|
1180
|
-
const lines = [];
|
|
1181
|
-
for (const [name, value] of Object.entries(uniforms)) {
|
|
1182
|
-
const type = getUniformType(value);
|
|
1183
|
-
if (type.includes("[")) {
|
|
1184
|
-
const [baseType, arrayPart] = type.split("[");
|
|
1185
|
-
lines.push(`uniform ${baseType} ${name}[${arrayPart};`);
|
|
1186
|
-
lines.push(`uniform int ${name}_count;`);
|
|
1187
|
-
} else {
|
|
1188
|
-
lines.push(`uniform ${type} ${name};`);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
return lines.join(`
|
|
1192
|
-
`);
|
|
1193
|
-
}
|
|
1194
|
-
var UNIFORM_MARKER = "// @UNIFORM_VALUES";
|
|
1195
|
-
function injectUniformDeclarations(shaderSource, customUniforms, defaultUniforms) {
|
|
1196
|
-
if (!shaderSource.includes(UNIFORM_MARKER)) {
|
|
1197
|
-
return shaderSource;
|
|
1198
|
-
}
|
|
1199
|
-
const allUniforms = { ...defaultUniforms, ...customUniforms };
|
|
1200
|
-
const declarations = generateUniformDeclarations(allUniforms);
|
|
1201
|
-
return shaderSource.replace(UNIFORM_MARKER, declarations);
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
// src/hooks/useWebGL.ts
|
|
1205
|
-
var DEFAULT_UNIFORM_TYPES = {
|
|
1206
|
-
iTime: 0,
|
|
1207
|
-
iMouse: [0, 0],
|
|
1208
|
-
iMouseNormalized: [0, 0],
|
|
1209
|
-
iMouseLeftDown: 0,
|
|
1210
|
-
iResolution: [0, 0]
|
|
1211
|
-
};
|
|
1212
|
-
function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
|
|
1213
|
-
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
|
1214
|
-
if (!gl) {
|
|
1215
|
-
throw new Error("WebGL not supported");
|
|
1216
|
-
}
|
|
1217
|
-
const processedVertex = injectUniformDeclarations(vertexSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
1218
|
-
const processedFragment = injectUniformDeclarations(fragmentSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
1219
|
-
const program = createShaderProgram(gl, processedVertex, processedFragment);
|
|
1220
|
-
const positionBuffer = gl.createBuffer();
|
|
1221
|
-
if (!positionBuffer) {
|
|
1222
|
-
throw new Error("Failed to create position buffer");
|
|
1223
|
-
}
|
|
1224
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
1225
|
-
const positions = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
|
|
1226
|
-
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
1227
|
-
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
|
|
1228
|
-
if (positionAttributeLocation === -1) {
|
|
1229
|
-
throw new Error('Vertex shader must have an "a_position" attribute');
|
|
1230
|
-
}
|
|
1231
|
-
return {
|
|
1232
|
-
gl,
|
|
1233
|
-
program,
|
|
1234
|
-
positionBuffer,
|
|
1235
|
-
positionAttributeLocation,
|
|
1236
|
-
uniformLocationCache: createUniformLocationCache(),
|
|
1237
|
-
textureManager: createTextureManager(gl)
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
function cleanupWebGL(gl, state) {
|
|
1241
|
-
cleanupTextures(gl, state.textureManager);
|
|
1242
|
-
gl.deleteBuffer(state.positionBuffer);
|
|
1243
|
-
gl.deleteProgram(state.program);
|
|
1244
|
-
}
|
|
1245
|
-
function useWebGL(options) {
|
|
1246
|
-
const canvasRef = import_react4.useRef(null);
|
|
1247
|
-
const stateRef = import_react4.useRef(null);
|
|
1248
|
-
const animationFrameRef = import_react4.useRef(0);
|
|
1249
|
-
const elapsedTimeRef = import_react4.useRef(0);
|
|
1250
|
-
const lastFrameTimeRef = import_react4.useRef(0);
|
|
1251
|
-
const mouseRef = import_react4.useRef([0, 0]);
|
|
1252
|
-
const mouseNormalizedRef = import_react4.useRef([0, 0]);
|
|
1253
|
-
const mouseLeftDownRef = import_react4.useRef(false);
|
|
1254
|
-
const canvasRectRef = import_react4.useRef(null);
|
|
1255
|
-
const contextLostRef = import_react4.useRef(false);
|
|
1256
|
-
const uniformsRef = import_react4.useRef(options.uniforms);
|
|
1257
|
-
const onErrorRef = import_react4.useRef(options.onError);
|
|
1258
|
-
const onFrameRef = import_react4.useRef(options.onFrame);
|
|
1259
|
-
const onClickRef = import_react4.useRef(options.onClick);
|
|
1260
|
-
const onMouseDownRef = import_react4.useRef(options.onMouseDown);
|
|
1261
|
-
const onMouseUpRef = import_react4.useRef(options.onMouseUp);
|
|
1262
|
-
const onMouseMoveRef = import_react4.useRef(options.onMouseMove);
|
|
1263
|
-
const onMouseWheelRef = import_react4.useRef(options.onMouseWheel);
|
|
1264
|
-
const timeScaleRef = import_react4.useRef(options.timeScale ?? 1);
|
|
1265
|
-
const vertexRef = import_react4.useRef(options.vertex);
|
|
1266
|
-
const fragmentRef = import_react4.useRef(options.fragment);
|
|
1267
|
-
const dprRef = import_react4.useRef(window.devicePixelRatio || 1);
|
|
1268
|
-
const defaultUniformsRef = import_react4.useRef({
|
|
1269
|
-
iTime: 0,
|
|
1270
|
-
iMouse: [0, 0],
|
|
1271
|
-
iMouseNormalized: [0, 0],
|
|
1272
|
-
iMouseLeftDown: 0,
|
|
1273
|
-
iResolution: [0, 0]
|
|
1274
|
-
});
|
|
1275
|
-
uniformsRef.current = options.uniforms;
|
|
1276
|
-
onErrorRef.current = options.onError;
|
|
1277
|
-
onFrameRef.current = options.onFrame;
|
|
1278
|
-
onClickRef.current = options.onClick;
|
|
1279
|
-
onMouseDownRef.current = options.onMouseDown;
|
|
1280
|
-
onMouseUpRef.current = options.onMouseUp;
|
|
1281
|
-
onMouseMoveRef.current = options.onMouseMove;
|
|
1282
|
-
onMouseWheelRef.current = options.onMouseWheel;
|
|
1283
|
-
timeScaleRef.current = options.timeScale ?? 1;
|
|
1284
|
-
vertexRef.current = options.vertex;
|
|
1285
|
-
fragmentRef.current = options.fragment;
|
|
1286
|
-
const render = import_react4.useCallback((time) => {
|
|
1287
|
-
if (contextLostRef.current)
|
|
1288
|
-
return;
|
|
1289
|
-
const state = stateRef.current;
|
|
1290
|
-
const canvas = canvasRef.current;
|
|
1291
|
-
if (!state || !canvas)
|
|
1292
|
-
return;
|
|
1293
|
-
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
1294
|
-
lastFrameTimeRef.current = time;
|
|
1295
|
-
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
1296
|
-
const { gl, program, positionAttributeLocation, uniformLocationCache, textureManager } = state;
|
|
1297
|
-
const elapsedTime = elapsedTimeRef.current;
|
|
1298
|
-
const dpr = dprRef.current;
|
|
1299
|
-
const displayWidth = canvas.clientWidth;
|
|
1300
|
-
const displayHeight = canvas.clientHeight;
|
|
1301
|
-
if (displayWidth === 0 || displayHeight === 0) {
|
|
1302
|
-
animationFrameRef.current = requestAnimationFrame(render);
|
|
1303
|
-
return;
|
|
1304
|
-
}
|
|
1305
|
-
const bufferWidth = Math.round(displayWidth * dpr);
|
|
1306
|
-
const bufferHeight = Math.round(displayHeight * dpr);
|
|
1307
|
-
if (canvas.width !== bufferWidth || canvas.height !== bufferHeight) {
|
|
1308
|
-
canvas.width = bufferWidth;
|
|
1309
|
-
canvas.height = bufferHeight;
|
|
1310
|
-
gl.viewport(0, 0, bufferWidth, bufferHeight);
|
|
1311
|
-
}
|
|
1312
|
-
gl.clearColor(0, 0, 0, 1);
|
|
1313
|
-
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
1314
|
-
gl.useProgram(program);
|
|
1315
|
-
gl.enableVertexAttribArray(positionAttributeLocation);
|
|
1316
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, state.positionBuffer);
|
|
1317
|
-
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
|
1318
|
-
const defaultUniforms = defaultUniformsRef.current;
|
|
1319
|
-
defaultUniforms.iTime = elapsedTime;
|
|
1320
|
-
defaultUniforms.iMouse = mouseRef.current;
|
|
1321
|
-
defaultUniforms.iMouseNormalized = mouseNormalizedRef.current;
|
|
1322
|
-
defaultUniforms.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
|
|
1323
|
-
defaultUniforms.iResolution = [canvas.width, canvas.height];
|
|
1324
|
-
setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache, textureManager);
|
|
1325
|
-
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
1326
|
-
if (onFrameRef.current) {
|
|
1327
|
-
onFrameRef.current({
|
|
1328
|
-
deltaTime,
|
|
1329
|
-
time: elapsedTime,
|
|
1330
|
-
resolution: [canvas.width, canvas.height],
|
|
1331
|
-
mouse: mouseRef.current,
|
|
1332
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1333
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1334
|
-
});
|
|
1335
|
-
}
|
|
1336
|
-
animationFrameRef.current = requestAnimationFrame(render);
|
|
1337
|
-
}, []);
|
|
1338
|
-
import_react4.useEffect(() => {
|
|
1339
|
-
const canvas = canvasRef.current;
|
|
1340
|
-
if (!canvas)
|
|
1341
|
-
return;
|
|
1342
|
-
const initialize = () => {
|
|
1343
|
-
try {
|
|
1344
|
-
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current, uniformsRef.current);
|
|
1345
|
-
elapsedTimeRef.current = 0;
|
|
1346
|
-
lastFrameTimeRef.current = 0;
|
|
1347
|
-
contextLostRef.current = false;
|
|
1348
|
-
animationFrameRef.current = requestAnimationFrame(render);
|
|
1349
|
-
} catch (err) {
|
|
1350
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
1351
|
-
if (onErrorRef.current) {
|
|
1352
|
-
onErrorRef.current(error);
|
|
1353
|
-
} else {
|
|
1354
|
-
console.error("WebGL initialization failed:", error);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
};
|
|
1358
|
-
const handleContextLost = (event) => {
|
|
1359
|
-
event.preventDefault();
|
|
1360
|
-
contextLostRef.current = true;
|
|
1361
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
1362
|
-
stateRef.current = null;
|
|
1363
|
-
};
|
|
1364
|
-
const handleContextRestored = () => {
|
|
1365
|
-
initialize();
|
|
1366
|
-
};
|
|
1367
|
-
const dprMediaQuery = window.matchMedia(`(resolution: ${dprRef.current}dppx)`);
|
|
1368
|
-
const handleDprChange = () => {
|
|
1369
|
-
dprRef.current = window.devicePixelRatio || 1;
|
|
1370
|
-
};
|
|
1371
|
-
dprMediaQuery.addEventListener("change", handleDprChange);
|
|
1372
|
-
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
1373
|
-
canvas.addEventListener("webglcontextrestored", handleContextRestored);
|
|
1374
|
-
initialize();
|
|
1375
|
-
return () => {
|
|
1376
|
-
dprMediaQuery.removeEventListener("change", handleDprChange);
|
|
1377
|
-
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
1378
|
-
canvas.removeEventListener("webglcontextrestored", handleContextRestored);
|
|
1379
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
1380
|
-
if (stateRef.current) {
|
|
1381
|
-
cleanupWebGL(stateRef.current.gl, stateRef.current);
|
|
1382
|
-
stateRef.current = null;
|
|
1383
|
-
}
|
|
1384
|
-
};
|
|
1385
|
-
}, [render]);
|
|
1386
|
-
import_react4.useEffect(() => {
|
|
1387
|
-
const canvas = canvasRef.current;
|
|
1388
|
-
if (!canvas)
|
|
1389
|
-
return;
|
|
1390
|
-
const updateRect = () => {
|
|
1391
|
-
canvasRectRef.current = canvas.getBoundingClientRect();
|
|
1392
|
-
};
|
|
1393
|
-
updateRect();
|
|
1394
|
-
const resizeObserver = new ResizeObserver(updateRect);
|
|
1395
|
-
resizeObserver.observe(canvas);
|
|
1396
|
-
window.addEventListener("scroll", updateRect, { passive: true });
|
|
1397
|
-
const handleMouseMove = (event) => {
|
|
1398
|
-
const rect = canvasRectRef.current;
|
|
1399
|
-
if (!rect)
|
|
1400
|
-
return;
|
|
1401
|
-
const dpr = dprRef.current;
|
|
1402
|
-
const x = (event.clientX - rect.left) * dpr;
|
|
1403
|
-
const y = (rect.height - (event.clientY - rect.top)) * dpr;
|
|
1404
|
-
mouseRef.current = [x, y];
|
|
1405
|
-
const minDimension = Math.min(canvas.width, canvas.height) || 1;
|
|
1406
|
-
mouseNormalizedRef.current = [
|
|
1407
|
-
(mouseRef.current[0] - canvas.width / 2) / minDimension,
|
|
1408
|
-
(mouseRef.current[1] - canvas.height / 2) / minDimension
|
|
1409
|
-
];
|
|
1410
|
-
onMouseMoveRef.current?.({
|
|
1411
|
-
deltaTime: 0,
|
|
1412
|
-
time: elapsedTimeRef.current,
|
|
1413
|
-
resolution: [canvas.width, canvas.height],
|
|
1414
|
-
mouse: mouseRef.current,
|
|
1415
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1416
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1417
|
-
});
|
|
1418
|
-
};
|
|
1419
|
-
const handleMouseDown = (event) => {
|
|
1420
|
-
if (event.button === 0) {
|
|
1421
|
-
mouseLeftDownRef.current = true;
|
|
1422
|
-
}
|
|
1423
|
-
onMouseDownRef.current?.({
|
|
1424
|
-
deltaTime: 0,
|
|
1425
|
-
time: elapsedTimeRef.current,
|
|
1426
|
-
resolution: [canvas.width, canvas.height],
|
|
1427
|
-
mouse: mouseRef.current,
|
|
1428
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1429
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1430
|
-
});
|
|
1431
|
-
};
|
|
1432
|
-
const handleMouseUp = (event) => {
|
|
1433
|
-
if (event.button === 0) {
|
|
1434
|
-
mouseLeftDownRef.current = false;
|
|
1435
|
-
}
|
|
1436
|
-
onMouseUpRef.current?.({
|
|
1437
|
-
deltaTime: 0,
|
|
1438
|
-
time: elapsedTimeRef.current,
|
|
1439
|
-
resolution: [canvas.width, canvas.height],
|
|
1440
|
-
mouse: mouseRef.current,
|
|
1441
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1442
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1443
|
-
});
|
|
1444
|
-
};
|
|
1445
|
-
const handleClick = () => {
|
|
1446
|
-
if (!onClickRef.current)
|
|
1447
|
-
return;
|
|
1448
|
-
onClickRef.current({
|
|
1449
|
-
deltaTime: 0,
|
|
1450
|
-
time: elapsedTimeRef.current,
|
|
1451
|
-
resolution: [canvas.width, canvas.height],
|
|
1452
|
-
mouse: mouseRef.current,
|
|
1453
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1454
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1455
|
-
});
|
|
1456
|
-
};
|
|
1457
|
-
const handleMouseWheel = (event) => {
|
|
1458
|
-
onMouseWheelRef.current?.({
|
|
1459
|
-
deltaTime: 0,
|
|
1460
|
-
time: elapsedTimeRef.current,
|
|
1461
|
-
resolution: [canvas.width, canvas.height],
|
|
1462
|
-
mouse: mouseRef.current,
|
|
1463
|
-
mouseNormalized: mouseNormalizedRef.current,
|
|
1464
|
-
mouseLeftDown: mouseLeftDownRef.current
|
|
1465
|
-
}, event.deltaY);
|
|
1466
|
-
};
|
|
1467
|
-
window.addEventListener("mousemove", handleMouseMove);
|
|
1468
|
-
window.addEventListener("mousedown", handleMouseDown);
|
|
1469
|
-
window.addEventListener("mouseup", handleMouseUp);
|
|
1470
|
-
canvas.addEventListener("click", handleClick);
|
|
1471
|
-
window.addEventListener("wheel", handleMouseWheel);
|
|
1472
|
-
return () => {
|
|
1473
|
-
resizeObserver.disconnect();
|
|
1474
|
-
window.removeEventListener("scroll", updateRect);
|
|
1475
|
-
window.removeEventListener("mousemove", handleMouseMove);
|
|
1476
|
-
window.removeEventListener("mousedown", handleMouseDown);
|
|
1477
|
-
window.removeEventListener("mouseup", handleMouseUp);
|
|
1478
|
-
canvas.removeEventListener("click", handleClick);
|
|
1479
|
-
window.removeEventListener("wheel", handleMouseWheel);
|
|
1480
|
-
};
|
|
1481
|
-
}, []);
|
|
1482
|
-
return { canvasRef, mouseRef };
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
// src/ReactShader.tsx
|
|
1486
|
-
var jsx_dev_runtime2 = require("react/jsx-dev-runtime");
|
|
1487
|
-
var DEFAULT_VERTEX = `#version 300 es
|
|
1488
|
-
precision highp float;
|
|
1489
|
-
in vec2 a_position;
|
|
1490
|
-
|
|
1491
|
-
void main() {
|
|
1492
|
-
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
1493
|
-
}
|
|
1494
|
-
`;
|
|
1495
|
-
var FULLSCREEN_CONTAINER_STYLE2 = {
|
|
1496
|
-
position: "fixed",
|
|
1497
|
-
top: 0,
|
|
1498
|
-
left: 0,
|
|
1499
|
-
width: "100vw",
|
|
1500
|
-
height: "100vh",
|
|
1501
|
-
zIndex: 9000
|
|
1502
|
-
};
|
|
1503
|
-
var DEFAULT_CONTAINER_STYLE2 = {
|
|
1504
|
-
position: "relative",
|
|
1505
|
-
width: "100%",
|
|
1506
|
-
height: "100%"
|
|
1507
|
-
};
|
|
1508
|
-
var CANVAS_STYLE2 = {
|
|
1509
|
-
display: "block",
|
|
1510
|
-
width: "100%",
|
|
1511
|
-
height: "100%"
|
|
1512
|
-
};
|
|
1513
|
-
function ReactShader({
|
|
1514
|
-
className,
|
|
1515
|
-
fragment,
|
|
1516
|
-
vertex = DEFAULT_VERTEX,
|
|
1517
|
-
uniforms,
|
|
1518
|
-
fullscreen = false,
|
|
1519
|
-
timeScale = 1,
|
|
1520
|
-
onFrame,
|
|
1521
|
-
onClick,
|
|
1522
|
-
onMouseMove,
|
|
1523
|
-
onMouseDown,
|
|
1524
|
-
onMouseUp,
|
|
1525
|
-
onMouseWheel
|
|
1526
|
-
}) {
|
|
1527
|
-
const [error, setError] = import_react5.useState(null);
|
|
1528
|
-
const handleError = import_react5.useCallback((err) => {
|
|
1529
|
-
setError(err.message);
|
|
1530
|
-
console.error("ReactShader error:", err);
|
|
1531
|
-
}, []);
|
|
1532
|
-
import_react5.useEffect(() => {
|
|
1533
|
-
setError(null);
|
|
1534
|
-
}, [fragment, vertex]);
|
|
1535
|
-
const { canvasRef } = useWebGL({
|
|
1536
|
-
fragment,
|
|
1537
|
-
vertex,
|
|
1538
|
-
uniforms,
|
|
1539
|
-
onError: handleError,
|
|
1540
|
-
onFrame,
|
|
1541
|
-
onClick,
|
|
1542
|
-
onMouseMove,
|
|
1543
|
-
onMouseDown,
|
|
1544
|
-
onMouseUp,
|
|
1545
|
-
onMouseWheel,
|
|
1546
|
-
timeScale
|
|
1547
|
-
});
|
|
1548
|
-
const containerStyle = import_react5.useMemo(() => fullscreen ? FULLSCREEN_CONTAINER_STYLE2 : DEFAULT_CONTAINER_STYLE2, [fullscreen]);
|
|
1549
|
-
if (error) {
|
|
1550
|
-
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("div", {
|
|
1551
|
-
className,
|
|
1552
|
-
style: {
|
|
1553
|
-
...containerStyle,
|
|
1554
|
-
display: "flex",
|
|
1555
|
-
alignItems: "center",
|
|
1556
|
-
justifyContent: "center",
|
|
1557
|
-
backgroundColor: "#1a1a1a",
|
|
1558
|
-
color: "#ff6b6b",
|
|
1559
|
-
fontFamily: "monospace",
|
|
1560
|
-
fontSize: "12px",
|
|
1561
|
-
padding: "16px",
|
|
1562
|
-
overflow: "auto",
|
|
1563
|
-
boxSizing: "border-box",
|
|
1564
|
-
width: "100%",
|
|
1565
|
-
height: "100%"
|
|
1566
|
-
},
|
|
1567
|
-
children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("pre", {
|
|
1568
|
-
style: { margin: 0, whiteSpace: "pre-wrap" },
|
|
1569
|
-
children: error
|
|
1570
|
-
}, undefined, false, undefined, this)
|
|
1571
|
-
}, undefined, false, undefined, this);
|
|
1572
|
-
}
|
|
1573
|
-
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV("canvas", {
|
|
1574
|
-
ref: canvasRef,
|
|
1575
|
-
className,
|
|
1576
|
-
style: CANVAS_STYLE2
|
|
1577
|
-
}, undefined, false, undefined, this);
|
|
1578
|
-
}
|
|
1579
|
-
// src/shaders/color-palette.ts
|
|
1580
|
-
function generateColorPaletteFunction(name, paletteString) {
|
|
1581
|
-
const paletteArray = paletteString.replace("[[", "").replace("]]", "").split("] [").map((s) => s.split(" "));
|
|
1582
|
-
return `
|
|
1583
|
-
vec3 ${name}( float t ) {
|
|
1584
|
-
vec3 a = vec3(${paletteArray[0].join(",")});
|
|
1585
|
-
vec3 b = vec3(${paletteArray[1].join(",")});
|
|
1586
|
-
vec3 c = vec3(${paletteArray[2].join(",")});
|
|
1587
|
-
vec3 d = vec3(${paletteArray[3].join(",")});
|
|
1588
|
-
return a + b * cos(6.28318 * (c * t + d));
|
|
1589
|
-
}
|
|
1590
|
-
`;
|
|
1591
|
-
}
|
|
1592
862
|
// src/shaders/color-palette-gpu.ts
|
|
1593
863
|
function generateColorPaletteFunctionGpu(name, paletteString) {
|
|
1594
864
|
const paletteArray = paletteString.replace("[[", "").replace("]]", "").split("] [").map((s) => s.split(" "));
|
|
@@ -1602,29 +872,6 @@ fn ${name}(t: f32) -> vec3f {
|
|
|
1602
872
|
}
|
|
1603
873
|
`;
|
|
1604
874
|
}
|
|
1605
|
-
// src/shaders/distortion-ripple.ts
|
|
1606
|
-
function generateDistortionRippleFunction() {
|
|
1607
|
-
return `
|
|
1608
|
-
vec2 DistortionRipple(vec2 uv, vec2 center, float radius, float intensity, float thickness) {
|
|
1609
|
-
// 1. Calculate vector and distance from center
|
|
1610
|
-
vec2 dir = uv - center;
|
|
1611
|
-
float dist = length(dir);
|
|
1612
|
-
|
|
1613
|
-
// 2. Create a mask so the ripple only exists near the radius Z
|
|
1614
|
-
// Using smoothstep creates a soft edge for the ripple
|
|
1615
|
-
float mask = smoothstep(radius + thickness, radius, dist) * smoothstep(radius - thickness, radius, dist);
|
|
1616
|
-
|
|
1617
|
-
// 3. Calculate the displacement amount using a Sine wave
|
|
1618
|
-
// We subtract dist from radius to orient the wave correctly
|
|
1619
|
-
float wave = sin((dist - radius) * 20.0);
|
|
1620
|
-
|
|
1621
|
-
// 4. Apply intensity and mask, then offset the UV
|
|
1622
|
-
vec2 offset = normalize(dir) * wave * intensity * mask;
|
|
1623
|
-
|
|
1624
|
-
return offset;
|
|
1625
|
-
}
|
|
1626
|
-
`;
|
|
1627
|
-
}
|
|
1628
875
|
// src/shaders/distortion-ripple-gpu.ts
|
|
1629
876
|
function generateDistortionRippleFunctionGpu() {
|
|
1630
877
|
return `
|
|
@@ -1648,40 +895,6 @@ fn DistortionRipple(uv: vec2f, center: vec2f, radius: f32, intensity: f32, thick
|
|
|
1648
895
|
}
|
|
1649
896
|
`;
|
|
1650
897
|
}
|
|
1651
|
-
// src/shaders/scene-circles.ts
|
|
1652
|
-
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]]") {
|
|
1653
|
-
return `
|
|
1654
|
-
${generateColorPaletteFunction("circlesPalette", paletteString)}
|
|
1655
|
-
|
|
1656
|
-
vec3 SceneCircles(
|
|
1657
|
-
vec2 uv0,
|
|
1658
|
-
float iterations,
|
|
1659
|
-
float fractMultiplier,
|
|
1660
|
-
float time,
|
|
1661
|
-
float waveLength,
|
|
1662
|
-
float edgeBlur,
|
|
1663
|
-
float contrast
|
|
1664
|
-
) {
|
|
1665
|
-
vec3 col = vec3(0.0);
|
|
1666
|
-
vec2 uv = uv0;
|
|
1667
|
-
|
|
1668
|
-
for (float i = 0.0; i < iterations; i++) {
|
|
1669
|
-
uv = fract(uv * fractMultiplier) - 0.5;
|
|
1670
|
-
|
|
1671
|
-
float d = length(uv) * exp(-length(uv0));
|
|
1672
|
-
|
|
1673
|
-
vec3 color = circlesPalette(length(uv0) + i * 0.4 + time * 0.4);
|
|
1674
|
-
|
|
1675
|
-
d = sin(d * waveLength + time) / waveLength;
|
|
1676
|
-
d = abs(d);
|
|
1677
|
-
d = pow(edgeBlur / d, contrast);
|
|
1678
|
-
|
|
1679
|
-
col += color * d;
|
|
1680
|
-
}
|
|
1681
|
-
return col;
|
|
1682
|
-
}
|
|
1683
|
-
`;
|
|
1684
|
-
}
|
|
1685
898
|
// src/shaders/scene-circles-gpu.ts
|
|
1686
899
|
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]]") {
|
|
1687
900
|
return `
|
|
@@ -1720,190 +933,6 @@ fn SceneCircles(
|
|
|
1720
933
|
}
|
|
1721
934
|
`;
|
|
1722
935
|
}
|
|
1723
|
-
// src/shaders/simplex-noise.ts
|
|
1724
|
-
function generateSimplexNoiseFunction() {
|
|
1725
|
-
return `
|
|
1726
|
-
|
|
1727
|
-
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
1728
|
-
|
|
1729
|
-
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
1730
|
-
|
|
1731
|
-
float mod289(float x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
|
1732
|
-
|
|
1733
|
-
vec4 permute(vec4 x) { return mod289(((x*34.0)+10.0)*x); }
|
|
1734
|
-
|
|
1735
|
-
float permute(float x) { return mod289(((x*34.0)+10.0)*x); }
|
|
1736
|
-
|
|
1737
|
-
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
|
|
1738
|
-
|
|
1739
|
-
float taylorInvSqrt(float r) { return 1.79284291400159 - 0.85373472095314 * r; }
|
|
1740
|
-
|
|
1741
|
-
vec4 grad4(float j, vec4 ip) {
|
|
1742
|
-
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
|
|
1743
|
-
vec4 p,s;
|
|
1744
|
-
|
|
1745
|
-
p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
|
|
1746
|
-
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
|
|
1747
|
-
s = vec4(lessThan(p, vec4(0.0)));
|
|
1748
|
-
p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
|
|
1749
|
-
|
|
1750
|
-
return p;
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
// (sqrt(5) - 1)/4 = F4, used once below
|
|
1754
|
-
#define F4 0.309016994374947451
|
|
1755
|
-
|
|
1756
|
-
float SimplexNoise4D(vec4 v) {
|
|
1757
|
-
const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4
|
|
1758
|
-
0.276393202250021, // 2 * G4
|
|
1759
|
-
0.414589803375032, // 3 * G4
|
|
1760
|
-
-0.447213595499958); // -1 + 4 * G4
|
|
1761
|
-
|
|
1762
|
-
// First corner
|
|
1763
|
-
vec4 i = floor(v + dot(v, vec4(F4)) );
|
|
1764
|
-
vec4 x0 = v - i + dot(i, C.xxxx);
|
|
1765
|
-
|
|
1766
|
-
// Other corners
|
|
1767
|
-
|
|
1768
|
-
// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
|
|
1769
|
-
vec4 i0;
|
|
1770
|
-
vec3 isX = step( x0.yzw, x0.xxx );
|
|
1771
|
-
vec3 isYZ = step( x0.zww, x0.yyz );
|
|
1772
|
-
// i0.x = dot( isX, vec3( 1.0 ) );
|
|
1773
|
-
i0.x = isX.x + isX.y + isX.z;
|
|
1774
|
-
i0.yzw = 1.0 - isX;
|
|
1775
|
-
// i0.y += dot( isYZ.xy, vec2( 1.0 ) );
|
|
1776
|
-
i0.y += isYZ.x + isYZ.y;
|
|
1777
|
-
i0.zw += 1.0 - isYZ.xy;
|
|
1778
|
-
i0.z += isYZ.z;
|
|
1779
|
-
i0.w += 1.0 - isYZ.z;
|
|
1780
|
-
|
|
1781
|
-
// i0 now contains the unique values 0,1,2,3 in each channel
|
|
1782
|
-
vec4 i3 = clamp( i0, 0.0, 1.0 );
|
|
1783
|
-
vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
|
|
1784
|
-
vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
|
|
1785
|
-
|
|
1786
|
-
// x0 = x0 - 0.0 + 0.0 * C.xxxx
|
|
1787
|
-
// x1 = x0 - i1 + 1.0 * C.xxxx
|
|
1788
|
-
// x2 = x0 - i2 + 2.0 * C.xxxx
|
|
1789
|
-
// x3 = x0 - i3 + 3.0 * C.xxxx
|
|
1790
|
-
// x4 = x0 - 1.0 + 4.0 * C.xxxx
|
|
1791
|
-
vec4 x1 = x0 - i1 + C.xxxx;
|
|
1792
|
-
vec4 x2 = x0 - i2 + C.yyyy;
|
|
1793
|
-
vec4 x3 = x0 - i3 + C.zzzz;
|
|
1794
|
-
vec4 x4 = x0 + C.wwww;
|
|
1795
|
-
|
|
1796
|
-
// Permutations
|
|
1797
|
-
i = mod289(i);
|
|
1798
|
-
float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
|
|
1799
|
-
vec4 j1 = permute( permute( permute( permute (
|
|
1800
|
-
i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
|
|
1801
|
-
+ i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
|
|
1802
|
-
+ i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
|
|
1803
|
-
+ i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
|
|
1804
|
-
|
|
1805
|
-
// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
|
|
1806
|
-
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
|
|
1807
|
-
vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
|
|
1808
|
-
|
|
1809
|
-
vec4 p0 = grad4(j0, ip);
|
|
1810
|
-
vec4 p1 = grad4(j1.x, ip);
|
|
1811
|
-
vec4 p2 = grad4(j1.y, ip);
|
|
1812
|
-
vec4 p3 = grad4(j1.z, ip);
|
|
1813
|
-
vec4 p4 = grad4(j1.w, ip);
|
|
1814
|
-
|
|
1815
|
-
// Normalise gradients
|
|
1816
|
-
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
|
|
1817
|
-
p0 *= norm.x;
|
|
1818
|
-
p1 *= norm.y;
|
|
1819
|
-
p2 *= norm.z;
|
|
1820
|
-
p3 *= norm.w;
|
|
1821
|
-
p4 *= taylorInvSqrt(dot(p4,p4));
|
|
1822
|
-
|
|
1823
|
-
// Mix contributions from the five corners
|
|
1824
|
-
vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
|
|
1825
|
-
vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);
|
|
1826
|
-
m0 = m0 * m0;
|
|
1827
|
-
m1 = m1 * m1;
|
|
1828
|
-
return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
|
|
1829
|
-
+ dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
float SimplexNoise3D(vec3 v) {
|
|
1833
|
-
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
|
|
1834
|
-
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
|
1835
|
-
|
|
1836
|
-
// First corner
|
|
1837
|
-
vec3 i = floor(v + dot(v, C.yyy) );
|
|
1838
|
-
vec3 x0 = v - i + dot(i, C.xxx) ;
|
|
1839
|
-
|
|
1840
|
-
// Other corners
|
|
1841
|
-
vec3 g = step(x0.yzx, x0.xyz);
|
|
1842
|
-
vec3 l = 1.0 - g;
|
|
1843
|
-
vec3 i1 = min( g.xyz, l.zxy );
|
|
1844
|
-
vec3 i2 = max( g.xyz, l.zxy );
|
|
1845
|
-
|
|
1846
|
-
// x0 = x0 - 0.0 + 0.0 * C.xxx;
|
|
1847
|
-
// x1 = x0 - i1 + 1.0 * C.xxx;
|
|
1848
|
-
// x2 = x0 - i2 + 2.0 * C.xxx;
|
|
1849
|
-
// x3 = x0 - 1.0 + 3.0 * C.xxx;
|
|
1850
|
-
vec3 x1 = x0 - i1 + C.xxx;
|
|
1851
|
-
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
|
|
1852
|
-
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
|
|
1853
|
-
|
|
1854
|
-
// Permutations
|
|
1855
|
-
i = mod289(i);
|
|
1856
|
-
vec4 p = permute( permute( permute(
|
|
1857
|
-
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
|
|
1858
|
-
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
|
|
1859
|
-
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
|
|
1860
|
-
|
|
1861
|
-
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
|
1862
|
-
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
|
1863
|
-
float n_ = 0.142857142857; // 1.0/7.0
|
|
1864
|
-
vec3 ns = n_ * D.wyz - D.xzx;
|
|
1865
|
-
|
|
1866
|
-
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
|
|
1867
|
-
|
|
1868
|
-
vec4 x_ = floor(j * ns.z);
|
|
1869
|
-
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
|
|
1870
|
-
|
|
1871
|
-
vec4 x = x_ *ns.x + ns.yyyy;
|
|
1872
|
-
vec4 y = y_ *ns.x + ns.yyyy;
|
|
1873
|
-
vec4 h = 1.0 - abs(x) - abs(y);
|
|
1874
|
-
|
|
1875
|
-
vec4 b0 = vec4( x.xy, y.xy );
|
|
1876
|
-
vec4 b1 = vec4( x.zw, y.zw );
|
|
1877
|
-
|
|
1878
|
-
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
|
|
1879
|
-
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
|
|
1880
|
-
vec4 s0 = floor(b0)*2.0 + 1.0;
|
|
1881
|
-
vec4 s1 = floor(b1)*2.0 + 1.0;
|
|
1882
|
-
vec4 sh = -step(h, vec4(0.0));
|
|
1883
|
-
|
|
1884
|
-
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
|
|
1885
|
-
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
|
|
1886
|
-
|
|
1887
|
-
vec3 p0 = vec3(a0.xy,h.x);
|
|
1888
|
-
vec3 p1 = vec3(a0.zw,h.y);
|
|
1889
|
-
vec3 p2 = vec3(a1.xy,h.z);
|
|
1890
|
-
vec3 p3 = vec3(a1.zw,h.w);
|
|
1891
|
-
|
|
1892
|
-
// Normalise gradients
|
|
1893
|
-
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
|
|
1894
|
-
p0 *= norm.x;
|
|
1895
|
-
p1 *= norm.y;
|
|
1896
|
-
p2 *= norm.z;
|
|
1897
|
-
p3 *= norm.w;
|
|
1898
|
-
|
|
1899
|
-
// Mix final noise value
|
|
1900
|
-
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
|
|
1901
|
-
m = m * m;
|
|
1902
|
-
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
|
|
1903
|
-
dot(p2,x2), dot(p3,x3) ) );
|
|
1904
|
-
}
|
|
1905
|
-
`;
|
|
1906
|
-
}
|
|
1907
936
|
// src/shaders/simplex-noise-gpu.ts
|
|
1908
937
|
function generateSimplexNoiseFunctionGpu() {
|
|
1909
938
|
return `
|
|
@@ -2079,15 +1108,3 @@ fn SimplexNoise3D(v: vec3f) -> f32 {
|
|
|
2079
1108
|
}
|
|
2080
1109
|
`;
|
|
2081
1110
|
}
|
|
2082
|
-
// src/shaders/utils.ts
|
|
2083
|
-
function generateUtilsFunction() {
|
|
2084
|
-
return `
|
|
2085
|
-
vec2 GetUv(vec2 fragCoord, vec2 resolution) {
|
|
2086
|
-
return (fragCoord - 0.5 * resolution) / resolution.y;
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
vec2 GetMouse(vec2 mouse, vec2 resolution) {
|
|
2090
|
-
return (mouse - 0.5 * resolution) / resolution.y;
|
|
2091
|
-
}
|
|
2092
|
-
`;
|
|
2093
|
-
}
|