Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support OffscreenCanvas #31

Merged
merged 7 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.13.1
20.16.0
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
### 0.9.0 (2024-08-13)

*New:*

- Added `afterDraw` callback to kampos config. Used to pass a function that will be called after each draw call.
- Added optional function argument `afterDraw` to `kampos#play()` method to be dynamically set to `kampos.config.afterDraw`.
- Added optional boolean argument `skipTextureCreation` to `kampos#setSource()` method to skip texture creation for the source
media. useful for cases where using an OffscreenCanvas as a source for multiple programs and need to switch between them.

### 0.8.0 (2023-04-01)

*Breaking:*
Expand Down
38 changes: 32 additions & 6 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset='utf-8'>
<title>kampos 0.8.0 | Documentation</title>
<title>kampos 0.9.0 | Documentation</title>
<meta name='description' content='Tiny and fast effects compositor on WebGL'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<link href='assets/bass.css' rel='stylesheet'>
Expand All @@ -15,7 +15,7 @@
<div id='split-left' class='overflow-auto fs0 height-viewport-100'>
<div class='py1 px2'>
<h3 class='mb0 no-anchor'>kampos</h3>
<div class='mb1'><code>0.8.0</code></div>
<div class='mb1'><code>0.9.0</code></div>
<input
placeholder='Filter'
id='filter-input'
Expand Down Expand Up @@ -613,7 +613,7 @@ <h3 class='fl m0' id='kampos'>
<div class="clearfix small pointer toggle-sibling">
<div class="py1 contain">
<a class='icon pin-right py1 dark-link caret-right'>▸</a>
<span class='code strong strong truncate'>setSource(source)</span>
<span class='code strong strong truncate'>setSource(source, skipTextureCreation?)</span>
</div>
</div>
<div class="clearfix display-none toggle-target">
Expand All @@ -623,7 +623,7 @@ <h3 class='fl m0' id='kampos'>

<p>Set the source config.</p>

<div class='pre p1 fill-light mt0'>setSource(source: (ArrayBufferView | ImageData | <a href="https://developer.mozilla.org/docs/Web/API/HTMLImageElement">HTMLImageElement</a> | <a href="https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement">HTMLCanvasElement</a> | <a href="https://developer.mozilla.org/docs/Web/API/HTMLVideoElement">HTMLVideoElement</a> | ImageBitmap | <a href="#kampossource">kamposSource</a>))</div>
<div class='pre p1 fill-light mt0'>setSource(source: (ArrayBufferView | ImageData | <a href="https://developer.mozilla.org/docs/Web/API/HTMLImageElement">HTMLImageElement</a> | <a href="https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement">HTMLCanvasElement</a> | <a href="https://developer.mozilla.org/docs/Web/API/HTMLVideoElement">HTMLVideoElement</a> | ImageBitmap | <a href="#kampossource">kamposSource</a>), skipTextureCreation: <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a>?)</div>



Expand All @@ -646,6 +646,16 @@ <h3 class='fl m0' id='kampos'>

</div>

<div class='space-bottom0'>
<div>
<span class='code bold'>skipTextureCreation</span> <code class='quiet'>(<a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a>?)</code>
defaults to
<code>false</code>

</div>

</div>

</div>


Expand Down Expand Up @@ -744,7 +754,7 @@ <h3 class='fl m0' id='kampos'>
<div class="clearfix small pointer toggle-sibling">
<div class="py1 contain">
<a class='icon pin-right py1 dark-link caret-right'>▸</a>
<span class='code strong strong truncate'>play(beforeDraw)</span>
<span class='code strong strong truncate'>play(beforeDraw, afterDraw)</span>
</div>
</div>
<div class="clearfix display-none toggle-target">
Expand All @@ -755,7 +765,7 @@ <h3 class='fl m0' id='kampos'>
<p>Starts the animation loop.</p>
<p>If a <a href="#ticker">Ticker</a> is used, this instance will be added to that <a href="#ticker">Ticker</a>.</p>

