three.js

Glitch Scroll Example by Three.js using React

Background

I've just started to learn Three.js. As you know this library is powerful to create 3d animation, motion graphics and effects. In this post I'm going to explain how I achieved to implement glitch image by scrolling using React. There is no meaning what I chose React. That is simply one of major framework and it's a bit helpful for some developers but I don't mention about React in this article.

What is Glitch?

Glitch is a word to depict collapsed or bug images. To express those of images there are various methodologies such as scratching, distorting, noise and jaggy.

I initially thought that RGB glitch was a cool effect and easy to implement because the glitched image is composed of only 3 images separated with R, B, G channels from original image. Indeed I found an example in learning-threejs repository including the shader file.

josdirksen/learning-threejs
Code repository for the examples from the Packt book “Learning Threejs” - josdirksen/learning-threejs
RGB shift image

Apply RGBShiftShader

Setup

First of all I created  THEE.Mesh objects to display images then added material with shader.

// NOTE: Setup material
const material = originalMaterial.clone();
material.uniforms.tDiffuse.value = cardTexture;

cardTexture is a THREE.Texture loaded from THREE.TextureLoader. This texture is assigned to tDiffuse property that is used in shader as image data. I placed these mesh objects along DIV layout in DOM tree into canvas. The layout is inspired by this sample and borrowed images in the repository.

Let's glitching

Thanks to this fantastic example all I need to do is change distance among RGB images and direction on scroll event. I customised this properties when scroll up event occurred, 90 degrees shifts with particular RGB amount value like this. Every time the content of the page is scrolled, this RGB glitch effect is applied.

let offset = 1.2
window.addEventListener('scroll', e => {
    const scrollTop = Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop)
    const currentScrollY = scrollTop
    const isDownScroll = oldScrollY >= currentScrollY
    let diff = Math.floor((oldScrollY - currentScrollY)) * offset
    let direction = 90
    
    if (isDownScroll) {
      diff *= -1
      direction *= -1
    }

    applyRgbShader(diff, direction)
})

const applyRgbShader = (value, direction = 90) => {
  planeGeometries.map(card => {
    const material = card.material;
    material.uniforms.amount.value = value / 300.0;
    material.uniforms.angle.value = direction * ( Math.PI / 180 );
  });
}

The planeGeometries in applyRgbShader function keeps exactly the targets of THREE.Mesh on canvas. Each item has a texture by an image as a material that I've initialised above a couple of code.

Scroll glitch effect

Glitch effect throughout a container

Not only each image to apply glitch effect,  I was also eager to did the effect on over the page. This goal is a bit tricky. I had to render the THREE.WebGLRenderer via EffectComposer. The role of EffectComposer is to combine multiple effects and apply them in throughout the webGLRenderer.domElement applied by the composit effects. This approach is called postprocessing. This postprocessing uses shader internally to apply throughout webGLRenderer. Just add shader pass to the EffectComposer.

let glitchPass = new GlitchPass();
let renderPass = new RenderPass(scene, camera);

glitchPass.enabled = false;
glitchPass.goWild = true;

const composer = new EffectComposer(webGLRenderer);
composer.addPass(renderPass);
composer.addPass(glitchPass);
composer.addPass(effectCopy);

When the glitchPass turned to enabled, the glitch effect is fired with random RGB effect parameter. I switch on the effect when resize event is being called.

window.addEventListener('resize', throttleFunc, false);

const throttleFunc = (function() {
  // Deley resize event by interval
  const interval = 350;
  let timer;

  return function() {
    glitchPass.enabled = true;
    clearTimeout(timer);

    const refreshScene = () => {
      const canvas = webGLRenderer.domElement;
      resizeCanvas(canvas)

      planeGeometries.map(card => scene.remove(card));
      planeGeometries = [];
      setupImages()

      const camera = createSceneCamera(canvas)
      renderPass.camera = camera;
      renderPass.scene = scene
    }

    refreshScene()

    timer = setTimeout(function() {
      refreshScene()
      glitchPass.enabled = false;
    }, interval);
  };
})();
Resize glitch effect

Conclusion

Applying glitch effect to a specific object is not difficult to apply. But if you want to apply it throughout page or container, you may have to dive into more complicate three.js world. You can see the final image in this post to visit my portfolio site.

Mitsuya Watanabe Archives
Web site created using create-react-app

I put this glitch scroll sample code in Github. I hope this post is helpful for some developers.

mitsuyacider/GlitchScroll
Whenever scrolling, images on canvas get effect by glitch. Using Three.js and shader. - mitsuyacider/GlitchScroll