@needle-tools/facefilter
Version:
Needle Engine FaceFilter
199 lines (197 loc) • 8.4 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { getParam, makeIdFromRandomWords, serializable, setParamWithoutReload, showBalloonMessage, showBalloonWarning, syncField } from "@needle-tools/engine";
import { FaceMeshBehaviour } from "../facemesh/FaceMeshBehaviour";
import { ShaderMaterial, Texture, Vector3, Vector4 } from "three";
/*
Shader Inputs
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform float iTime; // shader playback time (in seconds)
uniform float iTimeDelta; // render time (in seconds)
uniform float iFrameRate; // shader frame rate
uniform int iFrame; // shader playback frame
uniform float iChannelTime[4]; // channel playback time (in seconds)
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube
uniform vec4 iDate; // (year, month, day, time in seconds)
*/
const inputsChunk = `
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform float iTime; // shader playback time (in seconds)
uniform float iTimeDelta; // render time (in seconds)
uniform float iFrameRate; // shader frame rate
uniform int iFrame; // shader playback frame
// channelPlaybackTime
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
uniform vec4 iDate; // (year, month, day, time in seconds)
uniform sampler2D iChannel0;
`;
const mainChunk = `
uniform sampler2D mask;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(0.3, 1.0, 0.4, 1.0);
mainImage(gl_FragColor, gl_FragCoord.xy);
#ifdef USE_MASK
vec4 maskColor = texture2D(mask, vUv);
gl_FragColor.a *= maskColor.r;
#endif
}
`;
const fragmentShader = `
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ vec2 uv = fragCoord/iResolution.xy;
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
fragColor = vec4(col,1.0);
}
`;
class ShaderToyMaterial extends ShaderMaterial {
constructor(args) {
super(args);
if (!args?.fragmentShader) {
let shader = inputsChunk;
shader += fragmentShader;
shader += mainChunk;
this.fragmentShader = shader;
}
this.vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
this.transparent = true;
this.uniforms = {
iResolution: { value: new Vector3(100, 100, 1) },
iTime: { value: 0 },
iTimeDelta: { value: 0 },
iFrameRate: { value: 0 },
iFrame: { value: 0 },
iChannelTime: { value: [0, 0, 0, 0] },
iChannelResolution: { value: [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1)] },
iMouse: { value: new Vector4(0, 0, 0, 0) },
iDate: { value: new Vector4(0, 0, 0, 0) },
iChannel0: { value: null },
iChannel1: { value: null },
iChannel2: { value: null },
iChannel3: { value: null },
...this.uniforms,
};
}
update(filter) {
const context = filter.context;
this.uniforms.iResolution.value.set(context.domWidth * window.devicePixelRatio, context.domHeight * window.devicePixelRatio, 1);
this.uniforms.iTime.value = context.time.realtimeSinceStartup;
this.uniforms.iTimeDelta.value = context.time.deltaTime;
this.uniforms.iFrameRate.value = context.time.smoothedFps;
this.uniforms.iFrame.value = context.time.frameCount;
this.uniforms.iChannelTime.value[0] = context.time.realtimeSinceStartup;
// this.uniforms.iChannelResolution.value[0].set(context.domWidth, context.domHeight, 1);
const pointerPosition = context.input.getPointerPosition(0);
if (pointerPosition)
this.uniforms.iMouse.value.set(pointerPosition.x, pointerPosition.y, context.input.getPointerDown(0), 0);
const time = new Date();
this.uniforms.iDate.value.set(time.getFullYear(), time.getMonth(), time.getDate(), time.getTime());
}
}
export class ShaderToyFaceFilter extends FaceMeshBehaviour {
constructor(args) {
super();
if (args?.shader) {
this._networkedShader = args.shader;
}
}
allowPaste = true;
mask = null;
createMaterial() {
const mat = new ShaderToyMaterial({
uniforms: {
mask: { value: this.mask }
},
defines: {
USE_MASK: this.mask ? true : false
}
});
if (this._networkedShader) {
this.trySetShader(this._networkedShader, mat);
}
return mat;
}
awake() {
const info = `Copy paste <a href=\"https://shadertoy.com\" target=\"_blank\">shadertoy</a> shaders (the whole code) to use as a face filter.<br/>For example <a href=\"https://www.shadertoy.com/view/tlVGDt\" target=\"_blank\">this one</a> or <a href=\"https://www.shadertoy.com/view/ftSSRR\" target=\"_blank\">this one</a> or <a href=\"https://www.shadertoy.com/new\" target=\"_blank\">create your own</a>.`;
if (!this._networkedShader)
showBalloonMessage(info);
console.debug(info);
}
onEnable() {
super.onEnable();
window.addEventListener("paste", this.onPaste);
let shaderRoomName = getParam("shader");
if (typeof shaderRoomName != "string" || shaderRoomName.length < 1) {
shaderRoomName = makeIdFromRandomWords();
setParamWithoutReload("shader", shaderRoomName);
}
this.context.connection.joinRoom(shaderRoomName);
}
onDisable() {
super.onDisable();
window.removeEventListener("paste", this.onPaste);
const room = getParam("shader");
if (room) {
this.context.connection.leaveRoom(room);
}
}
update() {
const material = this.material;
if (material) {
material.update(this);
}
}
_networkedShader = null;
onPaste = (e) => {
if (!e.clipboardData)
return;
if (!this.allowPaste)
return;
const text = e.clipboardData.getData("text");
if (text) {
this.trySetShader(text);
}
};
trySetShader(shader, target) {
if (shader.includes("void mainImage")) {
const material = (target || this.material);
if (material) {
material.fragmentShader = inputsChunk + shader + mainChunk;
material.needsUpdate = true;
if (shader != this._networkedShader) {
this._networkedShader = shader;
}
}
}
else {
showBalloonWarning("The pasted text does not contain a mainImage function / is not a ShaderToy shader.");
}
}
onShaderChanged() {
if (this._networkedShader)
this.trySetShader(this._networkedShader);
}
}
__decorate([
serializable()
], ShaderToyFaceFilter.prototype, "allowPaste", void 0);
__decorate([
serializable(Texture)
], ShaderToyFaceFilter.prototype, "mask", void 0);
__decorate([
syncField(ShaderToyFaceFilter.prototype.onShaderChanged)
], ShaderToyFaceFilter.prototype, "_networkedShader", void 0);
//# sourceMappingURL=ShaderToy.js.map