I have been trying to get this pixel distortion to work for each image on my site

This pixel distortion from codrops link: Pixel Distortion Effect with Three.js | Codrops

I have being trying to get it to work for each image on my site, but it only works for the last image. And even at that it is not as smooth as the ones from zajno.com because that is exactly what i want to achieve. Anyone knows how to remotely get this as close? I have tried PP but its still same result.

Hard to say without seeing the code you’ve implemented. This example should be quite helpful though (code in the bottom right.)

1 Like

I simply want to implement the pixel distortion effect on multiple images the way it is on zajno.com. I used the pixelation effect from the codrops example I specified, but the effect ends up on working partially on ONLY the last image.

MY CODE:
Simply uncomment the this.generateGrid accordingly.

import * as THREE from "three";
import gsap from "gsap";

import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);

import vertex from "./shaders/vertexShader.glsl";
import fragment from "./shaders/fragmentShader.glsl";

const canvas = document.querySelector(".effect__canvas");

function clamp(number, min, max) {
  return Math.max(min, Math.min(number, max));
}

class Effect {
  constructor() {
    this.scroll = 0;
    this.threejsCanvas = document.querySelector(".effect__canvas");
    this.width = this.threejsCanvas.offsetWidth;
    this.height = this.threejsCanvas.offsetHeight;

    // Test Effect
    this.mouse = {
      x: 0,
      y: 0,
      prevX: 0,
      prevY: 0,
      vX: 0,
      vY: 0,
    };

    this.settings = {
      grid: 50,
      mouse: 0.05,
      strength: 0.11,
      relaxation: 0.9,
    };

    let st = ScrollTrigger.create({
      trigger: ".container",
      start: "top top",
      end: "bottom bottom",
    });

    document.addEventListener("scroll", () => {
      this.scroll = st.scroll();
    });

    this.setup();
    this.addTextureImages();
    this.onResize();
    this.addEventListeners();
    // this.onMouseMove();

    this.animate();
  }

  addTextureImages() {
    // this.regenerateGrid();

    this.images = [
      ...document.querySelectorAll(".image__wrapper .work__detail__image"),
    ];

    const geometry = new THREE.PlaneGeometry(1, 1);
    const textureLoader = new THREE.TextureLoader();

    this.imageData = this.images.map((image) => {
      const bounds = image.getBoundingClientRect();

      const imageTexture = textureLoader.load(image.getAttribute("src"));

      const material = new THREE.ShaderMaterial({
        uniforms: {
          uHoverState: { value: 0 },
          uTexture: { value: imageTexture },
        },
        vertexShader: vertex,
        fragmentShader: fragment,
      });

      const imageMesh = new THREE.Mesh(geometry, material);

      imageMesh.scale.set(bounds.width, bounds.height);

      this.scene.add(imageMesh);

      image.addEventListener("mouseenter", () => {
        gsap.to(material.uniforms.uHoverState, {
          duration: 1,
          value: 1,
          ease: "power3.out",
        });
      });

      image.addEventListener("mouseout", () => {
        gsap.to(material.uniforms.uHoverState, {
          duration: 1,
          value: 0,
          ease: "power3.out",
        });
      });

      return {
        image,
        imageMesh,
        width: bounds.width,
        height: bounds.height,
        top: bounds.top,
        left: bounds.left,
      };
    });
  }

  setup() {
    const cameraFOV = 2 * Math.atan(this.height / 2 / 10) * (180 / Math.PI);
    const cameraASPECT_RATIO = this.width / this.height;
    const cameraNEAR = 0.01;
    const cameraFAR = 1000;

    //  Camera
    this.camera = new THREE.PerspectiveCamera(
      cameraFOV,
      cameraASPECT_RATIO,
      cameraNEAR,
      cameraFAR
    );
    this.camera.position.set(0, 0, 10);

    // Scene
    this.scene = new THREE.Scene();

    // Renderer
    this.renderer = new THREE.WebGLRenderer({
      canvas: this.threejsCanvas,
      antialias: true,
      alpha: true,
    });

    this.renderer.setSize(this.width, this.height);
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  }

  updatePosition() {
    this.imageData.forEach((image) => {
      image.imageMesh.position.x =
        image.left - this.width / 2 + image.width / 2;
      image.imageMesh.position.y =
        this.scroll - image.top + this.height / 2 - image.height / 2;
    });
  }

