Skip to content

Commit

Permalink
Added iMouse as uniform
Browse files Browse the repository at this point in the history
iMouse has normalized values [x, y, x_prev, y_prev]
  • Loading branch information
reindernijhoff committed Jun 18, 2024
1 parent 0590252 commit e537d66
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 115 deletions.
47 changes: 11 additions & 36 deletions example/src/examples/Flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,18 @@ import flow_buffer from '../shader/flow_buffer.glsl?raw';
import ImageLoader from "../utils/ImageLoader";

export default class Flow {
constructor(wrapper, options = {}) {
this.wrapper = wrapper;
constructor(wrapper, options = {}) {
this.wrapper = wrapper;

this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, flow_image, options);
this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, flow_image, options);

this.mouseX = 0;
this.mouseY = 0;
this.prevMouseX = 0;
this.prevMouseY = 0;
this.renderer.createBuffer(0, flow_buffer);
this.renderer.buffers[0].setImage(0, this.renderer.buffers[0], {type: WebGLRenderingContext.FLOAT});
this.renderer.setImage(1, this.renderer.buffers[0]);

this.renderer.createBuffer(0, flow_buffer);
this.renderer.buffers[0].setImage(0, this.renderer.buffers[0], {type: WebGLRenderingContext.FLOAT});
this.renderer.setImage(1, this.renderer.buffers[0]);

const canvas = this.renderer.canvas;

canvas.onmousedown = () => {
this.mouseDown = true;
};

canvas.onmouseenter = canvas.onmousemove = (e) => {
const bounds = canvas.getBoundingClientRect();
const x = Math.max(0, Math.min(1, (e.clientX - bounds.left) / bounds.width));
const y = Math.max(0, Math.min(1, (e.clientY - bounds.top) / bounds.height));
this.mouseX = x;
this.mouseY = 1 - y;
};

this.renderer.tick(() => {
this.renderer.buffers[0].setUniformVec4('uMouse', this.mouseX, this.mouseY, this.prevMouseX, this.prevMouseY);
this.prevMouseX = this.mouseX;
this.prevMouseY = this.mouseY;
});

ImageLoader.loadImages(['./paddo.jpg']).then(([mask]) => {
this.renderer.setImage(0, mask, {flipY: true});
this.renderer.play();
});
}
ImageLoader.loadImages(['./paddo.jpg']).then(([mask]) => {
this.renderer.setImage(0, mask, {flipY: true});
this.renderer.play();
});
}
}
101 changes: 37 additions & 64 deletions example/src/examples/FluidDynamics.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,41 @@ import fluid_paint from '../shader/fluid_paint.glsl?raw';
import fluid_image from '../shader/fluid_image.glsl?raw';

export default class FluidDynamics {
constructor(wrapper, options = {}) {
this.wrapper = wrapper;

this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, fluid_image, {loop: true, ...options});

this.mouseX = 0;
this.mouseY = 0;
this.prevMouseX = 0;
this.prevMouseY = 0;

// fluid dynamics
this.renderer.createBuffer(0, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false,
pixelRatio: 0.5
});
this.renderer.createBuffer(1, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false,
pixelRatio: 0.5
});
this.renderer.createBuffer(2, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false,
pixelRatio: 0.5
});

this.renderer.buffers[0].setImage(0, this.renderer.buffers[2]);
this.renderer.buffers[1].setImage(0, this.renderer.buffers[0]);
this.renderer.buffers[2].setImage(0, this.renderer.buffers[1]);

// fluid paint
this.renderer.createBuffer(3, fluid_paint, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false
});
this.renderer.buffers[3].setImage(0, this.renderer.buffers[2]);
this.renderer.buffers[3].setImage(1, this.renderer.buffers[3]);

this.renderer.setImage(0, this.renderer.buffers[3]);

const canvas = this.renderer.canvas;

canvas.onmouseenter = canvas.onmousemove = (e) => {
const bounds = canvas.getBoundingClientRect();
const x = Math.max(0, Math.min(1, (e.clientX - bounds.left) / bounds.width));
const y = Math.max(0, Math.min(1, (e.clientY - bounds.top) / bounds.height));
this.mouseX = x;
this.mouseY = 1 - y;
};

