UNPKG

@needle-tools/facefilter

Version:

Needle Engine FaceFilter

171 lines (142 loc) 7.48 kB
import { Context, ObjectUtils } from "@needle-tools/engine"; import { type NeedleFilterTrackingManager } from "./FaceFilter.js"; import { CanvasTexture, IUniform, MeshBasicMaterial, Object3D, PerspectiveCamera, ShaderMaterial, Texture, Vector3, VideoTexture, WebGLRenderTarget } from "three"; import { mirror } from "./settings.js"; class CustomVideoMaterial extends ShaderMaterial { constructor() { super({ vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform sampler2D map; varying vec2 vUv; void main() { gl_FragColor = texture2D(map, vUv); } `, uniforms: { map: { value: null }, segmentationMask: { value: null }, } }) } } export class VideoRenderer { readonly context: Context; readonly owner: NeedleFilterTrackingManager; constructor(owner: NeedleFilterTrackingManager) { this.context = owner.context this.owner = owner; } private _videoTexture: VideoTexture | null = null; private _videoQuad: Object3D | null = null; private _material!: CustomVideoMaterial; private _canvasTexture?: CanvasTexture; enable() { this._videoTexture ??= new VideoTexture(this.owner.video); this._material ??= new CustomVideoMaterial(); this._material.uniforms.map.value = this._videoTexture; this._material.depthTest = false; this._material.depthWrite = false; this._videoQuad ??= ObjectUtils.createPrimitive("Quad", { rotation: new Vector3(Math.PI, Math.PI, 0), material: this._material }); this._videoQuad.name = "Video Quad (Face Filter)"; } disable() { this._videoQuad?.removeFromParent(); } private _texture: Texture | null = null; private _downloaded = false; private _maskCanvas: HTMLCanvasElement | null = null; private _maskCanvasContext: CanvasRenderingContext2D | null = null; onUpdate() { if (this._videoTexture && this._videoQuad && this.context.mainCamera instanceof PerspectiveCamera) { if (this._videoQuad.parent !== this.context.mainCamera) { this.context.mainCamera.add(this._videoQuad); } const far = this.context.mainCamera.far; this._videoQuad.renderOrder = -1000; this._videoQuad.position.z = -far + .01; this._videoTexture.colorSpace = this.context.renderer.outputColorSpace; if (this.owner.lastImageSegmentationResults) { const mask = this.owner.lastImageSegmentationResults.confidenceMasks?.[0]?.canvas; console.log(this.owner.lastImageSegmentationResults) // const mask = this.owner.poselandmarkerResult.segmentationMasks?.[0]; // if (mask?.canvas && this.owner.poselandmarkerResult.landmarks.length >= 1) { if (mask) { // if (!this._maskCanvas) { // // console.log(this.owner.poselandmarkerResult, this.owner.poselandmarkerResult.landmarks) // this._maskCanvas = document.createElement("canvas"); // this._maskCanvasContext = this._maskCanvas.getContext("2d")!; // this._maskCanvas.width = mask.canvas.width; // this._maskCanvas.height = mask.canvas.height; // if (mask.canvas instanceof OffscreenCanvas) { // mask.canvas.convertToBlob().then(blob => { // const url = URL.createObjectURL(blob); // const a = document.createElement("a"); // a.href = url; // a.download = "mask.png"; // a.click(); // }); // } // // Draw the mask onto the temporary canvas // this._maskCanvasContext.drawImage(mask.canvas, 0, 0); // // Convert the canvas to a data URL // const dataURL = this._maskCanvas.toDataURL('image/png'); // // Create a link element and trigger the download // const downloadLink = document.createElement('a'); // downloadLink.href = dataURL; // downloadLink.download = 'segmentation_mask.png'; // document.body.appendChild(downloadLink); // downloadLink.click(); // } // if (this._maskCanvas.width !== mask.canvas.width || this._maskCanvas.height !== mask.canvas.height) { // this._maskCanvas.width = mask.canvas.width; // this._maskCanvas.height = mask.canvas.height; // } // this._maskCanvasContext!.drawImage(mask.canvas, 0, 0); // this.context.domElement.appendChild(this._maskCanvas); this._texture ??= new Texture(mask); this._texture.needsUpdate = true; this._material.uniforms.map.value = this._texture; // if (this._texture) { // const texProps = this.context.renderer.properties.get(this._texture); // console.log(texProps); // texProps.__webglTexture = mask.getAsWebGLTexture(); // this._texture.needsUpdate = true; // console.log(texProps.__webglTexture) // this._material.uniforms.map.value = this._texture; // } // const rt = new WebGLRenderTarget() // const texture = new Texture(); // const gl = this.context.renderer.getContext(); // console.log(texture); // this._maskCanvas ??= document.createElement("canvas"); // this._maskCanvas.width = mask.width; // this._maskCanvas.height = mask.height; // this._maskCanvasContext ??= this._maskCanvas.getContext("2d")!; // this._canvasTexture ??= new CanvasTexture(mask.canvas); // this._material.uniforms.map.value = this._canvasTexture; // this._canvasTexture.needsUpdate = true; // this._material.needsUpdate = true; // this._material.uniformsNeedUpdate = true; } } let aspect = this.owner.video.videoWidth / this.owner.video.videoHeight; if (!mirror) { aspect *= -1; } this._videoQuad.scale .set(aspect, -1, 1) .multiplyScalar(far * Math.tan(this.context.mainCamera.fov * Math.PI / 180 / 2) * 2); this._videoQuad.visible = this.owner.showVideo; } } }