  addEventListeners() {
    window.addEventListener("resize", this.onResize.bind(this));
  }

  regenerateGrid() {
    this.size = this.settings.grid;

    const width = this.size;
    const height = this.size;

    const size = width * height;
    const data = new Float32Array(4 * size);
    const color = new THREE.Color(0xffffff);

    const r = Math.floor(color.r * 255);
    const g = Math.floor(color.g * 255);
    const b = Math.floor(color.b * 255);

    for (let i = 0; i < size; i++) {
      let r = Math.random() * 255 - 125;
      let r1 = Math.random() * 255 - 125;

      const stride = 1;

      data[stride] = r;
      data[stride + 1] = r1;
      data[stride + 2] = r;
      data[stride + 3] = 1;
    }

    // used the buffer to create a DataTexture
    this.dataTexture = new THREE.DataTexture(
      data,
      width,
      height,
      THREE.RGBAFormat,
      THREE.FloatType
    );

    this.dataTexture.magFilter = this.dataTexture.minFilter =
      THREE.NearestFilter;

    if (this.material) {
      this.material.uniforms.uDataTexture.value = this.dataTexture;
      this.material.uniforms.uDataTexture.value.needsUpdate = true;
    }
  }

  updateDataTexture() {
    let data = this.dataTexture.image.data;

    let gridMouseX = this.size * this.mouse.x;
    let gridMouseY = this.size * (1 - this.mouse.y);

    let maxDist = this.size * this.settings.mouse;
    let aspect = this.height / this.width;

    for (let i = 0; i < this.size; i++) {
      for (let j = 0; j < this.size; j++) {
        let distance = (gridMouseX - i) ** 2 / aspect + (gridMouseY - j) ** 2;
        let maxDistSq = maxDist ** 2;

        if (distance < maxDistSq) {
          let index = 4 * (i + this.size * j);
          let power = maxDist / Math.sqrt(distance);

          power = clamp(power, 0, 10);

          data[index] += this.settings.strength * 100 * this.mouse.vX * power;
          data[index + 1] -=
            this.settings.strength * 100 * this.mouse.vY * power;
        }
      }
    }

    for (let i = 0; i < data.length; i += 4) {
      data[i] *= this.settings.relaxation;
      data[i + 1] *= this.settings.relaxation;
    }

    this.mouse.vX *= 0.9;
    this.mouse.vY *= 0.9;
    this.dataTexture.needsUpdate = true;
  }

  onResize() {
    this.width = this.threejsCanvas.offsetWidth;
    this.height = this.threejsCanvas.offsetHeight;

    this.renderer.setSize(this.width, this.height);
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

    this.camera.aspect = this.width / this.height;
    this.camera.updateProjectionMatrix();

    if (this.imageData) {
      for (let i = 0; i < this.imageData.length; i++) {
        this.imageData[i].top = this.images[i].getBoundingClientRect().top;
        this.imageData[i].height =
          this.images[i].getBoundingClientRect().height;
        this.imageData[i].left = this.images[i].getBoundingClientRect().left;
        this.imageData[i].width = this.images[i].getBoundingClientRect().width;
        this.imageData[i].imageMesh.scale.set(
          this.images[i].getBoundingClientRect().width,
          this.images[i].getBoundingClientRect().height
        );
      }
    }

    // this.regenerateGrid();
  }

  animate() {
    // this.updateDataTexture();
    this.updatePosition();
    this.renderer.render(this.scene, this.camera);
    requestAnimationFrame(this.animate.bind(this));
  }
}

new Effect();

// image cover
// let imageAspect = imageHeight / imageWidth;

// let a1;
// let a2;

// if (bounds.height / bounds.width > imageAspect) {
//   a1 = (bounds.width / bounds.height) * imageAspect;
//   a2 = 1;
// } else {
//   a1 = 1;
//   a2 = bounds.height / bounds.width / imageAspect;
// }

// this.material.uniforms.resolution.value.x = bounds.width;
// this.material.uniforms.resolution.value.y = bounds.height;
// this.material.uniforms.resolution.value.z = a1;
// this.material.uniforms.resolution.value.w = a2;