this.renderer.tick(() => {
this.renderer.buffers[0].setUniformVec4('uMouse', this.mouseX, this.mouseY, this.prevMouseX, this.prevMouseY);
this.renderer.buffers[1].setUniformVec4('uMouse', this.mouseX, this.mouseY, this.prevMouseX, this.prevMouseY);
this.renderer.buffers[2].setUniformVec4('uMouse', this.mouseX, this.mouseY, this.prevMouseX, this.prevMouseY);
this.renderer.buffers[3].setUniformVec4('uMouse', this.mouseX, this.mouseY, this.prevMouseX, this.prevMouseY);
this.prevMouseX = this.mouseX;
this.prevMouseY = this.mouseY;
});
}
constructor(wrapper, options = {}) {
this.wrapper = wrapper;

this.renderer = ImageEffectRenderer.createTemporary(this.wrapper, fluid_image, {loop: true, ...options});

// fluid dynamics
this.renderer.createBuffer(0, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false
});
this.renderer.createBuffer(1, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false
});
this.renderer.createBuffer(2, fluid_dynamics, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false
});

this.renderer.buffers[0].setImage(0, this.renderer.buffers[2]);
this.renderer.buffers[1].setImage(0, this.renderer.buffers[0]);
this.renderer.buffers[2].setImage(0, this.renderer.buffers[1]);

// fluid paint
this.renderer.createBuffer(3, fluid_paint, {
type: WebGLRenderingContext.FLOAT,
clampX: false,
clampY: false
});
this.renderer.buffers[3].setImage(0, this.renderer.buffers[2]);
this.renderer.buffers[3].setImage(1, this.renderer.buffers[3]);

this.renderer.setImage(0, this.renderer.buffers[3]);
}
}
6 changes: 2 additions & 4 deletions example/src/shader/flow_buffer.glsl
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
uniform vec4 uMouse;