<div class='pre p1 fill-light mt0'>play(beforeDraw: <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function">function</a>)</div>
<div class='pre p1 fill-light mt0'>play(beforeDraw: <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function">function</a>, afterDraw: <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function">function</a>)</div>



Expand All @@ -779,6 +789,15 @@ <h3 class='fl m0' id='kampos'>

</div>

<div class='space-bottom0'>
<div>
<span class='code bold'>afterDraw</span> <code class='quiet'>(<a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function">function</a>)</code>
function to run after each draw call

</div>

</div>

</div>


Expand Down Expand Up @@ -1402,6 +1421,13 @@ <h3 class='fl m0' id='kamposconfig'>
will not be called.


</div>

<div class='space-bottom0'>
<span class='code bold'>afterDraw</span> <code class='quiet'>(<a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function">function</a>?)</code>
: function to run after each draw call.


</div>

<div class='space-bottom0'>
Expand Down
222 changes: 165 additions & 57 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,40 @@
right: 50px;
}

#target {
.target-canvas {
position: fixed;
top: 0;
left: 0;
z-index: -2;
pointer-events: none;
}

#target2 {
left: 50%;
}

#target3 {
position: fixed;
top: 10vh;
left: 5vw;
z-index: -1;
pointer-events: none;
}

#target4 {
position: fixed;
bottom: 10vh;
right: 5vw;
z-index: -1;
pointer-events: none;
}

#video, #video2 {
position: fixed;
top: 0;
content-visibility: hidden;
}

@media (min-width: 641px) {
html, body {
margin: 0;
Expand Down Expand Up @@ -137,80 +163,162 @@
</nav>
</header>
<main>
<canvas id="target"></canvas>
<canvas class="target-canvas" id="target"></canvas>
<canvas class="target-canvas" id="target2"></canvas>
<canvas id="target3"></canvas>
<canvas id="target4"></canvas>
<h1>kampos</h1>
<h2>Tiny and fast effects compositor</h2>
<video id="video" src="https://video.wixstatic.com/video/11062b_6ceb80744f45401caf9eb666caeb9887/480p/mp4/file.mp4" loop muted preload="auto" crossorigin="anonymous" playsinline autoplay></video>
<video id="video2" src="./demo/starry-night.mp4" loop muted preload="auto" crossorigin="anonymous" playsinline autoplay></video>
</main>
<script type="module">
import {Kampos, effects, noise} from './index.js';

const target = document.querySelector('#target');
const body = document.body;
const width = body.clientWidth;
const height = body.clientHeight;

target.width = width;
target.height = height;

const mousePosition = [0.5, 0.5];

const turbulence = effects.turbulence({noise: noise.simplex, output: '\n'});
const render = {
fragment: {
uniform: {
u_resolution: 'vec2',
u_pointer: 'vec2'
},
constant: `
</body>
<script type="module">
import {Kampos, effects, noise} from './index.js';

const target = document.querySelector('#target');
const targetCtx = target.getContext('bitmaprenderer');
const target2 = document.querySelector('#target2');
const target2Ctx = target2.getContext('bitmaprenderer');
const target3 = document.querySelector('#target3');
const target3Ctx = target3.getContext('bitmaprenderer');
const target4 = document.querySelector('#target4');
const target4Ctx = target4.getContext('bitmaprenderer');

const body = document.body;
const width = body.clientWidth / 2;
const height = body.clientHeight;

target.width = width;
target.height = height;
target2.width = width;
target2.height = height;

const offscreen = new OffscreenCanvas(width, height);

const mousePosition = [0.5, 0.5];
const mousePosition2 = [0.5, 0.5];

const turbulence = effects.turbulence({noise: noise.simplex, output: '\n'});
const getRender = (c, d, mouse) => ({
fragment: {
uniform: {
u_resolution: 'vec2',
u_pointer: 'vec2'
},
constant: `
vec3 A = vec3(0.5);
vec3 B = vec3(0.5);
vec3 C = vec3(1.0, 0.7, 0.4);
vec3 D = vec3(0.0, 0.15, 0.2);
vec3 C = vec3(${c});
vec3 D = vec3(${d});
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}`,
main: `
main: `
vec2 st = gl_FragCoord.xy / u_resolution.xy;
float dist = distance(u_pointer, st);
alpha = smoothstep(turbulenceValue * 0.2, 0.2 + turbulenceValue * 0.2, dist);
color = palette(turbulenceValue, A, B, C, D);`
},
uniforms: [
{
name: 'u_resolution',
type: 'f',
data: [width, height]
},
uniforms: [
{
name: 'u_resolution',
type: 'f',
data: [width, height]
},
{
name: 'u_pointer',
type: 'f',
data: mousePosition
}
],
get mouse() {
return this.uniforms[1].data;
},
set mouse(data) {
this.uniforms[1].data[0] = data[0];
this.uniforms[1].data[1] = data[1];
{
name: 'u_pointer',
type: 'f',
data: mouse
}
};
],
get mouse() {
return this.uniforms[1].data;
},
set mouse(data) {
this.uniforms[1].data[0] = data[0];
this.uniforms[1].data[1] = data[1];
}
});

