Comparsion slider between FaceMeshTexture filter and no filter

Hi everyone
Recently I started a project where I’m basically creating makeup try-ons using FaceMeshTexture filters, but I wanted to implement a comparison slider where I could see the difference between the filter being applied and not applied at the same time.
I’ve tried adding an intensity field on the shaderMaterial of the FaceMeshTexture but it ended up breaking the texture so I just reverted back to the original shaderMaterial.

Does anyone know how I could implement this slider?
Any help is appreciated.
Thanks!

Hi Tomás,

A common approach for a comparison slider – showing one half of your face with the FaceMeshTexture filter and the other half without – is to duplicate your face mesh so that you have two versions: one with the filter applied and the original one. Then, you can use clipping planes (or masks) to “cut” each instance along a vertical line that’s controlled by a slider value.

For example, you can have two components:
• One that applies a clipping plane to reveal only the filtered side.
• One that applies the inverse clipping plane to show only the original (unfiltered) side.
When you move the slider (an HTML range input), you update both clipping planes so their boundaries adjust seamlessly.

Here’s a simplified Needle Engine component (written in TypeScript) that demonstrates the idea:


import { Behaviour, serializable } from "@needle-tools/engine";
import { Plane, Vector3 } from "three";

export class FaceMeshComparison extends Behaviour {

  // Assign your two face mesh objects in the editor or via script
  @serializable(Object3D)
  faceMeshWithFilter: any = null;
  
  @serializable(Object3D)
  faceMeshOriginal: any = null;

  private filterPlane!: Plane;
  private originalPlane!: Plane;

  awake() {
    // Initialize the clipping planes along the vertical axis.
    // The normals are set so the plane clips away one side.
    this.filterPlane = new Plane(new Vector3(-1, 0, 0), 0);
    this.originalPlane = new Plane(new Vector3(1, 0, 0), 0);

    // Apply local clipping to the filtered face mesh
    if (this.faceMeshWithFilter) {
      this.faceMeshWithFilter.traverse((child: any) => {
        if (child.material) {
          child.material.clippingPlanes = [this.filterPlane];
          child.material.clipShadows = true;
          child.material.needsUpdate = true;
        }
      });
    }

    // Apply local clipping to the original face mesh
    if (this.faceMeshOriginal) {
      this.faceMeshOriginal.traverse((child: any) => {
        if (child.material) {
          child.material.clippingPlanes = [this.originalPlane];
          child.material.clipShadows = true;
          child.material.needsUpdate = true;
        }
      });
    }

    // Listen to slider input from your HTML element (make sure its id is "comparisonSlider")
    const slider = document.getElementById("comparisonSlider") as HTMLInputElement;
    if (slider) {
      slider.addEventListener("input", () => {
        const value = parseFloat(slider.value); // Assuming a value between 0 and 1
        this.updateClipping(value);
      });
    }
  }

  updateClipping(value: number) {
    // Adjust the clipping constant based on the slider value.
    // The relationship here is simplistic – you might need to adjust it
    // based on how your scene and camera are set up.
    this.filterPlane.constant = -value;
    this.originalPlane.constant = value;
  }
}

In this solution the slider’s value directly remaps where the clipping happens, letting you “slide” between versions. This avoids messing with shader properties on the FaceMeshTexture filter (which you noted was breaking the texture) by leveraging scene setup instead.

You can review similar techniques in other Needle Engine sample components, such as the face filter samples at facefilter—FaceSticker.ts.md. This strategy should give you smooth control over what portion of your face shows the filter versus the unfiltered version.

I hope this helps, and feel free to ask for more details if needed!

1 replies left - Become PRO for more replies.
thread_jeAvQuYPTAZ9ibd77FMoWa8J
Reply to this post directly if you need further help (bottom right button)

Hi, how did it break the shader and could you share the code you got so far?

Are you using the code from the repository https://github.com/needle-engine/facefilter or directly via npm?

const faceMeshTextureFrag = `

precision highp float;
uniform sampler2D map;
uniform sampler2D mask;
uniform float intensity;
varying vec2 vUv;
void main() {
    vec4 texColor = texture2D(map, vUv);
    texColor.a *= intensity;
    gl_FragColor = texColor;
#ifdef HAS_MASK
    vec4 maskColor = texture2D(mask, vUv);
    gl_FragColor.a *= maskColor.r;
#endif
}
`


export class FaceMeshTexture extends FaceMeshBehaviour {

    @serializable(Texture)
    texture: Texture | null = null;

    @serializable(Texture)
    mask: Texture | null = null;

    @serializable()
    intensity: number = 1.0;

    // @nonSerialized
    @serializable()
    set layout(value: FaceLayout) { this.__layout = value; }
    get layout(): FaceLayout { return this.__layout; }
    private __layout: FaceLayout = "mediapipe";

    private _material: ShaderMaterial | null = null;

    protected createMaterial() {
        return this._material = new ShaderMaterial({
            uniforms: {
                map: { value: this.texture },
                mask: { value: this.mask },
                intensity: { value: this.intensity },
            },
            defines: {
                HAS_MASK: this.mask ? true : false,
            },
            wireframe: false,
            transparent: true,
            fragmentShader: faceMeshTextureFrag,
            vertexShader: `
                varying vec2 vUv;
                void main() {
                    vUv = uv;
                    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                }
            `
        });
    }
}

Hi,
I used the original code from the FaceMeshBehaviour class under the Sample Face Filter Scene and displayed the wrong colors at the wrong spots along the texture.
I added these lines to the faceMeshTexture fragment:

uniform float intensity;

texColor.a *= intensity;

And this line to the createMaterial() function:

intensity: { value: this.intensity },

Everything else is the original code that was present already when I first created the project.

Hi, I’ve tried to recreate the issue on stackblitz but for me it seems to work fine using your code.

See here: Needle Engine FaceFilter Custom FaceMesh Shader - StackBlitz

I’m extending the FaceTexture class and modify the created material with the intensity and fragment shader.

It sounds like you’re using the FaceFilter example from samples - we have since then moved the code into it’s own npm package. The samples Unity package needs to be updated to reflect that. I’ll create an issue to prioritize this.

Hi
Thank you so much for your help, this worked!
Maybe it was broken because I was modifying the sample file?
Anyway, extending base class fixed my issue.

Thanks again!

1 Like

This topic was automatically closed 13 days after the last reply. New replies are no longer allowed.