vec3 mouseInput(vec2 uv) {
vec2 d = uv - uMouse.xy;
vec2 d = uv - iMouse.xy;
d.x *= iResolution.x / iResolution.y;
return vec3((uMouse.zw-uMouse.xy) * 20. * smoothstep(.2, 0., length(d)), 0);
return vec3((iMouse.zw-iMouse.xy) * 20. * smoothstep(.2, 0., length(d)), 0);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Expand Down
3 changes: 1 addition & 2 deletions example/src/shader/fluid_dynamics.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
uniform vec4 uMouse;
uniform float uMouseDown;

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
Expand All @@ -25,7 +24,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 viscosityForce = 0.55*(tu.xy + td.xy + tr.xy + tl.xy - 4.0*me.xy);
me.xyw = texture(iChannel0, uv - me.xy*(dt/iResolution.xy)).xyw;

vec2 externalForces = clamp(vec2(uMouse.xy - uMouse.zw) * (.4 / max(dot(uv - uMouse.xy, uv - uMouse.xy), .05)), -1., 1.);
vec2 externalForces = clamp(vec2(iMouse.xy - iMouse.zw) * (.4 / max(dot(uv - iMouse.xy, uv - iMouse.xy), .05)), -1., 1.);

// Semi−lagrangian advection.
me.xy += dt*(viscosityForce.xy + externalForces) - 0.2*DdX;
Expand Down
5 changes: 1 addition & 4 deletions example/src/shader/fluid_paint.glsl
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
uniform vec4 uMouse;
uniform float uMouseDown;

// The MIT License
// Copyright © 2015 Inigo Quilez
// https://www.shadertoy.com/view/ll2GD3
Expand All @@ -19,7 +16,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {

vec3 newCol = pal(iTime, vec3(0.5, 0.5, 0.5), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), vec3(0.0, 0.10, 0.20));

col += newCol * 0.01*distance(uMouse.xy, uMouse.zw)/(dot(uv - uMouse.xy, uv - uMouse.xy)+0.002);
col += newCol * 0.01*distance(iMouse.xy, iMouse.zw)/(dot(uv - iMouse.xy, uv - iMouse.xy)+0.002);

col = clamp(0.998 * col - 0.00005, 0., 5.);
fragColor = vec4(col, 1.);
Expand Down
31 changes: 31 additions & 0 deletions src/lib/MouseListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
let mouseX: number = -0;
let mouseY: number = -0;
let mouseBinded: boolean = false;

export function bindMouseListener(container: HTMLElement) {
if (mouseBinded) {
return;
}
mouseBinded = true;
container.addEventListener('mousemove', (event) => {
mouseX = event.clientX;
mouseY = event.clientY;
}, {passive: true});
}

export function getMousePosition(): [number, number] {
return [mouseX, mouseY];
}

export type Rect = {
left: number,
top: number,
width: number,
height: number,
}

export function getNormalizedMousePosition(container: Rect, mouse: [number, number]): [number, number] {
const x = (mouse[0] - container.left) / container.width;
const y = 1 - (mouse[1] - container.top) / container.height;
return [x, y];
}
9 changes: 8 additions & 1 deletion src/lib/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class Renderer {

public gl: WebGLInstance;
protected frame: number = 0;

protected mouse: [number, number, number, number] = [0, 0, 0, 0];
private uniforms: { [k: string]: Uniform } = {};
private textures: Texture[] = [];

Expand All @@ -55,6 +55,10 @@ export class Renderer {
return this.program.shaderCompiled;
}

public get iMouseUsed(): boolean {
return this.program.getUniformLocation('iMouse') !== null;
}

/**
* Set an image to a slot for rendering.
* Possible images can be image elements, video elements, canvas elements, or buffers.
Expand Down Expand Up @@ -210,6 +214,9 @@ export class Renderer {
this.setUniformFloat('iAspect', width / height);
this.setUniformVec2('iResolution', width, height);

const mouse = this.main.mouse;
this.setUniformVec4('iMouse', mouse[0], mouse[1], mouse[2], mouse[3]);

this.gl.setUniforms(this.uniforms, this.program);
this.gl.bindTextures(this.textures);
this.gl.drawQuad(this.program.getAttributeLocation('aPos'), this.program.getAttributeLocation('aUV'));
Expand Down
23 changes: 19 additions & 4 deletions src/lib/RendererInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {ImageEffectRendererOptions} from "./ImageEffectRenderer.js";
import {Renderer} from "./Renderer.js";
import {type BufferOptions, RendererBuffer} from "./RendererBuffer.js";
import Program from "./Program.js";
import {bindMouseListener, getMousePosition, getNormalizedMousePosition} from "./MouseListener.js";

export class RendererInstance extends Renderer {
private static index: number = 0;
Expand Down Expand Up @@ -42,10 +43,10 @@ export class RendererInstance extends Renderer {
this.canvas = <HTMLCanvasElement>this.gl.canvas;
}
Object.assign(this.canvas.style, {
inset: '0',
width: '100%',
height: '100%',
margin: '0',
inset: '0',
width: '100%',
height: '100%',
margin: '0',
display: 'block',
});

Expand All @@ -69,6 +70,10 @@ export class RendererInstance extends Renderer {
return (this.options.loop || this.drawOneFrame) && this.width > 0 && this.height > 0 && (!this.options.asyncCompile || this.allShadersCompiled);
}

public override get iMouseUsed(): boolean {
return super.iMouseUsed || this.buffers.some(buffer => buffer && buffer.iMouseUsed);
}

private get allShadersCompiled(): boolean {
return this.shaderCompiled && this.buffers.every(buffer => buffer && buffer.shaderCompiled);
}
Expand Down Expand Up @@ -143,6 +148,12 @@ export class RendererInstance extends Renderer {

this.tickFuncs.forEach(func => func(dt));

if (this.iMouseUsed) {
const xprev = this.mouse[0], yprev = this.mouse[1];
const [x, y] = getNormalizedMousePosition(this.container.getBoundingClientRect(), getMousePosition());
this.mouse = [x, y, xprev, yprev];
}

// update buffers
this.buffers.forEach(buffer => {
if (buffer) {
Expand All @@ -164,6 +175,10 @@ export class RendererInstance extends Renderer {
this._ready = true;
this.readyFuncs.forEach(func => func());
this.readyFuncs = [];

if (this.iMouseUsed) {
bindMouseListener(document.body);
}
}
}
}
Expand Down

0 comments on commit e537d66

Please sign in to comment.