@fjandin/react-shader 0.0.4 → 0.0.5
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 +87 -4
- package/dist/ReactShader.d.ts +1 -1
- package/dist/ReactShader.d.ts.map +1 -1
- package/dist/example/frontend.d.ts.map +1 -1
- package/dist/example/glsl/cartesian-to-polar.glsl.d.ts +2 -0
- package/dist/example/glsl/cartesian-to-polar.glsl.d.ts.map +1 -0
- package/dist/example/lib/logger.d.ts +2 -0
- package/dist/example/lib/logger.d.ts.map +1 -0
- package/dist/example/shader.d.ts.map +1 -1
- package/dist/hooks/useWebGL.d.ts +4 -0
- package/dist/hooks/useWebGL.d.ts.map +1 -1
- package/dist/index.cjs +148 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -8
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/uniforms.d.ts +3 -0
- package/dist/utils/uniforms.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,8 @@ function App() {
|
|
|
53
53
|
| `fullscreen` | `boolean` | No | `false` | Render as fixed fullscreen overlay |
|
|
54
54
|
| `timeScale` | `number` | No | `1` | Scale factor for elapsed time |
|
|
55
55
|
| `onFrame` | `(info: FrameInfo) => void` | No | - | Callback invoked on each frame |
|
|
56
|
+
| `onClick` | `(info: FrameInfo) => void` | No | - | Callback invoked on canvas click |
|
|
57
|
+
| `onMouseMove` | `(info: FrameInfo) => void` | No | - | Callback invoked on mouse move |
|
|
56
58
|
|
|
57
59
|
## Built-in Uniforms
|
|
58
60
|
|
|
@@ -62,6 +64,7 @@ These uniforms are automatically provided to your shader every frame:
|
|
|
62
64
|
|---------|-----------|-------------|
|
|
63
65
|
| `iTime` | `float` | Elapsed time in seconds (scaled by `timeScale` prop) |
|
|
64
66
|
| `iMouse` | `vec2` | Mouse position in pixels (Y=0 at bottom) |
|
|
67
|
+
| `iMouseNormalized` | `vec2` | Mouse position normalized with aspect correction (shorter axis -0.5 to 0.5, center is 0,0) |
|
|
65
68
|
| `iMouseLeftDown` | `float` | `1.0` when left mouse button is pressed, `0.0` otherwise |
|
|
66
69
|
| `iResolution` | `vec2` | Canvas resolution in pixels (includes high-DPI scaling) |
|
|
67
70
|
|
|
@@ -73,10 +76,14 @@ Pass custom uniform values via the `uniforms` prop:
|
|
|
73
76
|
<ReactShader
|
|
74
77
|
fragment={fragment}
|
|
75
78
|
uniforms={{
|
|
76
|
-
scale: 2.0,
|
|
77
|
-
offset: [0.5, 0.5],
|
|
78
|
-
color: [1.0, 0.5, 0.2],
|
|
79
|
-
transform: [1, 0, 0, 1],
|
|
79
|
+
scale: 2.0, // float
|
|
80
|
+
offset: [0.5, 0.5], // vec2
|
|
81
|
+
color: [1.0, 0.5, 0.2], // vec3
|
|
82
|
+
transform: [1, 0, 0, 1], // vec4
|
|
83
|
+
weights: [0.1, 0.2, 0.3, 0.25, 0.15], // float array
|
|
84
|
+
points: [[0, 0], [1, 0], [0.5, 1]], // vec2 array
|
|
85
|
+
colors: [[1, 0, 0], [0, 1, 0]], // vec3 array
|
|
86
|
+
ripples: [[0, 0, 0.5, 0.1]], // vec4 array
|
|
80
87
|
}}
|
|
81
88
|
/>
|
|
82
89
|
```
|
|
@@ -86,6 +93,62 @@ Supported types:
|
|
|
86
93
|
- `[number, number]` → `vec2`
|
|
87
94
|
- `[number, number, number]` → `vec3`
|
|
88
95
|
- `[number, number, number, number]` → `vec4`
|
|
96
|
+
- `number[]` (length > 4) → `float[N]`
|
|
97
|
+
- `[number, number][]` → `vec2[N]`
|
|
98
|
+
- `[number, number, number][]` → `vec3[N]`
|
|
99
|
+
- `[number, number, number, number][]` → `vec4[N]`
|
|
100
|
+
|
|
101
|
+
### Array Count Uniforms
|
|
102
|
+
|
|
103
|
+
For array uniforms, an additional `_count` uniform is automatically created and set:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
uniforms={{ points: [[0, 0], [1, 0], [0.5, 1]] }}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This generates both `uniform vec2 points[3]` and `uniform int points_count` (set to `3`), allowing you to loop over arrays in your shader:
|
|
110
|
+
|
|
111
|
+
```glsl
|
|
112
|
+
for (int i = 0; i < points_count; i++) {
|
|
113
|
+
// Use points[i]...
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Automatic Uniform Injection
|
|
118
|
+
|
|
119
|
+
Instead of manually declaring uniforms in your shader, you can use the `// @UNIFORM_VALUES` marker to automatically inject all uniform declarations:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const fragment = `#version 300 es
|
|
123
|
+
precision highp float;
|
|
124
|
+
|
|
125
|
+
// @UNIFORM_VALUES
|
|
126
|
+
|
|
127
|
+
out vec4 fragColor;
|
|
128
|
+
|
|
129
|
+
void main() {
|
|
130
|
+
vec2 uv = gl_FragCoord.xy / iResolution;
|
|
131
|
+
vec3 col = baseColor * (sin(iTime) * 0.5 + 0.5);
|
|
132
|
+
fragColor = vec4(col, 1.0);
|
|
133
|
+
}
|
|
134
|
+
`
|
|
135
|
+
|
|
136
|
+
<ReactShader
|
|
137
|
+
fragment={fragment}
|
|
138
|
+
uniforms={{ baseColor: [1.0, 0.5, 0.2] }}
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The marker gets replaced with declarations for both built-in uniforms (`iTime`, `iMouse`, `iMouseNormalized`, `iMouseLeftDown`, `iResolution`) and your custom uniforms:
|
|
143
|
+
|
|
144
|
+
```glsl
|
|
145
|
+
uniform float iTime;
|
|
146
|
+
uniform vec2 iMouse;
|
|
147
|
+
uniform vec2 iMouseNormalized;
|
|
148
|
+
uniform float iMouseLeftDown;
|
|
149
|
+
uniform vec2 iResolution;
|
|
150
|
+
uniform vec3 baseColor;
|
|
151
|
+
```
|
|
89
152
|
|
|
90
153
|
## Frame Callback
|
|
91
154
|
|
|
@@ -112,6 +175,7 @@ The `FrameInfo` object contains:
|
|
|
112
175
|
- `time` - Total elapsed time in seconds
|
|
113
176
|
- `resolution` - Canvas resolution as `[width, height]`
|
|
114
177
|
- `mouse` - Mouse position as `[x, y]`
|
|
178
|
+
- `mouseLeftDown` - Whether left mouse button is pressed
|
|
115
179
|
|
|
116
180
|
## TypeScript
|
|
117
181
|
|
|
@@ -122,6 +186,10 @@ import type {
|
|
|
122
186
|
Vec2,
|
|
123
187
|
Vec3,
|
|
124
188
|
Vec4,
|
|
189
|
+
FloatArray,
|
|
190
|
+
Vec2Array,
|
|
191
|
+
Vec3Array,
|
|
192
|
+
Vec4Array,
|
|
125
193
|
UniformValue,
|
|
126
194
|
DefaultUniforms,
|
|
127
195
|
FrameInfo,
|
|
@@ -129,6 +197,21 @@ import type {
|
|
|
129
197
|
} from "@fjandin/react-shader"
|
|
130
198
|
```
|
|
131
199
|
|
|
200
|
+
A utility function for generating uniform declarations is also exported:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { generateUniformDeclarations } from "@fjandin/react-shader"
|
|
204
|
+
|
|
205
|
+
const declarations = generateUniformDeclarations({
|
|
206
|
+
scale: 1.0,
|
|
207
|
+
points: [[0, 0], [1, 1]],
|
|
208
|
+
})
|
|
209
|
+
// Returns:
|
|
210
|
+
// uniform float scale;
|
|
211
|
+
// uniform vec2 points[2];
|
|
212
|
+
// uniform int points_count;
|
|
213
|
+
```
|
|
214
|
+
|
|
132
215
|
## Features
|
|
133
216
|
|
|
134
217
|
- WebGL2 with WebGL1 fallback
|
package/dist/ReactShader.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ReactShaderProps } from "./types";
|
|
2
|
-
export declare function ReactShader({ className, fragment, vertex, uniforms, fullscreen, timeScale, onFrame, }: ReactShaderProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ReactShader({ className, fragment, vertex, uniforms, fullscreen, timeScale, onFrame, onClick, onMouseMove, }: ReactShaderProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=ReactShader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactShader.d.ts","sourceRoot":"","sources":["../src/ReactShader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAU/C,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,MAAuB,EACvB,QAAQ,EACR,UAAkB,EAClB,SAAa,EACb,OAAO,
|
|
1
|
+
{"version":3,"file":"ReactShader.d.ts","sourceRoot":"","sources":["../src/ReactShader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAU/C,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,MAAuB,EACvB,QAAQ,EACR,UAAkB,EAClB,SAAa,EACb,OAAO,EACP,OAAO,EACP,WAAW,GACZ,EAAE,gBAAgB,2CA0ElB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;AAyBpB,wBAAgB,GAAG,4CAwFlB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const cartesianToPolar = "\n vec2 cartesianToPolar(vec2 cartesian, float seamAngleDegrees) {\n float radius = length(cartesian);\n // Rotate input to move where the seam appears\n float rot = radians(seamAngleDegrees);\n vec2 rotated = vec2(\n cartesian.x * cos(rot) + cartesian.y * sin(rot),\n -cartesian.x * sin(rot) + cartesian.y * cos(rot)\n );\n float angle = atan(rotated.y, rotated.x);\n // Map angle from [-PI, PI] to [-0.5, 0.5] range\n return vec2(angle / 6.28318, radius);\n }\n";
|
|
2
|
+
//# sourceMappingURL=cartesian-to-polar.glsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cartesian-to-polar.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/cartesian-to-polar.glsl.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,wfAa5B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/example/lib/logger.ts"],"names":[],"mappings":"AACA,wBAAgB,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,QAejC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/example/shader.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/example/shader.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,QA4FnB,CAAA"}
|
package/dist/hooks/useWebGL.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface FrameInfo {
|
|
|
4
4
|
time: number;
|
|
5
5
|
resolution: [number, number];
|
|
6
6
|
mouse: [number, number];
|
|
7
|
+
mouseNormalized: [number, number];
|
|
8
|
+
mouseLeftDown: boolean;
|
|
7
9
|
}
|
|
8
10
|
interface UseWebGLOptions {
|
|
9
11
|
fragment: string;
|
|
@@ -11,6 +13,8 @@ interface UseWebGLOptions {
|
|
|
11
13
|
uniforms?: Record<string, UniformValue>;
|
|
12
14
|
onError?: (error: Error) => void;
|
|
13
15
|
onFrame?: (info: FrameInfo) => void;
|
|
16
|
+
onClick?: (info: FrameInfo) => void;
|
|
17
|
+
onMouseMove?: (info: FrameInfo) => void;
|
|
14
18
|
running?: boolean;
|
|
15
19
|
timeScale?: number;
|
|
16
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAI5C,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAI5C,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAqED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe;;;EA+PhD"}
|
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ var __export = (target, all) => {
|
|
|
29
29
|
// src/index.ts
|
|
30
30
|
var exports_src = {};
|
|
31
31
|
__export(exports_src, {
|
|
32
|
+
generateUniformDeclarations: () => generateUniformDeclarations,
|
|
32
33
|
ReactShader: () => ReactShader
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(exports_src);
|
|
@@ -84,14 +85,30 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// src/utils/uniforms.ts
|
|
88
|
+
var MAX_ARRAY_LENGTH = 100;
|
|
87
89
|
function isVec2(value) {
|
|
88
|
-
return Array.isArray(value) && value.length === 2;
|
|
90
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
|
|
89
91
|
}
|
|
90
92
|
function isVec3(value) {
|
|
91
|
-
return Array.isArray(value) && value.length === 3;
|
|
93
|
+
return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
|
|
92
94
|
}
|
|
93
95
|
function isVec4(value) {
|
|
94
|
-
return Array.isArray(value) && value.length === 4;
|
|
96
|
+
return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
|
|
97
|
+
}
|
|
98
|
+
function isFloatArray(value) {
|
|
99
|
+
return Array.isArray(value) && value.length > 4 && typeof value[0] === "number";
|
|
100
|
+
}
|
|
101
|
+
function isVec2Array(value) {
|
|
102
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 2;
|
|
103
|
+
}
|
|
104
|
+
function isVec3Array(value) {
|
|
105
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
|
|
106
|
+
}
|
|
107
|
+
function isVec4Array(value) {
|
|
108
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
|
|
109
|
+
}
|
|
110
|
+
function isArrayUniform(value) {
|
|
111
|
+
return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
|
|
95
112
|
}
|
|
96
113
|
function setUniform(gl, location, value) {
|
|
97
114
|
if (location === null) {
|
|
@@ -99,6 +116,14 @@ function setUniform(gl, location, value) {
|
|
|
99
116
|
}
|
|
100
117
|
if (typeof value === "number") {
|
|
101
118
|
gl.uniform1f(location, value);
|
|
119
|
+
} else if (isVec4Array(value)) {
|
|
120
|
+
gl.uniform4fv(location, value.flat());
|
|
121
|
+
} else if (isVec3Array(value)) {
|
|
122
|
+
gl.uniform3fv(location, value.flat());
|
|
123
|
+
} else if (isVec2Array(value)) {
|
|
124
|
+
gl.uniform2fv(location, value.flat());
|
|
125
|
+
} else if (isFloatArray(value)) {
|
|
126
|
+
gl.uniform1fv(location, value);
|
|
102
127
|
} else if (isVec4(value)) {
|
|
103
128
|
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
104
129
|
} else if (isVec3(value)) {
|
|
@@ -118,19 +143,90 @@ function setUniforms(gl, program, uniforms, locationCache) {
|
|
|
118
143
|
locationCache.set(name, location);
|
|
119
144
|
}
|
|
120
145
|
setUniform(gl, location, value);
|
|
146
|
+
if (isArrayUniform(value)) {
|
|
147
|
+
const countName = `${name}_count`;
|
|
148
|
+
let countLocation = locationCache.get(countName);
|
|
149
|
+
if (countLocation === undefined) {
|
|
150
|
+
countLocation = getUniformLocation(gl, program, countName);
|
|
151
|
+
locationCache.set(countName, countLocation);
|
|
152
|
+
}
|
|
153
|
+
if (countLocation !== null) {
|
|
154
|
+
gl.uniform1i(countLocation, value.length);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
121
157
|
}
|
|
122
158
|
}
|
|
123
159
|
function createUniformLocationCache() {
|
|
124
160
|
return new Map;
|
|
125
161
|
}
|
|
162
|
+
function getUniformType(value) {
|
|
163
|
+
if (typeof value === "number") {
|
|
164
|
+
return "float";
|
|
165
|
+
}
|
|
166
|
+
if (isVec4Array(value)) {
|
|
167
|
+
return `vec4[${MAX_ARRAY_LENGTH}]`;
|
|
168
|
+
}
|
|
169
|
+
if (isVec3Array(value)) {
|
|
170
|
+
return `vec3[${MAX_ARRAY_LENGTH}]`;
|
|
171
|
+
}
|
|
172
|
+
if (isVec2Array(value)) {
|
|
173
|
+
return `vec2[${MAX_ARRAY_LENGTH}]`;
|
|
174
|
+
}
|
|
175
|
+
if (isFloatArray(value)) {
|
|
176
|
+
return `float[${MAX_ARRAY_LENGTH}]`;
|
|
177
|
+
}
|
|
178
|
+
if (isVec4(value)) {
|
|
179
|
+
return "vec4";
|
|
180
|
+
}
|
|
181
|
+
if (isVec3(value)) {
|
|
182
|
+
return "vec3";
|
|
183
|
+
}
|
|
184
|
+
if (isVec2(value)) {
|
|
185
|
+
return "vec2";
|
|
186
|
+
}
|
|
187
|
+
return "float";
|
|
188
|
+
}
|
|
189
|
+
function generateUniformDeclarations(uniforms) {
|
|
190
|
+
const lines = [];
|
|
191
|
+
for (const [name, value] of Object.entries(uniforms)) {
|
|
192
|
+
const type = getUniformType(value);
|
|
193
|
+
if (type.includes("[")) {
|
|
194
|
+
const [baseType, arrayPart] = type.split("[");
|
|
195
|
+
lines.push(`uniform ${baseType} ${name}[${arrayPart};`);
|
|
196
|
+
lines.push(`uniform int ${name}_count;`);
|
|
197
|
+
} else {
|
|
198
|
+
lines.push(`uniform ${type} ${name};`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return lines.join(`
|
|
202
|
+
`);
|
|
203
|
+
}
|
|
204
|
+
var UNIFORM_MARKER = "// @UNIFORM_VALUES";
|
|
205
|
+
function injectUniformDeclarations(shaderSource, customUniforms, defaultUniforms) {
|
|
206
|
+
if (!shaderSource.includes(UNIFORM_MARKER)) {
|
|
207
|
+
return shaderSource;
|
|
208
|
+
}
|
|
209
|
+
const allUniforms = { ...defaultUniforms, ...customUniforms };
|
|
210
|
+
const declarations = generateUniformDeclarations(allUniforms);
|
|
211
|
+
return shaderSource.replace(UNIFORM_MARKER, declarations);
|
|
212
|
+
}
|
|
126
213
|
|
|
127
214
|
// src/hooks/useWebGL.ts
|
|
128
|
-
|
|
215
|
+
var DEFAULT_UNIFORM_TYPES = {
|
|
216
|
+
iTime: 0,
|
|
217
|
+
iMouse: [0, 0],
|
|
218
|
+
iMouseNormalized: [0, 0],
|
|
219
|
+
iMouseLeftDown: 0,
|
|
220
|
+
iResolution: [0, 0]
|
|
221
|
+
};
|
|
222
|
+
function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
|
|
129
223
|
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
|
130
224
|
if (!gl) {
|
|
131
225
|
throw new Error("WebGL not supported");
|
|
132
226
|
}
|
|
133
|
-
const
|
|
227
|
+
const processedVertex = injectUniformDeclarations(vertexSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
228
|
+
const processedFragment = injectUniformDeclarations(fragmentSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
229
|
+
const program = createShaderProgram(gl, processedVertex, processedFragment);
|
|
134
230
|
const positionBuffer = gl.createBuffer();
|
|
135
231
|
if (!positionBuffer) {
|
|
136
232
|
throw new Error("Failed to create position buffer");
|
|
@@ -161,18 +257,23 @@ function useWebGL(options) {
|
|
|
161
257
|
const elapsedTimeRef = import_react.useRef(0);
|
|
162
258
|
const lastFrameTimeRef = import_react.useRef(0);
|
|
163
259
|
const mouseRef = import_react.useRef([0, 0]);
|
|
260
|
+
const mouseNormalizedRef = import_react.useRef([0, 0]);
|
|
164
261
|
const mouseLeftDownRef = import_react.useRef(false);
|
|
165
262
|
const canvasRectRef = import_react.useRef(null);
|
|
166
263
|
const contextLostRef = import_react.useRef(false);
|
|
167
264
|
const uniformsRef = import_react.useRef(options.uniforms);
|
|
168
265
|
const onErrorRef = import_react.useRef(options.onError);
|
|
169
266
|
const onFrameRef = import_react.useRef(options.onFrame);
|
|
267
|
+
const onClickRef = import_react.useRef(options.onClick);
|
|
268
|
+
const onMouseMoveRef = import_react.useRef(options.onMouseMove);
|
|
170
269
|
const timeScaleRef = import_react.useRef(options.timeScale ?? 1);
|
|
171
270
|
const vertexRef = import_react.useRef(options.vertex);
|
|
172
271
|
const fragmentRef = import_react.useRef(options.fragment);
|
|
173
272
|
uniformsRef.current = options.uniforms;
|
|
174
273
|
onErrorRef.current = options.onError;
|
|
175
274
|
onFrameRef.current = options.onFrame;
|
|
275
|
+
onClickRef.current = options.onClick;
|
|
276
|
+
onMouseMoveRef.current = options.onMouseMove;
|
|
176
277
|
timeScaleRef.current = options.timeScale ?? 1;
|
|
177
278
|
vertexRef.current = options.vertex;
|
|
178
279
|
fragmentRef.current = options.fragment;
|
|
@@ -208,9 +309,15 @@ function useWebGL(options) {
|
|
|
208
309
|
gl.enableVertexAttribArray(positionAttributeLocation);
|
|
209
310
|
gl.bindBuffer(gl.ARRAY_BUFFER, state.positionBuffer);
|
|
210
311
|
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
|
312
|
+
const minDimension = Math.min(canvas.width, canvas.height) || 1;
|
|
313
|
+
mouseNormalizedRef.current = [
|
|
314
|
+
(mouseRef.current[0] - canvas.width / 2) / minDimension,
|
|
315
|
+
(mouseRef.current[1] - canvas.height / 2) / minDimension
|
|
316
|
+
];
|
|
211
317
|
const defaultUniforms = {
|
|
212
318
|
iTime: elapsedTime,
|
|
213
319
|
iMouse: mouseRef.current,
|
|
320
|
+
iMouseNormalized: mouseNormalizedRef.current,
|
|
214
321
|
iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
|
|
215
322
|
iResolution: [canvas.width, canvas.height]
|
|
216
323
|
};
|
|
@@ -224,7 +331,9 @@ function useWebGL(options) {
|
|
|
224
331
|
deltaTime,
|
|
225
332
|
time: elapsedTime,
|
|
226
333
|
resolution: [canvas.width, canvas.height],
|
|
227
|
-
mouse: mouseRef.current
|
|
334
|
+
mouse: mouseRef.current,
|
|
335
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
336
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
228
337
|
});
|
|
229
338
|
}
|
|
230
339
|
animationFrameRef.current = requestAnimationFrame(render);
|
|
@@ -235,7 +344,7 @@ function useWebGL(options) {
|
|
|
235
344
|
return;
|
|
236
345
|
const initialize = () => {
|
|
237
346
|
try {
|
|
238
|
-
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current);
|
|
347
|
+
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current, uniformsRef.current);
|
|
239
348
|
elapsedTimeRef.current = 0;
|
|
240
349
|
lastFrameTimeRef.current = 0;
|
|
241
350
|
contextLostRef.current = false;
|
|
@@ -290,6 +399,19 @@ function useWebGL(options) {
|
|
|
290
399
|
const x = (event.clientX - rect.left) * dpr;
|
|
291
400
|
const y = (rect.height - (event.clientY - rect.top)) * dpr;
|
|
292
401
|
mouseRef.current = [x, y];
|
|
402
|
+
const minDimension = Math.min(canvas.width, canvas.height) || 1;
|
|
403
|
+
mouseNormalizedRef.current = [
|
|
404
|
+
(mouseRef.current[0] - canvas.width / 2) / minDimension,
|
|
405
|
+
(mouseRef.current[1] - canvas.height / 2) / minDimension
|
|
406
|
+
];
|
|
407
|
+
onMouseMoveRef.current?.({
|
|
408
|
+
deltaTime: 0,
|
|
409
|
+
time: elapsedTimeRef.current,
|
|
410
|
+
resolution: [canvas.width, canvas.height],
|
|
411
|
+
mouse: mouseRef.current,
|
|
412
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
413
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
414
|
+
});
|
|
293
415
|
};
|
|
294
416
|
const handleMouseDown = (event) => {
|
|
295
417
|
if (event.button === 0) {
|
|
@@ -301,15 +423,29 @@ function useWebGL(options) {
|
|
|
301
423
|
mouseLeftDownRef.current = false;
|
|
302
424
|
}
|
|
303
425
|
};
|
|
426
|
+
const handleClick = () => {
|
|
427
|
+
if (!onClickRef.current)
|
|
428
|
+
return;
|
|
429
|
+
onClickRef.current({
|
|
430
|
+
deltaTime: 0,
|
|
431
|
+
time: elapsedTimeRef.current,
|
|
432
|
+
resolution: [canvas.width, canvas.height],
|
|
433
|
+
mouse: mouseRef.current,
|
|
434
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
435
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
436
|
+
});
|
|
437
|
+
};
|
|
304
438
|
window.addEventListener("mousemove", handleMouseMove);
|
|
305
439
|
window.addEventListener("mousedown", handleMouseDown);
|
|
306
440
|
window.addEventListener("mouseup", handleMouseUp);
|
|
441
|
+
canvas.addEventListener("click", handleClick);
|
|
307
442
|
return () => {
|
|
308
443
|
resizeObserver.disconnect();
|
|
309
444
|
window.removeEventListener("scroll", updateRect);
|
|
310
445
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
311
446
|
window.removeEventListener("mousedown", handleMouseDown);
|
|
312
447
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
448
|
+
canvas.removeEventListener("click", handleClick);
|
|
313
449
|
};
|
|
314
450
|
}, []);
|
|
315
451
|
return { canvasRef, mouseRef };
|
|
@@ -331,7 +467,9 @@ function ReactShader({
|
|
|
331
467
|
uniforms,
|
|
332
468
|
fullscreen = false,
|
|
333
469
|
timeScale = 1,
|
|
334
|
-
onFrame
|
|
470
|
+
onFrame,
|
|
471
|
+
onClick,
|
|
472
|
+
onMouseMove
|
|
335
473
|
}) {
|
|
336
474
|
const [error, setError] = import_react2.useState(null);
|
|
337
475
|
const handleError = import_react2.useCallback((err) => {
|
|
@@ -352,6 +490,8 @@ function ReactShader({
|
|
|
352
490
|
uniforms,
|
|
353
491
|
onError: handleError,
|
|
354
492
|
onFrame: handleFrame,
|
|
493
|
+
onClick,
|
|
494
|
+
onMouseMove,
|
|
355
495
|
timeScale
|
|
356
496
|
});
|
|
357
497
|
const containerStyle = fullscreen ? {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { ReactShader } from "./ReactShader";
|
|
2
|
-
export type { DefaultUniforms, ReactShaderProps, UniformValue, Vec2, Vec3, Vec4, } from "./types";
|
|
2
|
+
export type { DefaultUniforms, FloatArray, ReactShaderProps, UniformValue, Vec2, Vec2Array, Vec3, Vec3Array, Vec4, Vec4Array, } from "./types";
|
|
3
|
+
export { generateUniformDeclarations } from "./utils/uniforms";
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,IAAI,EACJ,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -49,14 +49,30 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/utils/uniforms.ts
|
|
52
|
+
var MAX_ARRAY_LENGTH = 100;
|
|
52
53
|
function isVec2(value) {
|
|
53
|
-
return Array.isArray(value) && value.length === 2;
|
|
54
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "number";
|
|
54
55
|
}
|
|
55
56
|
function isVec3(value) {
|
|
56
|
-
return Array.isArray(value) && value.length === 3;
|
|
57
|
+
return Array.isArray(value) && value.length === 3 && typeof value[0] === "number";
|
|
57
58
|
}
|
|
58
59
|
function isVec4(value) {
|
|
59
|
-
return Array.isArray(value) && value.length === 4;
|
|
60
|
+
return Array.isArray(value) && value.length === 4 && typeof value[0] === "number";
|
|
61
|
+
}
|
|
62
|
+
function isFloatArray(value) {
|
|
63
|
+
return Array.isArray(value) && value.length > 4 && typeof value[0] === "number";
|
|
64
|
+
}
|
|
65
|
+
function isVec2Array(value) {
|
|
66
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 2;
|
|
67
|
+
}
|
|
68
|
+
function isVec3Array(value) {
|
|
69
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 3;
|
|
70
|
+
}
|
|
71
|
+
function isVec4Array(value) {
|
|
72
|
+
return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]) && value[0].length === 4;
|
|
73
|
+
}
|
|
74
|
+
function isArrayUniform(value) {
|
|
75
|
+
return isFloatArray(value) || isVec2Array(value) || isVec3Array(value) || isVec4Array(value);
|
|
60
76
|
}
|
|
61
77
|
function setUniform(gl, location, value) {
|
|
62
78
|
if (location === null) {
|
|
@@ -64,6 +80,14 @@ function setUniform(gl, location, value) {
|
|
|
64
80
|
}
|
|
65
81
|
if (typeof value === "number") {
|
|
66
82
|
gl.uniform1f(location, value);
|
|
83
|
+
} else if (isVec4Array(value)) {
|
|
84
|
+
gl.uniform4fv(location, value.flat());
|
|
85
|
+
} else if (isVec3Array(value)) {
|
|
86
|
+
gl.uniform3fv(location, value.flat());
|
|
87
|
+
} else if (isVec2Array(value)) {
|
|
88
|
+
gl.uniform2fv(location, value.flat());
|
|
89
|
+
} else if (isFloatArray(value)) {
|
|
90
|
+
gl.uniform1fv(location, value);
|
|
67
91
|
} else if (isVec4(value)) {
|
|
68
92
|
gl.uniform4f(location, value[0], value[1], value[2], value[3]);
|
|
69
93
|
} else if (isVec3(value)) {
|
|
@@ -83,19 +107,90 @@ function setUniforms(gl, program, uniforms, locationCache) {
|
|
|
83
107
|
locationCache.set(name, location);
|
|
84
108
|
}
|
|
85
109
|
setUniform(gl, location, value);
|
|
110
|
+
if (isArrayUniform(value)) {
|
|
111
|
+
const countName = `${name}_count`;
|
|
112
|
+
let countLocation = locationCache.get(countName);
|
|
113
|
+
if (countLocation === undefined) {
|
|
114
|
+
countLocation = getUniformLocation(gl, program, countName);
|
|
115
|
+
locationCache.set(countName, countLocation);
|
|
116
|
+
}
|
|
117
|
+
if (countLocation !== null) {
|
|
118
|
+
gl.uniform1i(countLocation, value.length);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
86
121
|
}
|
|
87
122
|
}
|
|
88
123
|
function createUniformLocationCache() {
|
|
89
124
|
return new Map;
|
|
90
125
|
}
|
|
126
|
+
function getUniformType(value) {
|
|
127
|
+
if (typeof value === "number") {
|
|
128
|
+
return "float";
|
|
129
|
+
}
|
|
130
|
+
if (isVec4Array(value)) {
|
|
131
|
+
return `vec4[${MAX_ARRAY_LENGTH}]`;
|
|
132
|
+
}
|
|
133
|
+
if (isVec3Array(value)) {
|
|
134
|
+
return `vec3[${MAX_ARRAY_LENGTH}]`;
|
|
135
|
+
}
|
|
136
|
+
if (isVec2Array(value)) {
|
|
137
|
+
return `vec2[${MAX_ARRAY_LENGTH}]`;
|
|
138
|
+
}
|
|
139
|
+
if (isFloatArray(value)) {
|
|
140
|
+
return `float[${MAX_ARRAY_LENGTH}]`;
|
|
141
|
+
}
|
|
142
|
+
if (isVec4(value)) {
|
|
143
|
+
return "vec4";
|
|
144
|
+
}
|
|
145
|
+
if (isVec3(value)) {
|
|
146
|
+
return "vec3";
|
|
147
|
+
}
|
|
148
|
+
if (isVec2(value)) {
|
|
149
|
+
return "vec2";
|
|
150
|
+
}
|
|
151
|
+
return "float";
|
|
152
|
+
}
|
|
153
|
+
function generateUniformDeclarations(uniforms) {
|
|
154
|
+
const lines = [];
|
|
155
|
+
for (const [name, value] of Object.entries(uniforms)) {
|
|
156
|
+
const type = getUniformType(value);
|
|
157
|
+
if (type.includes("[")) {
|
|
158
|
+
const [baseType, arrayPart] = type.split("[");
|
|
159
|
+
lines.push(`uniform ${baseType} ${name}[${arrayPart};`);
|
|
160
|
+
lines.push(`uniform int ${name}_count;`);
|
|
161
|
+
} else {
|
|
162
|
+
lines.push(`uniform ${type} ${name};`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return lines.join(`
|
|
166
|
+
`);
|
|
167
|
+
}
|
|
168
|
+
var UNIFORM_MARKER = "// @UNIFORM_VALUES";
|
|
169
|
+
function injectUniformDeclarations(shaderSource, customUniforms, defaultUniforms) {
|
|
170
|
+
if (!shaderSource.includes(UNIFORM_MARKER)) {
|
|
171
|
+
return shaderSource;
|
|
172
|
+
}
|
|
173
|
+
const allUniforms = { ...defaultUniforms, ...customUniforms };
|
|
174
|
+
const declarations = generateUniformDeclarations(allUniforms);
|
|
175
|
+
return shaderSource.replace(UNIFORM_MARKER, declarations);
|
|
176
|
+
}
|
|
91
177
|
|
|
92
178
|
// src/hooks/useWebGL.ts
|
|
93
|
-
|
|
179
|
+
var DEFAULT_UNIFORM_TYPES = {
|
|
180
|
+
iTime: 0,
|
|
181
|
+
iMouse: [0, 0],
|
|
182
|
+
iMouseNormalized: [0, 0],
|
|
183
|
+
iMouseLeftDown: 0,
|
|
184
|
+
iResolution: [0, 0]
|
|
185
|
+
};
|
|
186
|
+
function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
|
|
94
187
|
const gl = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
|
95
188
|
if (!gl) {
|
|
96
189
|
throw new Error("WebGL not supported");
|
|
97
190
|
}
|
|
98
|
-
const
|
|
191
|
+
const processedVertex = injectUniformDeclarations(vertexSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
192
|
+
const processedFragment = injectUniformDeclarations(fragmentSource, customUniforms, DEFAULT_UNIFORM_TYPES);
|
|
193
|
+
const program = createShaderProgram(gl, processedVertex, processedFragment);
|
|
99
194
|
const positionBuffer = gl.createBuffer();
|
|
100
195
|
if (!positionBuffer) {
|
|
101
196
|
throw new Error("Failed to create position buffer");
|
|
@@ -126,18 +221,23 @@ function useWebGL(options) {
|
|
|
126
221
|
const elapsedTimeRef = useRef(0);
|
|
127
222
|
const lastFrameTimeRef = useRef(0);
|
|
128
223
|
const mouseRef = useRef([0, 0]);
|
|
224
|
+
const mouseNormalizedRef = useRef([0, 0]);
|
|
129
225
|
const mouseLeftDownRef = useRef(false);
|
|
130
226
|
const canvasRectRef = useRef(null);
|
|
131
227
|
const contextLostRef = useRef(false);
|
|
132
228
|
const uniformsRef = useRef(options.uniforms);
|
|
133
229
|
const onErrorRef = useRef(options.onError);
|
|
134
230
|
const onFrameRef = useRef(options.onFrame);
|
|
231
|
+
const onClickRef = useRef(options.onClick);
|
|
232
|
+
const onMouseMoveRef = useRef(options.onMouseMove);
|
|
135
233
|
const timeScaleRef = useRef(options.timeScale ?? 1);
|
|
136
234
|
const vertexRef = useRef(options.vertex);
|
|
137
235
|
const fragmentRef = useRef(options.fragment);
|
|
138
236
|
uniformsRef.current = options.uniforms;
|
|
139
237
|
onErrorRef.current = options.onError;
|
|
140
238
|
onFrameRef.current = options.onFrame;
|
|
239
|
+
onClickRef.current = options.onClick;
|
|
240
|
+
onMouseMoveRef.current = options.onMouseMove;
|
|
141
241
|
timeScaleRef.current = options.timeScale ?? 1;
|
|
142
242
|
vertexRef.current = options.vertex;
|
|
143
243
|
fragmentRef.current = options.fragment;
|
|
@@ -173,9 +273,15 @@ function useWebGL(options) {
|
|
|
173
273
|
gl.enableVertexAttribArray(positionAttributeLocation);
|
|
174
274
|
gl.bindBuffer(gl.ARRAY_BUFFER, state.positionBuffer);
|
|
175
275
|
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
|
276
|
+
const minDimension = Math.min(canvas.width, canvas.height) || 1;
|
|
277
|
+
mouseNormalizedRef.current = [
|
|
278
|
+
(mouseRef.current[0] - canvas.width / 2) / minDimension,
|
|
279
|
+
(mouseRef.current[1] - canvas.height / 2) / minDimension
|
|
280
|
+
];
|
|
176
281
|
const defaultUniforms = {
|
|
177
282
|
iTime: elapsedTime,
|
|
178
283
|
iMouse: mouseRef.current,
|
|
284
|
+
iMouseNormalized: mouseNormalizedRef.current,
|
|
179
285
|
iMouseLeftDown: mouseLeftDownRef.current ? 1 : 0,
|
|
180
286
|
iResolution: [canvas.width, canvas.height]
|
|
181
287
|
};
|
|
@@ -189,7 +295,9 @@ function useWebGL(options) {
|
|
|
189
295
|
deltaTime,
|
|
190
296
|
time: elapsedTime,
|
|
191
297
|
resolution: [canvas.width, canvas.height],
|
|
192
|
-
mouse: mouseRef.current
|
|
298
|
+
mouse: mouseRef.current,
|
|
299
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
300
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
193
301
|
});
|
|
194
302
|
}
|
|
195
303
|
animationFrameRef.current = requestAnimationFrame(render);
|
|
@@ -200,7 +308,7 @@ function useWebGL(options) {
|
|
|
200
308
|
return;
|
|
201
309
|
const initialize = () => {
|
|
202
310
|
try {
|
|
203
|
-
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current);
|
|
311
|
+
stateRef.current = initializeWebGL(canvas, vertexRef.current, fragmentRef.current, uniformsRef.current);
|
|
204
312
|
elapsedTimeRef.current = 0;
|
|
205
313
|
lastFrameTimeRef.current = 0;
|
|
206
314
|
contextLostRef.current = false;
|
|
@@ -255,6 +363,19 @@ function useWebGL(options) {
|
|
|
255
363
|
const x = (event.clientX - rect.left) * dpr;
|
|
256
364
|
const y = (rect.height - (event.clientY - rect.top)) * dpr;
|
|
257
365
|
mouseRef.current = [x, y];
|
|
366
|
+
const minDimension = Math.min(canvas.width, canvas.height) || 1;
|
|
367
|
+
mouseNormalizedRef.current = [
|
|
368
|
+
(mouseRef.current[0] - canvas.width / 2) / minDimension,
|
|
369
|
+
(mouseRef.current[1] - canvas.height / 2) / minDimension
|
|
370
|
+
];
|
|
371
|
+
onMouseMoveRef.current?.({
|
|
372
|
+
deltaTime: 0,
|
|
373
|
+
time: elapsedTimeRef.current,
|
|
374
|
+
resolution: [canvas.width, canvas.height],
|
|
375
|
+
mouse: mouseRef.current,
|
|
376
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
377
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
378
|
+
});
|
|
258
379
|
};
|
|
259
380
|
const handleMouseDown = (event) => {
|
|
260
381
|
if (event.button === 0) {
|
|
@@ -266,15 +387,29 @@ function useWebGL(options) {
|
|
|
266
387
|
mouseLeftDownRef.current = false;
|
|
267
388
|
}
|
|
268
389
|
};
|
|
390
|
+
const handleClick = () => {
|
|
391
|
+
if (!onClickRef.current)
|
|
392
|
+
return;
|
|
393
|
+
onClickRef.current({
|
|
394
|
+
deltaTime: 0,
|
|
395
|
+
time: elapsedTimeRef.current,
|
|
396
|
+
resolution: [canvas.width, canvas.height],
|
|
397
|
+
mouse: mouseRef.current,
|
|
398
|
+
mouseNormalized: mouseNormalizedRef.current,
|
|
399
|
+
mouseLeftDown: mouseLeftDownRef.current
|
|
400
|
+
});
|
|
401
|
+
};
|
|
269
402
|
window.addEventListener("mousemove", handleMouseMove);
|
|
270
403
|
window.addEventListener("mousedown", handleMouseDown);
|
|
271
404
|
window.addEventListener("mouseup", handleMouseUp);
|
|
405
|
+
canvas.addEventListener("click", handleClick);
|
|
272
406
|
return () => {
|
|
273
407
|
resizeObserver.disconnect();
|
|
274
408
|
window.removeEventListener("scroll", updateRect);
|
|
275
409
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
276
410
|
window.removeEventListener("mousedown", handleMouseDown);
|
|
277
411
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
412
|
+
canvas.removeEventListener("click", handleClick);
|
|
278
413
|
};
|
|
279
414
|
}, []);
|
|
280
415
|
return { canvasRef, mouseRef };
|
|
@@ -296,7 +431,9 @@ function ReactShader({
|
|
|
296
431
|
uniforms,
|
|
297
432
|
fullscreen = false,
|
|
298
433
|
timeScale = 1,
|
|
299
|
-
onFrame
|
|
434
|
+
onFrame,
|
|
435
|
+
onClick,
|
|
436
|
+
onMouseMove
|
|
300
437
|
}) {
|
|
301
438
|
const [error, setError] = useState(null);
|
|
302
439
|
const handleError = useCallback2((err) => {
|
|
@@ -317,6 +454,8 @@ function ReactShader({
|
|
|
317
454
|
uniforms,
|
|
318
455
|
onError: handleError,
|
|
319
456
|
onFrame: handleFrame,
|
|
457
|
+
onClick,
|
|
458
|
+
onMouseMove,
|
|
320
459
|
timeScale
|
|
321
460
|
});
|
|
322
461
|
const containerStyle = fullscreen ? {
|
|
@@ -362,5 +501,6 @@ function ReactShader({
|
|
|
362
501
|
}, undefined, false, undefined, this);
|
|
363
502
|
}
|
|
364
503
|
export {
|
|
504
|
+
generateUniformDeclarations,
|
|
365
505
|
ReactShader
|
|
366
506
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -2,7 +2,11 @@ import type { FrameInfo } from "./hooks/useWebGL";
|
|
|
2
2
|
export type Vec2 = [number, number];
|
|
3
3
|
export type Vec3 = [number, number, number];
|
|
4
4
|
export type Vec4 = [number, number, number, number];
|
|
5
|
-
export type
|
|
5
|
+
export type FloatArray = number[];
|
|
6
|
+
export type Vec2Array = Vec2[];
|
|
7
|
+
export type Vec3Array = Vec3[];
|
|
8
|
+
export type Vec4Array = Vec4[];
|
|
9
|
+
export type UniformValue = number | Vec2 | Vec3 | Vec4 | FloatArray | Vec2Array | Vec3Array | Vec4Array;
|
|
6
10
|
export interface ReactShaderProps {
|
|
7
11
|
className?: string;
|
|
8
12
|
fragment: string;
|
|
@@ -11,10 +15,13 @@ export interface ReactShaderProps {
|
|
|
11
15
|
fullscreen?: boolean;
|
|
12
16
|
timeScale?: number;
|
|
13
17
|
onFrame?: (info: FrameInfo) => void;
|
|
18
|
+
onClick?: (info: FrameInfo) => void;
|
|
19
|
+
onMouseMove?: (info: FrameInfo) => void;
|
|
14
20
|
}
|
|
15
21
|
export interface DefaultUniforms {
|
|
16
22
|
iTime: number;
|
|
17
23
|
iMouse: Vec2;
|
|
24
|
+
iMouseNormalized: Vec2;
|
|
18
25
|
iMouseLeftDown: number;
|
|
19
26
|
iResolution: Vec2;
|
|
20
27
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAE9B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAEvG,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,IAAI,CAAA;IACZ,gBAAgB,EAAE,IAAI,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;CAClB"}
|
package/dist/utils/uniforms.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { UniformValue } from "../types";
|
|
2
2
|
type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
3
|
+
export declare const MAX_ARRAY_LENGTH = 100;
|
|
3
4
|
export declare function setUniform(gl: WebGLContext, location: WebGLUniformLocation | null, value: UniformValue): void;
|
|
4
5
|
export declare function getUniformLocation(gl: WebGLContext, program: WebGLProgram, name: string): WebGLUniformLocation | null;
|
|
5
6
|
export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null>): void;
|
|
6
7
|
export declare function createUniformLocationCache(): Map<string, WebGLUniformLocation | null>;
|
|
8
|
+
export declare function generateUniformDeclarations(uniforms: Record<string, UniformValue>): string;
|
|
9
|
+
export declare function injectUniformDeclarations(shaderSource: string, customUniforms: Record<string, UniformValue> | undefined, defaultUniforms: Record<string, UniformValue>): string;
|
|
7
10
|
export {};
|
|
8
11
|
//# sourceMappingURL=uniforms.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAqD,MAAM,UAAU,CAAA;AAE3G,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAkCnC,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAsB7G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAErH;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,GACtD,IAAI,CAsBN;AAED,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAErF;AA8BD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAa1F;AAID,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,SAAS,EACxD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC5C,MAAM,CAQR"}
|