@paper-design/shaders 0.0.31-backbuffer-experiment.0 → 0.0.31-backbuffer-experiment.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/get-shader-color-from-string.js +93 -0
- package/dist/get-shader-color-from-string.js.map +7 -0
- package/dist/get-shader-noise-texture.js +23 -0
- package/dist/get-shader-noise-texture.js.map +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +109 -0
- package/dist/index.js.map +7 -0
- package/dist/shader-color-spaces.js +96 -0
- package/dist/shader-color-spaces.js.map +7 -0
- package/dist/shader-mount.d.ts +9 -0
- package/dist/shader-mount.js +733 -0
- package/dist/shader-mount.js.map +7 -0
- package/dist/shader-sizing.js +193 -0
- package/dist/shader-sizing.js.map +7 -0
- package/dist/shader-utils.js +59 -0
- package/dist/shader-utils.js.map +7 -0
- package/dist/shaders/dithering.d.ts +6 -6
- package/dist/shaders/dithering.js +221 -0
- package/dist/shaders/dithering.js.map +7 -0
- package/dist/shaders/dot-grid.js +107 -0
- package/dist/shaders/dot-grid.js.map +7 -0
- package/dist/shaders/dot-orbit.d.ts +3 -0
- package/dist/shaders/dot-orbit.js +124 -0
- package/dist/shaders/dot-orbit.js.map +7 -0
- package/dist/shaders/god-rays.d.ts +6 -3
- package/dist/shaders/god-rays.js +133 -0
- package/dist/shaders/god-rays.js.map +7 -0
- package/dist/shaders/grain-gradient.d.ts +2 -0
- package/dist/shaders/grain-gradient.js +243 -0
- package/dist/shaders/grain-gradient.js.map +7 -0
- package/dist/shaders/liquid-metal.d.ts +25 -10
- package/dist/shaders/liquid-metal.js +221 -0
- package/dist/shaders/liquid-metal.js.map +7 -0
- package/dist/shaders/mesh-gradient.js +97 -0
- package/dist/shaders/mesh-gradient.js.map +7 -0
- package/dist/shaders/metaballs.d.ts +3 -0
- package/dist/shaders/metaballs.js +110 -0
- package/dist/shaders/metaballs.js.map +7 -0
- package/dist/shaders/neuro-noise.d.ts +10 -4
- package/dist/shaders/neuro-noise.js +81 -0
- package/dist/shaders/neuro-noise.js.map +7 -0
- package/dist/shaders/perlin-noise.d.ts +8 -8
- package/dist/shaders/perlin-noise.js +201 -0
- package/dist/shaders/perlin-noise.js.map +7 -0
- package/dist/shaders/pulsing-border.js +218 -0
- package/dist/shaders/pulsing-border.js.map +7 -0
- package/dist/shaders/simplex-noise.js +103 -0
- package/dist/shaders/simplex-noise.js.map +7 -0
- package/dist/shaders/smoke-ring.js +141 -0
- package/dist/shaders/smoke-ring.js.map +7 -0
- package/dist/shaders/spiral.d.ts +6 -6
- package/dist/shaders/spiral.js +83 -0
- package/dist/shaders/spiral.js.map +7 -0
- package/dist/shaders/swirl.d.ts +2 -0
- package/dist/shaders/swirl.js +91 -0
- package/dist/shaders/swirl.js.map +7 -0
- package/dist/shaders/voronoi.d.ts +5 -5
- package/dist/shaders/voronoi.js +143 -0
- package/dist/shaders/voronoi.js.map +7 -0
- package/dist/shaders/warp.js +128 -0
- package/dist/shaders/warp.js.map +7 -0
- package/dist/shaders/waves.d.ts +6 -6
- package/dist/shaders/waves.js +68 -0
- package/dist/shaders/waves.js.map +7 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +7 -0
- package/package.json +2 -2
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2
|
+
* Paper Shaders *
|
|
3
|
+
* https://github.com/paper-design/shaders *
|
|
4
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
5
|
+
|
|
6
|
+
const DEFAULT_MAX_PIXEL_COUNT = 1920 * 1080 * 4;
|
|
7
|
+
class ShaderMount {
|
|
8
|
+
parentElement;
|
|
9
|
+
canvasElement;
|
|
10
|
+
gl;
|
|
11
|
+
program = null;
|
|
12
|
+
uniformLocations = {};
|
|
13
|
+
/** The fragment shader that we are using */
|
|
14
|
+
fragmentShader;
|
|
15
|
+
/** Stores the RAF for the render loop */
|
|
16
|
+
rafId = null;
|
|
17
|
+
/** Time of the last rendered frame */
|
|
18
|
+
lastRenderTime = 0;
|
|
19
|
+
/** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */
|
|
20
|
+
totalFrameTime = 0;
|
|
21
|
+
/** The current speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */
|
|
22
|
+
speed = 0;
|
|
23
|
+
/** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */
|
|
24
|
+
providedUniforms;
|
|
25
|
+
/** Just a sanity check to make sure frames don't run after we're disposed */
|
|
26
|
+
hasBeenDisposed = false;
|
|
27
|
+
/** If the resolution of the canvas has changed since the last render */
|
|
28
|
+
resolutionChanged = true;
|
|
29
|
+
/** Store textures that are provided by the user */
|
|
30
|
+
textures = /* @__PURE__ */ new Map();
|
|
31
|
+
minPixelRatio;
|
|
32
|
+
maxPixelCount;
|
|
33
|
+
isSafari = isSafari();
|
|
34
|
+
// Framebuffer properties
|
|
35
|
+
framebuffer = null;
|
|
36
|
+
framebufferTexture = null;
|
|
37
|
+
framebufferWidth = 0;
|
|
38
|
+
framebufferHeight = 0;
|
|
39
|
+
framebufferSamplingProgram = null;
|
|
40
|
+
framebufferSamplingUniformLocations = {};
|
|
41
|
+
constructor(parentElement, fragmentShader, uniforms, webGlContextAttributes, speed = 0, frame = 0, minPixelRatio = 2, maxPixelCount = DEFAULT_MAX_PIXEL_COUNT) {
|
|
42
|
+
if (parentElement instanceof HTMLElement) {
|
|
43
|
+
this.parentElement = parentElement;
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error("Paper Shaders: parent element must be an HTMLElement");
|
|
46
|
+
}
|
|
47
|
+
if (!document.querySelector("style[data-paper-shaders]")) {
|
|
48
|
+
const styleElement = document.createElement("style");
|
|
49
|
+
styleElement.innerHTML = defaultStyle;
|
|
50
|
+
styleElement.setAttribute("data-paper-shaders", "");
|
|
51
|
+
document.head.prepend(styleElement);
|
|
52
|
+
}
|
|
53
|
+
const canvasElement = document.createElement("canvas");
|
|
54
|
+
this.canvasElement = canvasElement;
|
|
55
|
+
this.parentElement.prepend(canvasElement);
|
|
56
|
+
this.fragmentShader = fragmentShader;
|
|
57
|
+
this.providedUniforms = uniforms;
|
|
58
|
+
this.totalFrameTime = frame;
|
|
59
|
+
this.minPixelRatio = minPixelRatio;
|
|
60
|
+
this.maxPixelCount = maxPixelCount;
|
|
61
|
+
const gl = canvasElement.getContext("webgl2", webGlContextAttributes);
|
|
62
|
+
if (!gl) {
|
|
63
|
+
throw new Error("Paper Shaders: WebGL is not supported in this browser");
|
|
64
|
+
}
|
|
65
|
+
this.gl = gl;
|
|
66
|
+
this.framebuffer = this.gl.createFramebuffer();
|
|
67
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
|
|
68
|
+
this.framebufferTexture = this.gl.createTexture();
|
|
69
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.framebufferTexture);
|
|
70
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
71
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
72
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
73
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
74
|
+
this.initProgram();
|
|
75
|
+
this.setupPositionAttribute();
|
|
76
|
+
this.setupUniforms();
|
|
77
|
+
this.setUniformValues(this.providedUniforms);
|
|
78
|
+
this.setupResizeObserver();
|
|
79
|
+
this.setSpeed(speed);
|
|
80
|
+
this.parentElement.setAttribute("data-paper-shaders", "");
|
|
81
|
+
this.parentElement.paperShaderMount = this;
|
|
82
|
+
}
|
|
83
|
+
initProgram = () => {
|
|
84
|
+
const program = createProgram(this.gl, vertexShaderSource, this.fragmentShader);
|
|
85
|
+
if (!program) return;
|
|
86
|
+
this.program = program;
|
|
87
|
+
const framebufferVertexShader = `#version 300 es
|
|
88
|
+
precision mediump float;
|
|
89
|
+
|
|
90
|
+
layout(location = 0) in vec4 a_position;
|
|
91
|
+
out vec2 v_texCoord;
|
|
92
|
+
|
|
93
|
+
void main() {
|
|
94
|
+
gl_Position = a_position;
|
|
95
|
+
v_texCoord = a_position.xy * 0.5 + 0.5;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
const framebufferFragmentShader = `#version 300 es
|
|
99
|
+
precision mediump float;
|
|
100
|
+
uniform sampler2D u_texture;
|
|
101
|
+
uniform vec2 u_textureSize;
|
|
102
|
+
uniform vec2 u_targetSize;
|
|
103
|
+
|
|
104
|
+
in vec2 v_texCoord;
|
|
105
|
+
out vec4 fragColor;
|
|
106
|
+
|
|
107
|
+
void main() {
|
|
108
|
+
// Calculate the scale factor between target size and texture size
|
|
109
|
+
vec2 scale = u_targetSize / u_textureSize;
|
|
110
|
+
|
|
111
|
+
// Scale UVs to only sample the portion of the texture that contains our render
|
|
112
|
+
vec2 uv = v_texCoord * scale;
|
|
113
|
+
|
|
114
|
+
fragColor = texture(u_texture, uv);
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
const framebufferProgram = createProgram(this.gl, framebufferVertexShader, framebufferFragmentShader);
|
|
118
|
+
if (!framebufferProgram) return;
|
|
119
|
+
this.framebufferSamplingProgram = framebufferProgram;
|
|
120
|
+
this.framebufferSamplingUniformLocations = {
|
|
121
|
+
u_texture: this.gl.getUniformLocation(framebufferProgram, "u_texture"),
|
|
122
|
+
u_textureSize: this.gl.getUniformLocation(framebufferProgram, "u_textureSize"),
|
|
123
|
+
u_targetSize: this.gl.getUniformLocation(framebufferProgram, "u_targetSize")
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
setupPositionAttribute = () => {
|
|
127
|
+
const positionAttributeLocation = this.gl.getAttribLocation(this.program, "a_position");
|
|
128
|
+
const positionBuffer = this.gl.createBuffer();
|
|
129
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
|
|
130
|
+
const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
|
|
131
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);
|
|
132
|
+
this.gl.enableVertexAttribArray(positionAttributeLocation);
|
|
133
|
+
this.gl.vertexAttribPointer(positionAttributeLocation, 2, this.gl.FLOAT, false, 0, 0);
|
|
134
|
+
};
|
|
135
|
+
setupUniforms = () => {
|
|
136
|
+
const uniformLocations = {
|
|
137
|
+
u_time: this.gl.getUniformLocation(this.program, "u_time"),
|
|
138
|
+
u_pixelRatio: this.gl.getUniformLocation(this.program, "u_pixelRatio"),
|
|
139
|
+
u_resolution: this.gl.getUniformLocation(this.program, "u_resolution")
|
|
140
|
+
};
|
|
141
|
+
Object.entries(this.providedUniforms).forEach(([key, value]) => {
|
|
142
|
+
uniformLocations[key] = this.gl.getUniformLocation(this.program, key);
|
|
143
|
+
if (value instanceof HTMLImageElement) {
|
|
144
|
+
const aspectRatioUniformName = `${key}_aspect_ratio`;
|
|
145
|
+
uniformLocations[aspectRatioUniformName] = this.gl.getUniformLocation(this.program, aspectRatioUniformName);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
this.uniformLocations = uniformLocations;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* The scale that we should render at.
|
|
152
|
+
* - Used to target 2x rendering even on 1x screens for better antialiasing
|
|
153
|
+
* - Prevents the virtual resolution from going beyond the maximum resolution
|
|
154
|
+
* - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels
|
|
155
|
+
*/
|
|
156
|
+
renderScale = 1;
|
|
157
|
+
parentWidth = 0;
|
|
158
|
+
parentHeight = 0;
|
|
159
|
+
resizeObserver = null;
|
|
160
|
+
setupResizeObserver = () => {
|
|
161
|
+
this.resizeObserver = new ResizeObserver(([entry]) => {
|
|
162
|
+
if (entry?.borderBoxSize[0]) {
|
|
163
|
+
this.parentWidth = entry.borderBoxSize[0].inlineSize;
|
|
164
|
+
this.parentHeight = entry.borderBoxSize[0].blockSize;
|
|
165
|
+
}
|
|
166
|
+
this.handleResize();
|
|
167
|
+
});
|
|
168
|
+
this.resizeObserver.observe(this.parentElement);
|
|
169
|
+
visualViewport?.addEventListener("resize", this.handleVisualViewportChange);
|
|
170
|
+
const rect = this.parentElement.getBoundingClientRect();
|
|
171
|
+
this.parentWidth = rect.width;
|
|
172
|
+
this.parentHeight = rect.height;
|
|
173
|
+
this.handleResize();
|
|
174
|
+
};
|
|
175
|
+
// Visual viewport resize handler, mainly used to react to browser zoom changes.
|
|
176
|
+
// Wait 2 frames to align with when the resize observer callback is done (in case it might follow):
|
|
177
|
+
// - Frame 1: a paint after the visual viewport resize
|
|
178
|
+
// - Frame 2: a paint after the resize observer has been handled, if it was ever triggered
|
|
179
|
+
//
|
|
180
|
+
// Both resize observer and visual viewport will react to classic browser zoom changes,
|
|
181
|
+
// so we dedupe the callbacks, but pinch zoom only triggers the visual viewport handler.
|
|
182
|
+
resizeRafId = null;
|
|
183
|
+
handleVisualViewportChange = () => {
|
|
184
|
+
if (this.resizeRafId !== null) {
|
|
185
|
+
cancelAnimationFrame(this.resizeRafId);
|
|
186
|
+
}
|
|
187
|
+
this.resizeRafId = requestAnimationFrame(() => {
|
|
188
|
+
this.resizeRafId = requestAnimationFrame(() => {
|
|
189
|
+
this.handleResize();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
/** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */
|
|
194
|
+
handleResize = () => {
|
|
195
|
+
if (this.resizeRafId !== null) {
|
|
196
|
+
cancelAnimationFrame(this.resizeRafId);
|
|
197
|
+
}
|
|
198
|
+
const { width, height, renderScale } = this.getCanvasSize();
|
|
199
|
+
if (this.canvasElement.width !== width || this.canvasElement.height !== height || this.renderScale !== renderScale) {
|
|
200
|
+
this.renderScale = renderScale;
|
|
201
|
+
this.canvasElement.width = width;
|
|
202
|
+
this.canvasElement.height = height;
|
|
203
|
+
this.resolutionChanged = true;
|
|
204
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
205
|
+
this.render(performance.now());
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
getCanvasSize = () => {
|
|
209
|
+
const pinchZoom = visualViewport?.scale ?? 1;
|
|
210
|
+
const innerWidth = visualViewport ? visualViewport.width * visualViewport.scale : window.innerWidth;
|
|
211
|
+
const classicZoom = Math.round(1e4 * window.outerWidth / innerWidth) / 1e4;
|
|
212
|
+
const realPixelRatio = this.isSafari ? devicePixelRatio : devicePixelRatio / classicZoom;
|
|
213
|
+
const targetPixelRatio = Math.max(realPixelRatio, this.minPixelRatio);
|
|
214
|
+
const targetRenderScale = targetPixelRatio * classicZoom * pinchZoom;
|
|
215
|
+
const targetPixelWidth = this.parentWidth * targetRenderScale;
|
|
216
|
+
const targetPixelHeight = this.parentHeight * targetRenderScale;
|
|
217
|
+
const maxPixelCountHeadroom = Math.sqrt(this.maxPixelCount) / Math.sqrt(targetPixelWidth * targetPixelHeight);
|
|
218
|
+
const newRenderScale = targetRenderScale * Math.min(1, maxPixelCountHeadroom);
|
|
219
|
+
const newWidth = Math.round(this.parentWidth * newRenderScale);
|
|
220
|
+
const newHeight = Math.round(this.parentHeight * newRenderScale);
|
|
221
|
+
return {
|
|
222
|
+
width: newWidth,
|
|
223
|
+
height: newHeight,
|
|
224
|
+
renderScale: newRenderScale
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
handleResolutionChange = () => {
|
|
228
|
+
const { renderScale, width, height } = this.getCanvasSize();
|
|
229
|
+
if (this.renderScale !== renderScale || this.canvasElement.width !== width || this.canvasElement.height !== height) {
|
|
230
|
+
this.renderScale = renderScale;
|
|
231
|
+
this.canvasElement.width = width;
|
|
232
|
+
this.canvasElement.height = height;
|
|
233
|
+
this.resolutionChanged = true;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
ensureSceneTarget = (targetFramebufferWidth, targetFramebufferHeight) => {
|
|
237
|
+
if (targetFramebufferWidth <= this.framebufferWidth && targetFramebufferHeight <= this.framebufferHeight) return;
|
|
238
|
+
this.framebufferWidth = Math.max(targetFramebufferWidth, this.framebufferWidth);
|
|
239
|
+
this.framebufferHeight = Math.max(targetFramebufferHeight, this.framebufferHeight);
|
|
240
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.framebufferTexture);
|
|
241
|
+
this.gl.texImage2D(
|
|
242
|
+
this.gl.TEXTURE_2D,
|
|
243
|
+
0,
|
|
244
|
+
this.gl.RGBA,
|
|
245
|
+
this.framebufferWidth,
|
|
246
|
+
this.framebufferHeight,
|
|
247
|
+
0,
|
|
248
|
+
this.gl.RGBA,
|
|
249
|
+
this.gl.UNSIGNED_BYTE,
|
|
250
|
+
null
|
|
251
|
+
);
|
|
252
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
|
|
253
|
+
this.gl.framebufferTexture2D(
|
|
254
|
+
this.gl.FRAMEBUFFER,
|
|
255
|
+
this.gl.COLOR_ATTACHMENT0,
|
|
256
|
+
this.gl.TEXTURE_2D,
|
|
257
|
+
this.framebufferTexture,
|
|
258
|
+
0
|
|
259
|
+
);
|
|
260
|
+
const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
|
|
261
|
+
if (status !== this.gl.FRAMEBUFFER_COMPLETE) {
|
|
262
|
+
console.error("Framebuffer is not complete:", status);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
render = (currentTime) => {
|
|
266
|
+
if (this.hasBeenDisposed) return;
|
|
267
|
+
if (this.program === null || this.framebufferSamplingProgram === null) {
|
|
268
|
+
console.warn("Tried to render before program or framebuffer or gl was initialized");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const dt = currentTime - this.lastRenderTime;
|
|
272
|
+
this.lastRenderTime = currentTime;
|
|
273
|
+
if (this.speed !== 0) {
|
|
274
|
+
this.totalFrameTime += dt * this.speed;
|
|
275
|
+
}
|
|
276
|
+
const targetFramebufferWidth = Math.round(this.parentWidth * this.renderScale);
|
|
277
|
+
const targetFramebufferHeight = Math.round(this.parentHeight * this.renderScale);
|
|
278
|
+
this.ensureSceneTarget(targetFramebufferWidth, targetFramebufferHeight);
|
|
279
|
+
for (let i = 0; i < this.textures.size; i++) {
|
|
280
|
+
this.gl.activeTexture(this.gl.TEXTURE0 + i);
|
|
281
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
282
|
+
}
|
|
283
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
|
|
284
|
+
this.gl.viewport(0, 0, targetFramebufferWidth, targetFramebufferHeight);
|
|
285
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
286
|
+
this.gl.useProgram(this.program);
|
|
287
|
+
this.gl.uniform1f(this.uniformLocations.u_time, this.totalFrameTime * 1e-3);
|
|
288
|
+
if (this.resolutionChanged) {
|
|
289
|
+
this.gl.uniform2f(this.uniformLocations.u_resolution, targetFramebufferWidth, targetFramebufferHeight);
|
|
290
|
+
this.gl.uniform1f(this.uniformLocations.u_pixelRatio, this.renderScale);
|
|
291
|
+
this.resolutionChanged = false;
|
|
292
|
+
}
|
|
293
|
+
let textureUnit = 0;
|
|
294
|
+
this.textures.forEach((texture, uniformName) => {
|
|
295
|
+
const location = this.uniformLocations[uniformName];
|
|
296
|
+
if (location) {
|
|
297
|
+
this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit);
|
|
298
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
299
|
+
this.gl.uniform1i(location, textureUnit);
|
|
300
|
+
textureUnit++;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
|
|
304
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
305
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
306
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
307
|
+
this.gl.useProgram(this.framebufferSamplingProgram);
|
|
308
|
+
this.gl.activeTexture(this.gl.TEXTURE0);
|
|
309
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, this.framebufferTexture);
|
|
310
|
+
this.gl.uniform1i(this.framebufferSamplingUniformLocations.u_texture, 0);
|
|
311
|
+
this.gl.uniform2f(
|
|
312
|
+
this.framebufferSamplingUniformLocations.u_textureSize,
|
|
313
|
+
this.framebufferWidth,
|
|
314
|
+
this.framebufferHeight
|
|
315
|
+
);
|
|
316
|
+
this.gl.uniform2f(
|
|
317
|
+
this.framebufferSamplingUniformLocations.u_targetSize,
|
|
318
|
+
targetFramebufferWidth,
|
|
319
|
+
targetFramebufferHeight
|
|
320
|
+
);
|
|
321
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
|
|
322
|
+
if (this.speed !== 0) {
|
|
323
|
+
this.requestRender();
|
|
324
|
+
} else {
|
|
325
|
+
this.rafId = null;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
requestRender = () => {
|
|
329
|
+
if (this.rafId !== null) {
|
|
330
|
+
cancelAnimationFrame(this.rafId);
|
|
331
|
+
}
|
|
332
|
+
this.rafId = requestAnimationFrame(this.render);
|
|
333
|
+
};
|
|
334
|
+
/** Creates a texture from an image and sets it into a uniform value */
|
|
335
|
+
setTextureUniform = (uniformName, image) => {
|
|
336
|
+
if (!image.complete || image.naturalWidth === 0) {
|
|
337
|
+
throw new Error(`Paper Shaders: image for uniform ${uniformName} must be fully loaded`);
|
|
338
|
+
}
|
|
339
|
+
const existingTexture = this.textures.get(uniformName);
|
|
340
|
+
if (existingTexture) {
|
|
341
|
+
this.gl.deleteTexture(existingTexture);
|
|
342
|
+
}
|
|
343
|
+
const texture = this.gl.createTexture();
|
|
344
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
345
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT);
|
|
346
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT);
|
|
347
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
348
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
349
|
+
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
|
|
350
|
+
const error = this.gl.getError();
|
|
351
|
+
if (error !== this.gl.NO_ERROR || texture === null) {
|
|
352
|
+
console.error("Paper Shaders: WebGL error when uploading texture:", error);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
this.textures.set(uniformName, texture);
|
|
356
|
+
const location = this.uniformLocations[uniformName];
|
|
357
|
+
if (location) {
|
|
358
|
+
const textureUnit = this.textures.size - 1;
|
|
359
|
+
this.gl.useProgram(this.program);
|
|
360
|
+
this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit);
|
|
361
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
362
|
+
this.gl.uniform1i(location, textureUnit);
|
|
363
|
+
const aspectRatioUniformName = `${uniformName}_aspect_ratio`;
|
|
364
|
+
const aspectRatioLocation = this.uniformLocations[aspectRatioUniformName];
|
|
365
|
+
if (aspectRatioLocation) {
|
|
366
|
+
const aspectRatio = image.naturalWidth / image.naturalHeight;
|
|
367
|
+
this.gl.uniform1f(aspectRatioLocation, aspectRatio);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
/** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */
|
|
372
|
+
setUniformValues = (updatedUniforms) => {
|
|
373
|
+
this.gl.useProgram(this.program);
|
|
374
|
+
Object.entries(updatedUniforms).forEach(([key, value]) => {
|
|
375
|
+
const location = this.uniformLocations[key];
|
|
376
|
+
if (!location) {
|
|
377
|
+
console.warn(`Uniform location for ${key} not found`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (value instanceof HTMLImageElement) {
|
|
381
|
+
this.setTextureUniform(key, value);
|
|
382
|
+
} else if (Array.isArray(value)) {
|
|
383
|
+
let flatArray = null;
|
|
384
|
+
let valueLength = null;
|
|
385
|
+
if (value[0] !== void 0 && Array.isArray(value[0])) {
|
|
386
|
+
const firstChildLength = value[0].length;
|
|
387
|
+
if (value.every((arr) => arr.length === firstChildLength)) {
|
|
388
|
+
flatArray = value.flat();
|
|
389
|
+
valueLength = firstChildLength;
|
|
390
|
+
} else {
|
|
391
|
+
console.warn(`All child arrays must be the same length for ${key}`);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
flatArray = value;
|
|
396
|
+
valueLength = flatArray.length;
|
|
397
|
+
}
|
|
398
|
+
switch (valueLength) {
|
|
399
|
+
case 2:
|
|
400
|
+
this.gl.uniform2fv(location, flatArray);
|
|
401
|
+
break;
|
|
402
|
+
case 3:
|
|
403
|
+
this.gl.uniform3fv(location, flatArray);
|
|
404
|
+
break;
|
|
405
|
+
case 4:
|
|
406
|
+
this.gl.uniform4fv(location, flatArray);
|
|
407
|
+
break;
|
|
408
|
+
case 9:
|
|
409
|
+
this.gl.uniformMatrix3fv(location, false, flatArray);
|
|
410
|
+
break;
|
|
411
|
+
case 16:
|
|
412
|
+
this.gl.uniformMatrix4fv(location, false, flatArray);
|
|
413
|
+
break;
|
|
414
|
+
default:
|
|
415
|
+
console.warn(`Unsupported uniform array length: ${valueLength}`);
|
|
416
|
+
}
|
|
417
|
+
} else if (typeof value === "number") {
|
|
418
|
+
this.gl.uniform1f(location, value);
|
|
419
|
+
} else if (typeof value === "boolean") {
|
|
420
|
+
this.gl.uniform1i(location, value ? 1 : 0);
|
|
421
|
+
} else {
|
|
422
|
+
console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
};
|
|
426
|
+
/** Gets the current total animation time from 0ms */
|
|
427
|
+
getCurrentFrameTime = () => {
|
|
428
|
+
return this.totalFrameTime;
|
|
429
|
+
};
|
|
430
|
+
/** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */
|
|
431
|
+
setFrame = (newFrame) => {
|
|
432
|
+
this.totalFrameTime = newFrame;
|
|
433
|
+
this.lastRenderTime = performance.now();
|
|
434
|
+
this.render(performance.now());
|
|
435
|
+
};
|
|
436
|
+
/** Set an animation speed (or 0 to stop animation) */
|
|
437
|
+
setSpeed = (newSpeed = 1) => {
|
|
438
|
+
this.speed = newSpeed;
|
|
439
|
+
if (this.rafId === null && newSpeed !== 0) {
|
|
440
|
+
this.lastRenderTime = performance.now();
|
|
441
|
+
this.rafId = requestAnimationFrame(this.render);
|
|
442
|
+
}
|
|
443
|
+
if (this.rafId !== null && newSpeed === 0) {
|
|
444
|
+
cancelAnimationFrame(this.rafId);
|
|
445
|
+
this.rafId = null;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
/** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */
|
|
449
|
+
setMaxPixelCount = (newMaxPixelCount = DEFAULT_MAX_PIXEL_COUNT) => {
|
|
450
|
+
this.maxPixelCount = newMaxPixelCount;
|
|
451
|
+
this.handleResolutionChange();
|
|
452
|
+
};
|
|
453
|
+
/** Set the minimum pixel ratio for the shader */
|
|
454
|
+
setMinPixelRatio = (newMinPixelRatio = 2) => {
|
|
455
|
+
this.minPixelRatio = newMinPixelRatio;
|
|
456
|
+
this.handleResolutionChange();
|
|
457
|
+
};
|
|
458
|
+
/** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */
|
|
459
|
+
setUniforms = (newUniforms) => {
|
|
460
|
+
this.providedUniforms = { ...this.providedUniforms, ...newUniforms };
|
|
461
|
+
this.setUniformValues(newUniforms);
|
|
462
|
+
this.render(performance.now());
|
|
463
|
+
};
|
|
464
|
+
/** Dispose of the shader mount, cleaning up all of the WebGL resources */
|
|
465
|
+
dispose = () => {
|
|
466
|
+
this.hasBeenDisposed = true;
|
|
467
|
+
if (this.rafId !== null) {
|
|
468
|
+
cancelAnimationFrame(this.rafId);
|
|
469
|
+
this.rafId = null;
|
|
470
|
+
}
|
|
471
|
+
if (this.gl) {
|
|
472
|
+
this.textures.forEach((texture) => {
|
|
473
|
+
this.gl.deleteTexture(texture);
|
|
474
|
+
});
|
|
475
|
+
this.textures.clear();
|
|
476
|
+
if (this.framebuffer) {
|
|
477
|
+
this.gl.deleteFramebuffer(this.framebuffer);
|
|
478
|
+
this.framebuffer = null;
|
|
479
|
+
}
|
|
480
|
+
if (this.framebufferTexture) {
|
|
481
|
+
this.gl.deleteTexture(this.framebufferTexture);
|
|
482
|
+
this.framebufferTexture = null;
|
|
483
|
+
}
|
|
484
|
+
if (this.framebufferSamplingProgram) {
|
|
485
|
+
this.gl.deleteProgram(this.framebufferSamplingProgram);
|
|
486
|
+
this.framebufferSamplingProgram = null;
|
|
487
|
+
}
|
|
488
|
+
if (this.program) {
|
|
489
|
+
this.gl.deleteProgram(this.program);
|
|
490
|
+
this.program = null;
|
|
491
|
+
}
|
|
492
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
|
493
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
|
494
|
+
this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
|
|
495
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
496
|
+
this.gl.getError();
|
|
497
|
+
}
|
|
498
|
+
if (this.resizeObserver) {
|
|
499
|
+
this.resizeObserver.disconnect();
|
|
500
|
+
this.resizeObserver = null;
|
|
501
|
+
}
|
|
502
|
+
visualViewport?.removeEventListener("resize", this.handleVisualViewportChange);
|
|
503
|
+
this.uniformLocations = {};
|
|
504
|
+
this.framebufferSamplingUniformLocations = {};
|
|
505
|
+
this.parentElement.paperShaderMount = void 0;
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
const vertexShaderSource = `#version 300 es
|
|
509
|
+
precision mediump float;
|
|
510
|
+
|
|
511
|
+
layout(location = 0) in vec4 a_position;
|
|
512
|
+
|
|
513
|
+
uniform vec2 u_resolution;
|
|
514
|
+
uniform float u_pixelRatio;
|
|
515
|
+
|
|
516
|
+
uniform float u_originX;
|
|
517
|
+
uniform float u_originY;
|
|
518
|
+
uniform float u_worldWidth;
|
|
519
|
+
uniform float u_worldHeight;
|
|
520
|
+
uniform float u_fit;
|
|
521
|
+
|
|
522
|
+
uniform float u_scale;
|
|
523
|
+
uniform float u_rotation;
|
|
524
|
+
uniform float u_offsetX;
|
|
525
|
+
uniform float u_offsetY;
|
|
526
|
+
|
|
527
|
+
uniform float u_pxSize;
|
|
528
|
+
|
|
529
|
+
out vec2 v_objectUV;
|
|
530
|
+
out vec2 v_objectBoxSize;
|
|
531
|
+
out vec2 v_objectHelperBox;
|
|
532
|
+
|
|
533
|
+
out vec2 v_responsiveUV;
|
|
534
|
+
out vec2 v_responsiveBoxSize;
|
|
535
|
+
out vec2 v_responsiveHelperBox;
|
|
536
|
+
out vec2 v_responsiveBoxGivenSize;
|
|
537
|
+
|
|
538
|
+
out vec2 v_patternUV;
|
|
539
|
+
out vec2 v_patternBoxSize;
|
|
540
|
+
out vec2 v_patternHelperBox;
|
|
541
|
+
|
|
542
|
+
// #define ADD_HELPERS
|
|
543
|
+
|
|
544
|
+
vec3 getBoxSize(float boxRatio, vec2 givenBoxSize, vec2 maxBoxSize) {
|
|
545
|
+
vec2 box = vec2(0.);
|
|
546
|
+
// fit = none
|
|
547
|
+
box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y);
|
|
548
|
+
float noFitBoxWidth = box.x;
|
|
549
|
+
if (u_fit == 1.) { // fit = contain
|
|
550
|
+
box.x = boxRatio * min(maxBoxSize[0] / boxRatio, maxBoxSize[1]);
|
|
551
|
+
} else if (u_fit == 2.) { // fit = cover
|
|
552
|
+
box.x = boxRatio * max(maxBoxSize[0] / boxRatio, maxBoxSize[1]);
|
|
553
|
+
}
|
|
554
|
+
box.y = box.x / boxRatio;
|
|
555
|
+
return vec3(box, noFitBoxWidth);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
void main() {
|
|
559
|
+
gl_Position = a_position;
|
|
560
|
+
|
|
561
|
+
vec2 uv = gl_Position.xy * .5;
|
|
562
|
+
vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);
|
|
563
|
+
vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);
|
|
564
|
+
givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;
|
|
565
|
+
vec2 maxBoxSize = vec2(max(u_resolution.x, givenBoxSize.x), max(u_resolution.y, givenBoxSize.y));
|
|
566
|
+
float r = u_rotation * 3.14159265358979323846 / 180.;
|
|
567
|
+
mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));
|
|
568
|
+
vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
// ===================================================
|
|
572
|
+
// Sizing api for graphic objects with fixed ratio
|
|
573
|
+
// (currently supports only ratio = 1)
|
|
574
|
+
|
|
575
|
+
float fixedRatio = 1.;
|
|
576
|
+
vec2 fixedRatioBoxGivenSize = vec2(
|
|
577
|
+
(u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,
|
|
578
|
+
(u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize, maxBoxSize).xy;
|
|
582
|
+
vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize;
|
|
583
|
+
|
|
584
|
+
#ifdef ADD_HELPERS
|
|
585
|
+
v_objectHelperBox = uv;
|
|
586
|
+
v_objectHelperBox *= objectWorldScale;
|
|
587
|
+
v_objectHelperBox += boxOrigin * (objectWorldScale - 1.);
|
|
588
|
+
#endif
|
|
589
|
+
|
|
590
|
+
v_objectUV = uv;
|
|
591
|
+
v_objectUV *= objectWorldScale;
|
|
592
|
+
v_objectUV += boxOrigin * (objectWorldScale - 1.);
|
|
593
|
+
v_objectUV += graphicOffset;
|
|
594
|
+
v_objectUV /= u_scale;
|
|
595
|
+
v_objectUV = graphicRotation * v_objectUV;
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
// ===================================================
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
// ===================================================
|
|
602
|
+
// Sizing api for graphic objects with either givenBoxSize ratio or canvas ratio.
|
|
603
|
+
// Full-screen mode available with u_worldWidth = u_worldHeight = 0
|
|
604
|
+
|
|
605
|
+
v_responsiveBoxGivenSize = vec2(
|
|
606
|
+
(u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,
|
|
607
|
+
(u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y
|
|
608
|
+
);
|
|
609
|
+
float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;
|
|
610
|
+
v_responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize, maxBoxSize).xy;
|
|
611
|
+
vec2 responsiveBoxScale = u_resolution.xy / v_responsiveBoxSize;
|
|
612
|
+
|
|
613
|
+
#ifdef ADD_HELPERS
|
|
614
|
+
v_responsiveHelperBox = uv;
|
|
615
|
+
v_responsiveHelperBox *= responsiveBoxScale;
|
|
616
|
+
v_responsiveHelperBox += boxOrigin * (responsiveBoxScale - 1.);
|
|
617
|
+
#endif
|
|
618
|
+
|
|
619
|
+
v_responsiveUV = uv;
|
|
620
|
+
v_responsiveUV *= responsiveBoxScale;
|
|
621
|
+
v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.);
|
|
622
|
+
v_responsiveUV += graphicOffset;
|
|
623
|
+
v_responsiveUV /= u_scale;
|
|
624
|
+
v_responsiveUV.x *= responsiveRatio;
|
|
625
|
+
v_responsiveUV = graphicRotation * v_responsiveUV;
|
|
626
|
+
v_responsiveUV.x /= responsiveRatio;
|
|
627
|
+
|
|
628
|
+
// ===================================================
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
// ===================================================
|
|
632
|
+
// Sizing api for patterns
|
|
633
|
+
// (treating graphics as a image u_worldWidth x u_worldHeight size)
|
|
634
|
+
|
|
635
|
+
float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;
|
|
636
|
+
vec2 patternBoxGivenSize = vec2(
|
|
637
|
+
(u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,
|
|
638
|
+
(u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y
|
|
639
|
+
);
|
|
640
|
+
patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;
|
|
641
|
+
|
|
642
|
+
vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize, maxBoxSize);
|
|
643
|
+
v_patternBoxSize = boxSizeData.xy;
|
|
644
|
+
float patternBoxNoFitBoxWidth = boxSizeData.z;
|
|
645
|
+
vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;
|
|
646
|
+
|
|
647
|
+
#ifdef ADD_HELPERS
|
|
648
|
+
v_patternHelperBox = uv;
|
|
649
|
+
v_patternHelperBox *= patternBoxScale;
|
|
650
|
+
v_patternHelperBox += boxOrigin * (patternBoxScale - 1.);
|
|
651
|
+
#endif
|
|
652
|
+
|
|
653
|
+
v_patternUV = uv;
|
|
654
|
+
v_patternUV += graphicOffset / patternBoxScale;
|
|
655
|
+
v_patternUV += boxOrigin;
|
|
656
|
+
v_patternUV -= boxOrigin / patternBoxScale;
|
|
657
|
+
v_patternUV *= u_resolution.xy;
|
|
658
|
+
v_patternUV /= u_pixelRatio;
|
|
659
|
+
if (u_fit > 0.) {
|
|
660
|
+
v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);
|
|
661
|
+
}
|
|
662
|
+
v_patternUV /= u_scale;
|
|
663
|
+
v_patternUV = graphicRotation * v_patternUV;
|
|
664
|
+
v_patternUV += boxOrigin / patternBoxScale;
|
|
665
|
+
v_patternUV -= boxOrigin;
|
|
666
|
+
v_patternUV += .5;
|
|
667
|
+
|
|
668
|
+
// ===================================================
|
|
669
|
+
|
|
670
|
+
}`;
|
|
671
|
+
function createShader(gl, type, source) {
|
|
672
|
+
const shader = gl.createShader(type);
|
|
673
|
+
if (!shader) return null;
|
|
674
|
+
gl.shaderSource(shader, source);
|
|
675
|
+
gl.compileShader(shader);
|
|
676
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
677
|
+
console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
|
|
678
|
+
gl.deleteShader(shader);
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
return shader;
|
|
682
|
+
}
|
|
683
|
+
function createProgram(gl, vertexShaderSource2, fragmentShaderSource) {
|
|
684
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource2);
|
|
685
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
686
|
+
if (!vertexShader || !fragmentShader) return null;
|
|
687
|
+
const program = gl.createProgram();
|
|
688
|
+
if (!program) return null;
|
|
689
|
+
gl.attachShader(program, vertexShader);
|
|
690
|
+
gl.attachShader(program, fragmentShader);
|
|
691
|
+
gl.linkProgram(program);
|
|
692
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
693
|
+
console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));
|
|
694
|
+
gl.deleteProgram(program);
|
|
695
|
+
gl.deleteShader(vertexShader);
|
|
696
|
+
gl.deleteShader(fragmentShader);
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
gl.detachShader(program, vertexShader);
|
|
700
|
+
gl.detachShader(program, fragmentShader);
|
|
701
|
+
gl.deleteShader(vertexShader);
|
|
702
|
+
gl.deleteShader(fragmentShader);
|
|
703
|
+
return program;
|
|
704
|
+
}
|
|
705
|
+
const defaultStyle = `@layer paper-shaders {
|
|
706
|
+
:where([data-paper-shaders]) {
|
|
707
|
+
isolation: isolate;
|
|
708
|
+
position: relative;
|
|
709
|
+
|
|
710
|
+
& canvas {
|
|
711
|
+
contain: strict;
|
|
712
|
+
display: block;
|
|
713
|
+
position: absolute;
|
|
714
|
+
inset: 0;
|
|
715
|
+
z-index: -1;
|
|
716
|
+
width: 100%;
|
|
717
|
+
height: 100%;
|
|
718
|
+
border-radius: inherit;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}`;
|
|
722
|
+
function isPaperShaderElement(element) {
|
|
723
|
+
return "paperShaderMount" in element;
|
|
724
|
+
}
|
|
725
|
+
function isSafari() {
|
|
726
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
727
|
+
return ua.includes("safari") && !ua.includes("chrome") && !ua.includes("android");
|
|
728
|
+
}
|
|
729
|
+
export {
|
|
730
|
+
ShaderMount,
|
|
731
|
+
isPaperShaderElement
|
|
732
|
+
};
|
|
733
|
+
//# sourceMappingURL=shader-mount.js.map
|