const render = getRender([1.0, 0.7, 0.4], [0.0, 0.15, 0.2], mousePosition);
const render2 = getRender([0.0, 0.9, 0.6], [0.6, 0.15, 0.5], mousePosition2);

const FREQUENCY = 2 / (width > height ? width : height);

const FREQUENCY = 2 / (width > height ? width : height);
turbulence.frequency = {x: FREQUENCY, y: FREQUENCY};
turbulence.octaves = 6;

turbulence.frequency = {x: FREQUENCY, y: FREQUENCY};
turbulence.octaves = 6;
const instance = new Kampos({target: offscreen, effects:[turbulence, render], noSource: true});
const instance2 = new Kampos({target: offscreen, effects:[turbulence, render2], noSource: true});
const source = {width, height};

const instance = new Kampos({target, effects:[turbulence, render], noSource: true});
instance.play((time) => {
render.mouse = mousePosition;
turbulence.time = time * 2;
instance.play((time) => {
instance.setSource(source, true);
render.mouse = mousePosition;
turbulence.time = time * 2;
}, () => {
const bitmap = offscreen.transferToImageBitmap();
targetCtx.transferFromImageBitmap(bitmap);
});

instance2.play((time) => {
instance.setSource(source, true);
render2.mouse = mousePosition2;
turbulence.time = time * 3;
}, () => {
const bitmap = offscreen.transferToImageBitmap();
target2Ctx.transferFromImageBitmap(bitmap);
});

document.body.addEventListener('mousemove', (e) => {
mousePosition[0] = e.clientX / width;
mousePosition2[0] = (e.clientX - width) / width;
mousePosition[1] = (height - e.clientY) / height;
mousePosition2[1] = (height - e.clientY) / height;
});

const video = document.querySelector('#video');
video.addEventListener('timeupdate', () => {
const duotone = effects.duotone();
const instance3 = new Kampos({target: offscreen, effects:[duotone]});

const width = video.videoWidth;
const height = video.videoHeight;
const source = {media: video, width, height};
target3.width = width;
target3.height = height;
instance3.setSource(source);

instance3.play((time) => {
instance3.setSource(source, true);
}, () => {
const bitmap = offscreen.transferToImageBitmap();
target3Ctx.transferFromImageBitmap(bitmap);
});
}, {once: true});

document.body.addEventListener('mousemove', (e) => {
mousePosition[0] = e.clientX / width;
mousePosition[1] = (height - e.clientY) / height;
const video2 = document.querySelector('#video2');
video2.addEventListener('timeupdate', () => {
const duotone = effects.duotone({
light: [1, 1, 1, 1],
dark: [0, 0, 0, 1]
});
</script>
</body>
const instance4 = new Kampos({target: offscreen, effects:[duotone]});

const width = video2.videoWidth;
const height = video2.videoHeight;
const source = {media: video2, width, height};
target4.width = width;
target4.height = height;
instance4.setSource(source);

instance4.play((time) => {
instance4.setSource(source, true);
}, () => {
const bitmap = offscreen.transferToImageBitmap();
target4Ctx.transferFromImageBitmap(bitmap);
});
}, {once: true});
</script>
</html>
Loading
Loading