multiplane 0.3.6 → 0.3.8
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/LICENSE +202 -202
- package/README.en.md +3 -0
- package/README.md +3 -0
- package/THIRD_PARTY_NOTICES.md +30 -30
- package/bin/multiplane +24 -14
- package/examples/alpha.html +18 -18
- package/examples/animation.html +17 -17
- package/examples/assets/OFL.txt +93 -93
- package/examples/assets/card.svg +6 -6
- package/examples/three-cards.html +17 -17
- package/examples/typography.html +17 -17
- package/licenses/GPL-3.0.txt +232 -232
- package/licenses/LGPL-3.0.txt +304 -304
- package/licenses/acorn-LICENSE +21 -21
- package/licenses/colour-LICENSE.md +82 -82
- package/licenses/detect-libc-LICENSE +201 -201
- package/licenses/entities-LICENSE +11 -11
- package/licenses/nanoid-LICENSE +20 -20
- package/licenses/parse5-LICENSE +19 -19
- package/licenses/picocolors-LICENSE +15 -15
- package/licenses/playwright-NOTICE +5 -5
- package/licenses/playwright-ThirdPartyNotices.txt +14 -14
- package/licenses/playwright-core-ThirdPartyNotices.txt +13 -13
- package/licenses/postcss-LICENSE +20 -20
- package/licenses/postcss-value-parser-LICENSE +22 -22
- package/licenses/semver-LICENSE +15 -15
- package/licenses/sharp-LICENSE +191 -191
- package/licenses/sharp-win32-x64-NOTICES.md +45 -45
- package/licenses/source-map-js-LICENSE +28 -28
- package/licenses/three-LICENSE +21 -21
- package/package.json +92 -92
- package/skills/multiplane/SKILL.md +3 -0
- package/src/alpha.mjs +69 -69
- package/src/artifacts.mjs +61 -61
- package/src/browser-recovery.mjs +26 -26
- package/src/capture-watchdog.mjs +29 -0
- package/src/capture.mjs +34 -18
- package/src/encoder.mjs +16 -16
- package/src/environment.mjs +56 -56
- package/src/errors.mjs +1 -0
- package/src/events.mjs +12 -10
- package/src/ffmpeg.mjs +32 -32
- package/src/format.mjs +27 -27
- package/src/frame-rate.mjs +28 -28
- package/src/home-migration.mjs +54 -54
- package/src/hosted.mjs +3 -3
- package/src/index.mjs +1 -0
- package/src/input-records.mjs +33 -0
- package/src/jobs.mjs +3 -1
- package/src/lint.mjs +248 -248
- package/src/output-alpha.mjs +36 -36
- package/src/output-check.mjs +110 -110
- package/src/platform.mjs +114 -114
- package/src/png-native.mjs +12 -12
- package/src/png.mjs +74 -74
- package/src/post.mjs +56 -39
- package/src/preview.mjs +23 -23
- package/src/process.mjs +9 -9
- package/src/render-pool.mjs +41 -14
- package/src/render-worker.mjs +14 -3
- package/src/render.mjs +148 -52
- package/src/resource-path.mjs +29 -29
- package/src/resources.mjs +27 -27
- package/src/resume.mjs +23 -0
- package/src/runtime/camera.js +37 -37
- package/src/runtime/shot.css +9 -9
- package/src/segment-encoder.mjs +91 -0
- package/src/segments.mjs +123 -0
- package/src/server.mjs +11 -7
- package/src/setup.mjs +51 -51
- package/src/skills.mjs +128 -128
- package/src/stills.mjs +1 -1
- package/src/three/index.js +39 -31
- package/src/verify.mjs +2 -2
- package/src/video-pipe.mjs +6 -6
- package/tools/bench/shots/canvas-2d.html +1 -1
- package/tools/bench/shots/dom-light.html +1 -1
- package/tools/bench/shots/scene.js +44 -44
- package/tools/bench/shots/webgl.html +1 -1
- package/tools/install-browser.mjs +18 -18
package/src/three/index.js
CHANGED
|
@@ -176,7 +176,7 @@ export function createThreeLayer({ stage = document.querySelector('#stage'), siz
|
|
|
176
176
|
}
|
|
177
177
|
return patterns.get(count);
|
|
178
178
|
}
|
|
179
|
-
function renderAccumulation(focus, radius, count, blur, rotation, budget = count) {
|
|
179
|
+
function renderAccumulation(focus, radius, count, blur, rotation, budget = count, stride = 1, offset = 0) {
|
|
180
180
|
const position = camera.position.clone(), projection = camera.projectionMatrix.clone();
|
|
181
181
|
const projectionInverse = camera.projectionMatrixInverse.clone();
|
|
182
182
|
const autoClear = renderer.autoClear, oldTarget = renderer.getRenderTarget();
|
|
@@ -195,7 +195,9 @@ export function createThreeLayer({ stage = document.querySelector('#stage'), siz
|
|
|
195
195
|
sumMaterial.uniforms.cameraNear.value = camera.near;
|
|
196
196
|
sumMaterial.uniforms.cameraFar.value = camera.far;
|
|
197
197
|
sumMaterial.uniforms.blurScale.value = radius * height * projection.elements[5] * 0.5 * blur / Math.sqrt(budget);
|
|
198
|
-
|
|
198
|
+
const points = pattern(count * stride);
|
|
199
|
+
for (let i = offset; i < points.length; i += stride) {
|
|
200
|
+
const [x, y, jx, jy] = points[i];
|
|
199
201
|
const dx = (x * Math.cos(rotation) - y * Math.sin(rotation)) * radius;
|
|
200
202
|
const dy = (x * Math.sin(rotation) + y * Math.cos(rotation)) * radius;
|
|
201
203
|
camera.position.copy(position).addScaledVector(right, dx).addScaledVector(up, dy);
|
|
@@ -225,34 +227,34 @@ export function createThreeLayer({ stage = document.querySelector('#stage'), siz
|
|
|
225
227
|
textures.add(texture);
|
|
226
228
|
return texture;
|
|
227
229
|
}
|
|
228
|
-
/**
|
|
229
|
-
* @param {object} options
|
|
230
|
-
* @param {number} [options.lodBias=0] Texture mip LOD bias; negative values sharpen
|
|
231
|
-
* front-facing cards but can cause moire on dense textures at oblique angles.
|
|
232
|
-
*/
|
|
233
|
-
function card({ src, width, height, radius = 0, position = [0, 0, 0], rotation = [0, 0, 0],
|
|
234
|
-
border = null, shadow = false, annotations = [], browserFrame = false, textureWidth = 2048, style = {}, lodBias = 0 }) {
|
|
235
|
-
if (!Number.isFinite(lodBias)) throw Object.assign(new Error('lodBias must be a finite number.'), { code: 'shot_contract_invalid' });
|
|
236
|
-
const textureStyle = { ...layerStyle, ...style };
|
|
237
|
-
const composeBackground = annotations.length > 0 || browserFrame || style.textureBackground !== undefined;
|
|
230
|
+
/**
|
|
231
|
+
* @param {object} options
|
|
232
|
+
* @param {number} [options.lodBias=0] Texture mip LOD bias; negative values sharpen
|
|
233
|
+
* front-facing cards but can cause moire on dense textures at oblique angles.
|
|
234
|
+
*/
|
|
235
|
+
function card({ src, width, height, radius = 0, position = [0, 0, 0], rotation = [0, 0, 0],
|
|
236
|
+
border = null, shadow = false, annotations = [], browserFrame = false, textureWidth = 2048, style = {}, lodBias = 0 }) {
|
|
237
|
+
if (!Number.isFinite(lodBias)) throw Object.assign(new Error('lodBias must be a finite number.'), { code: 'shot_contract_invalid' });
|
|
238
|
+
const textureStyle = { ...layerStyle, ...style };
|
|
239
|
+
const composeBackground = annotations.length > 0 || browserFrame || style.textureBackground !== undefined;
|
|
238
240
|
const group = new THREE.Group();
|
|
239
241
|
group.position.fromArray(position); group.rotation.set(...rotation);
|
|
240
|
-
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide, transparent: !composeBackground });
|
|
241
|
-
if (lodBias !== 0) {
|
|
242
|
-
material.onBeforeCompile = shader => {
|
|
243
|
-
shader.uniforms.cardLodBias = { value: lodBias };
|
|
244
|
-
shader.fragmentShader = 'uniform float cardLodBias;\n' + shader.fragmentShader.replace('#include <map_fragment>',
|
|
245
|
-
THREE.ShaderChunk.map_fragment.replace('texture2D( map, vMapUv )', 'texture2D( map, vMapUv, cardLodBias )'));
|
|
246
|
-
};
|
|
247
|
-
material.customProgramCacheKey = () => 'card-lod-bias';
|
|
248
|
-
}
|
|
242
|
+
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide, transparent: !composeBackground });
|
|
243
|
+
if (lodBias !== 0) {
|
|
244
|
+
material.onBeforeCompile = shader => {
|
|
245
|
+
shader.uniforms.cardLodBias = { value: lodBias };
|
|
246
|
+
shader.fragmentShader = 'uniform float cardLodBias;\n' + shader.fragmentShader.replace('#include <map_fragment>',
|
|
247
|
+
THREE.ShaderChunk.map_fragment.replace('texture2D( map, vMapUv )', 'texture2D( map, vMapUv, cardLodBias )'));
|
|
248
|
+
};
|
|
249
|
+
material.customProgramCacheKey = () => 'card-lod-bias';
|
|
250
|
+
}
|
|
249
251
|
const mesh = new THREE.Mesh(roundedGeometry(width, height, radius), material);
|
|
250
252
|
group.add(mesh);
|
|
251
253
|
if (border) {
|
|
252
254
|
const edge = new THREE.Mesh(roundedGeometry(width + 2 * border.width, height + 2 * border.width, radius + border.width),
|
|
253
|
-
new THREE.MeshBasicMaterial({ color: border.color, side: THREE.DoubleSide,
|
|
254
|
-
polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1 }));
|
|
255
|
-
group.add(edge);
|
|
255
|
+
new THREE.MeshBasicMaterial({ color: border.color, side: THREE.DoubleSide,
|
|
256
|
+
polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1 }));
|
|
257
|
+
group.add(edge);
|
|
256
258
|
}
|
|
257
259
|
if (shadow) {
|
|
258
260
|
const c = document.createElement('canvas'); c.width = c.height = 512;
|
|
@@ -336,16 +338,22 @@ export function createThreeLayer({ stage = document.querySelector('#stage'), siz
|
|
|
336
338
|
});
|
|
337
339
|
const budget = adaptive ? Math.min(maximum, Math.max(count, Math.ceil((2 * maxRadius / spacing) ** 2))) : count;
|
|
338
340
|
const probe = globalThis.__multiplaneMotionProbe;
|
|
339
|
-
// Probes retain the stable base pattern; final captures share one frame budget.
|
|
340
|
-
const divisor = probe?.motionSamples ?? motionBlur;
|
|
341
|
-
const minimum = probe?.time === undefined ? 2 : 8;
|
|
342
|
-
const actual = rotate && motionBlur > 1 ? Math.min(budget, Math.max(minimum, Math.ceil(budget / divisor))) : budget;
|
|
343
|
-
if (probe) probe.samples += actual;
|
|
341
|
+
// Probes retain the stable base pattern; final captures share one frame budget.
|
|
342
|
+
const divisor = probe?.motionSamples ?? motionBlur;
|
|
343
|
+
const minimum = probe?.time === undefined ? 2 : 8;
|
|
344
|
+
const actual = rotate && motionBlur > 1 ? Math.min(budget, Math.max(minimum, Math.ceil(budget / divisor))) : budget;
|
|
345
|
+
if (probe) probe.samples += actual;
|
|
344
346
|
if (probe && rotate && motionBlur > 1) probe.used = true;
|
|
345
347
|
const phase = (probe?.time ?? t) * 997 * Math.sqrt(2);
|
|
346
|
-
|
|
348
|
+
// Final temporal samples partition one lens pattern, including its jitter.
|
|
349
|
+
// Fixed-phase probes and direct layer calls retain their existing pattern.
|
|
350
|
+
const interleaved = rotate && motionBlur > 1 && probe?.time === undefined
|
|
351
|
+
&& Number.isInteger(probe?.motionIndex) && probe.motionIndex >= 0;
|
|
352
|
+
const stride = interleaved ? divisor : 1;
|
|
353
|
+
const rotation = !interleaved && rotate && motionBlur > 1 ? (phase - Math.floor(phase)) * Math.PI * 2 : 0;
|
|
347
354
|
Object.assign(stats, { samples: actual, maxBlurRadius: maxRadius, rotation });
|
|
348
|
-
renderAccumulation(focus, radius, actual, blur, rotation, budget
|
|
355
|
+
renderAccumulation(focus, radius, actual, blur, rotation, interleaved ? actual * stride : budget,
|
|
356
|
+
stride, interleaved ? probe.motionIndex : 0);
|
|
349
357
|
}
|
|
350
358
|
},
|
|
351
359
|
gpuInfo() {
|
package/src/verify.mjs
CHANGED
|
@@ -48,11 +48,11 @@ export async function verifyShot(shot, options = {}) {
|
|
|
48
48
|
let errors = 0, totalChangedPixels = 0;
|
|
49
49
|
for (const t of times) {
|
|
50
50
|
events.check(); references.set(t, decodePNG(await processed.frame(t)));
|
|
51
|
-
dofHistogram[processed.lastDofSamples] = (dofHistogram[processed.lastDofSamples] ?? 0) + 1;
|
|
51
|
+
dofHistogram[processed.lastDofSamples] = (dofHistogram[processed.lastDofSamples] ?? 0) + 1;
|
|
52
52
|
histogram[processed.lastBlurSamples] = (histogram[processed.lastBlurSamples] ?? 0) + 1;
|
|
53
53
|
}
|
|
54
54
|
data.post = { ...processed.post, adaptiveBlur: Number(options['adaptive-blur'] ?? 0.5) };
|
|
55
|
-
data.blurSamples = blurSampleReport(histogram);
|
|
55
|
+
data.blurSamples = blurSampleReport(histogram);
|
|
56
56
|
data.dofSamples = blurSampleReport(dofHistogram);
|
|
57
57
|
const check = async (t, actual, name, reference = references.get(t)) => {
|
|
58
58
|
events.check();
|
package/src/video-pipe.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export async function writeFrame(stream, bytes) {
|
|
2
|
-
const chunkSize = 1024 * 1024;
|
|
3
|
-
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
4
|
-
await new Promise((resolve, reject) => stream.write(bytes.subarray(offset, offset + chunkSize), error => error ? reject(error) : resolve()));
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
export async function writeFrame(stream, bytes) {
|
|
2
|
+
const chunkSize = 1024 * 1024;
|
|
3
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
4
|
+
await new Promise((resolve, reject) => stream.write(bytes.subarray(offset, offset + chunkSize), error => error ? reject(error) : resolve()));
|
|
5
|
+
}
|
|
6
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>canvas-2d</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="canvas-2d"></div><script src="scene.js"></script></body></html>
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>canvas-2d</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="canvas-2d"></div><script src="scene.js"></script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>dom-light</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="dom-light"></div><script src="scene.js"></script></body></html>
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>dom-light</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="dom-light"></div><script src="scene.js"></script></body></html>
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
const stage = document.querySelector('#stage');
|
|
2
|
-
const kind = stage.dataset.kind;
|
|
3
|
-
const tiles = [];
|
|
4
|
-
if (kind.startsWith('dom') || kind === 'mixed') {
|
|
5
|
-
const count = kind === 'dom-heavy' ? 1200 : 12;
|
|
6
|
-
for (let i = 0; i < count; i++) {
|
|
7
|
-
const tile = document.createElement('div');
|
|
8
|
-
tile.className = 'tile'; tile.textContent = `Item ${i}`;
|
|
9
|
-
tile.style.left = `${(i % 40) * 48}px`; tile.style.top = `${Math.floor(i / 40) * 35}px`;
|
|
10
|
-
if (kind === 'dom-heavy') tile.style.filter = `blur(${i % 3 * 0.15}px)`;
|
|
11
|
-
stage.append(tile); tiles.push(tile);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
let canvas, ctx, gl, program, timeLocation;
|
|
15
|
-
if (['canvas-2d', 'webgl', 'mixed'].includes(kind)) {
|
|
16
|
-
canvas = document.createElement('canvas'); stage.append(canvas);
|
|
17
|
-
canvas.width = 1920 * devicePixelRatio; canvas.height = 1080 * devicePixelRatio;
|
|
18
|
-
if (kind === 'canvas-2d') ctx = canvas.getContext('2d', { alpha: false, colorSpace: 'srgb' });
|
|
19
|
-
else {
|
|
20
|
-
gl = canvas.getContext('webgl', { alpha: kind === 'mixed', premultipliedAlpha: true, antialias: true, preserveDrawingBuffer: true });
|
|
21
|
-
if (!gl) throw Error('WebGL unavailable');
|
|
22
|
-
const shader = (type, source) => { const s = gl.createShader(type); gl.shaderSource(s, source); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw Error(gl.getShaderInfoLog(s)); return s; };
|
|
23
|
-
program = gl.createProgram();
|
|
24
|
-
gl.attachShader(program, shader(gl.VERTEX_SHADER, 'attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}'));
|
|
25
|
-
gl.attachShader(program, shader(gl.FRAGMENT_SHADER, `precision highp float;uniform float t;uniform vec2 size;void main(){vec2 p=gl_FragCoord.xy/size;float stripe=step(.5,fract((p.x+p.y)*12.+t));gl_FragColor=vec4(.15+.55*p.x,.2+.5*p.y,.25+.4*stripe,${kind === 'mixed' ? '.5' : '1.'});}`));
|
|
26
|
-
gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw Error(gl.getProgramInfoLog(program));
|
|
27
|
-
gl.useProgram(program); const b = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,b); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
|
|
28
|
-
const loc=gl.getAttribLocation(program,'p');gl.enableVertexAttribArray(loc);gl.vertexAttribPointer(loc,2,gl.FLOAT,false,0,0);
|
|
29
|
-
gl.uniform2f(gl.getUniformLocation(program,'size'),canvas.width,canvas.height);timeLocation=gl.getUniformLocation(program,'t');gl.viewport(0,0,canvas.width,canvas.height);
|
|
30
|
-
window.BENCH_GL = gl;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
window.SHOT = {
|
|
34
|
-
duration: 2, fps: 30, ready: document.fonts.load('16px Bench'),
|
|
35
|
-
render(t) {
|
|
36
|
-
for (let i=0;i<tiles.length;i++) tiles[i].style.transform=`translate(${Math.sin(t*3+i)*20}px,${Math.cos(t*2+i)*10}px)`;
|
|
37
|
-
if(ctx){
|
|
38
|
-
// Capture can temporarily change DPR; use the fixed backing-store dimensions.
|
|
39
|
-
ctx.setTransform(canvas.width/1920,0,0,canvas.height/1080,0,0);ctx.fillStyle='#18202c';ctx.fillRect(0,0,1920,1080);
|
|
40
|
-
for(let i=0;i<160;i++){ctx.fillStyle=`hsl(${i*17%360} 60% 60%)`;ctx.beginPath();ctx.arc((i%20)*96+48+Math.sin(t*3+i)*20,Math.floor(i/20)*130+65,28+i%9,0,Math.PI*2);ctx.fill();}
|
|
41
|
-
ctx.fillStyle='#ffffff80';ctx.fillRect(300+t*100,250,600,400);
|
|
42
|
-
}
|
|
43
|
-
if(gl){gl.uniform1f(timeLocation,t);gl.drawArrays(gl.TRIANGLES,0,6);gl.finish();}
|
|
44
|
-
}
|
|
1
|
+
const stage = document.querySelector('#stage');
|
|
2
|
+
const kind = stage.dataset.kind;
|
|
3
|
+
const tiles = [];
|
|
4
|
+
if (kind.startsWith('dom') || kind === 'mixed') {
|
|
5
|
+
const count = kind === 'dom-heavy' ? 1200 : 12;
|
|
6
|
+
for (let i = 0; i < count; i++) {
|
|
7
|
+
const tile = document.createElement('div');
|
|
8
|
+
tile.className = 'tile'; tile.textContent = `Item ${i}`;
|
|
9
|
+
tile.style.left = `${(i % 40) * 48}px`; tile.style.top = `${Math.floor(i / 40) * 35}px`;
|
|
10
|
+
if (kind === 'dom-heavy') tile.style.filter = `blur(${i % 3 * 0.15}px)`;
|
|
11
|
+
stage.append(tile); tiles.push(tile);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
let canvas, ctx, gl, program, timeLocation;
|
|
15
|
+
if (['canvas-2d', 'webgl', 'mixed'].includes(kind)) {
|
|
16
|
+
canvas = document.createElement('canvas'); stage.append(canvas);
|
|
17
|
+
canvas.width = 1920 * devicePixelRatio; canvas.height = 1080 * devicePixelRatio;
|
|
18
|
+
if (kind === 'canvas-2d') ctx = canvas.getContext('2d', { alpha: false, colorSpace: 'srgb' });
|
|
19
|
+
else {
|
|
20
|
+
gl = canvas.getContext('webgl', { alpha: kind === 'mixed', premultipliedAlpha: true, antialias: true, preserveDrawingBuffer: true });
|
|
21
|
+
if (!gl) throw Error('WebGL unavailable');
|
|
22
|
+
const shader = (type, source) => { const s = gl.createShader(type); gl.shaderSource(s, source); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw Error(gl.getShaderInfoLog(s)); return s; };
|
|
23
|
+
program = gl.createProgram();
|
|
24
|
+
gl.attachShader(program, shader(gl.VERTEX_SHADER, 'attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}'));
|
|
25
|
+
gl.attachShader(program, shader(gl.FRAGMENT_SHADER, `precision highp float;uniform float t;uniform vec2 size;void main(){vec2 p=gl_FragCoord.xy/size;float stripe=step(.5,fract((p.x+p.y)*12.+t));gl_FragColor=vec4(.15+.55*p.x,.2+.5*p.y,.25+.4*stripe,${kind === 'mixed' ? '.5' : '1.'});}`));
|
|
26
|
+
gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw Error(gl.getProgramInfoLog(program));
|
|
27
|
+
gl.useProgram(program); const b = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,b); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
|
|
28
|
+
const loc=gl.getAttribLocation(program,'p');gl.enableVertexAttribArray(loc);gl.vertexAttribPointer(loc,2,gl.FLOAT,false,0,0);
|
|
29
|
+
gl.uniform2f(gl.getUniformLocation(program,'size'),canvas.width,canvas.height);timeLocation=gl.getUniformLocation(program,'t');gl.viewport(0,0,canvas.width,canvas.height);
|
|
30
|
+
window.BENCH_GL = gl;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
window.SHOT = {
|
|
34
|
+
duration: 2, fps: 30, ready: document.fonts.load('16px Bench'),
|
|
35
|
+
render(t) {
|
|
36
|
+
for (let i=0;i<tiles.length;i++) tiles[i].style.transform=`translate(${Math.sin(t*3+i)*20}px,${Math.cos(t*2+i)*10}px)`;
|
|
37
|
+
if(ctx){
|
|
38
|
+
// Capture can temporarily change DPR; use the fixed backing-store dimensions.
|
|
39
|
+
ctx.setTransform(canvas.width/1920,0,0,canvas.height/1080,0,0);ctx.fillStyle='#18202c';ctx.fillRect(0,0,1920,1080);
|
|
40
|
+
for(let i=0;i<160;i++){ctx.fillStyle=`hsl(${i*17%360} 60% 60%)`;ctx.beginPath();ctx.arc((i%20)*96+48+Math.sin(t*3+i)*20,Math.floor(i/20)*130+65,28+i%9,0,Math.PI*2);ctx.fill();}
|
|
41
|
+
ctx.fillStyle='#ffffff80';ctx.fillRect(300+t*100,250,600,400);
|
|
42
|
+
}
|
|
43
|
+
if(gl){gl.uniform1f(timeLocation,t);gl.drawArrays(gl.TRIANGLES,0,6);gl.finish();}
|
|
44
|
+
}
|
|
45
45
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>webgl</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="webgl"></div><script src="scene.js"></script></body></html>
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>webgl</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="webgl"></div><script src="scene.js"></script></body></html>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
|
|
6
|
-
if (!process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD) {
|
|
7
|
-
const require = createRequire(import.meta.url);
|
|
8
|
-
const playwrightRequire = createRequire(require.resolve('playwright/package.json'));
|
|
9
|
-
// Avoid taking Playwright's installation lock when both pinned browsers already exist.
|
|
10
|
-
const { registry: { registry } } = playwrightRequire('playwright-core/lib/coreBundle');
|
|
11
|
-
const installed = ['chromium', 'chromium-headless-shell'].every(name => existsSync(registry.findExecutable(name).executablePath()));
|
|
12
|
-
if (installed) process.exit(0);
|
|
13
|
-
const result = spawnSync(process.execPath, [path.join(path.dirname(require.resolve('playwright/package.json')), 'cli.js'), 'install', 'chromium'],
|
|
14
|
-
{ stdio: 'inherit', windowsHide: true });
|
|
15
|
-
if (result.error) throw result.error;
|
|
16
|
-
if (process.platform === 'linux') console.error('If Chromium cannot launch because system libraries are missing, run npx playwright install-deps chromium.');
|
|
17
|
-
process.exitCode = result.status ?? 1;
|
|
18
|
-
}
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
if (!process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD) {
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const playwrightRequire = createRequire(require.resolve('playwright/package.json'));
|
|
9
|
+
// Avoid taking Playwright's installation lock when both pinned browsers already exist.
|
|
10
|
+
const { registry: { registry } } = playwrightRequire('playwright-core/lib/coreBundle');
|
|
11
|
+
const installed = ['chromium', 'chromium-headless-shell'].every(name => existsSync(registry.findExecutable(name).executablePath()));
|
|
12
|
+
if (installed) process.exit(0);
|
|
13
|
+
const result = spawnSync(process.execPath, [path.join(path.dirname(require.resolve('playwright/package.json')), 'cli.js'), 'install', 'chromium'],
|
|
14
|
+
{ stdio: 'inherit', windowsHide: true });
|
|
15
|
+
if (result.error) throw result.error;
|
|
16
|
+
if (process.platform === 'linux') console.error('If Chromium cannot launch because system libraries are missing, run npx playwright install-deps chromium.');
|
|
17
|
+
process.exitCode = result.status ?? 1;
|
|
18
|
+
}
|