JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/GLTFLoader.js"></script>

<div class="container-canvas"></div>
<script id="vs" type="f">
  #define TAU 6.28318530718

  varying vec3 worldNormal;
  varying vec3 viewDirection;
  
  void main() {
    vec4 worldPosition = modelMatrix * vec4( position, 1.0);
    worldNormal = normalize( modelViewMatrix * vec4(normal, 0.)).xyz;
    viewDirection = normalize(worldPosition.xyz - cameraPosition);

    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
</script>
<script id="fs" type="f">
  #define TAU 6.28318530718
  #define PI 3.14159265359
  #define S(a,b,n) smoothstep(a,b,n)
  
  uniform sampler2D envMap;
  uniform vec2 uReso;
  
  varying vec3 worldNormal;
  varying vec3 viewDirection;
  
  void main() {
      float ior = 1.5;
      // screen coordinates
      vec2 uv = gl_FragCoord.xy / uReso;

      // combine backface and frontface normal
      vec3 normal = worldNormal;

      // calculate refraction and apply to uv
      vec3 refracted = refract(viewDirection, normal, 1.0/ior);
      uv += refracted.xy;

      // sample environment texture
      vec4 tex = texture2D(envMap, uv);

      vec4 color = tex;

      gl_FragColor = vec4(color.rgb, 1.0);
  }
  
</script>

CSS

body{
  margin: 0;
}

canvas{
  width: 100%;
  height: 100vh;
  display: block;
}

JavaScript

class Helper{
  constructor(scene){
    this.scene = scene
  }
  
  axes(size){
    const _axesHelper = new THREE.AxesHelper( size );
    this.scene.add( _axesHelper );  
  }
  
  grid(size, divisions){
    const _gridHelper = new THREE.GridHelper( size, divisions );
    this.scene.add( _gridHelper );
  }
  
  direction(light, type, size = 1){
    let _helper = ''
    
    if(type === 'directional'){
      _helper = new THREE.DirectionalLightHelper( light, size );  
    }else if(type === 'hemisphere'){
       _helper = new THREE.HemisphereLightHelper( light, size );
    }else if(type === 'point'){
       _helper =  new THREE.PointLightHelper( light, size );    
    }else if(type === 'rectarea'){
       _helper = new THREE.RectAreaLightHelper( light );
    }else if(type === 'spot'){
       _helper = new THREE.SpotLightHelper( light );
    }
    
    this.scene.add( _helper ); 
  }
  
  camera(cam){
    const _helper = new THREE.CameraHelper(cam);
    this.scene.add(_helper);
  }
}

class RefractionObject {
  constructor(scene, renderer, orthoCamera, model, texture) {
    this.scene = scene;
    this.orthoCamera = orthoCamera;
    this.renderer = renderer;
    this.model = model;
    this.texture = texture;

    this.orthoCamera.position.z = 5;
    this.orthoCamera.layers.set(1);

    this.createBackground();
    this.createModel();
  }

  createBackground() {
    const PG = new THREE.PlaneBufferGeometry();
    const PM = new THREE.MeshBasicMaterial({
      map: this.texture
    });
    this.plane = new THREE.Mesh(PG, PM);
    this.plane.scale.set(window.innerWidth, window.innerHeight, 1);
    this.plane.layers.set(1);
    this.scene.add(this.plane);

    this.envFbo = new THREE.WebGLRenderTarget(
      window.innerWidth,
      window.innerHeight
    );

    this.renderer.autoClear = false;
  }

  createModel() {
    this.refractionMaterial = new THREE.ShaderMaterial({
      uniforms: {
        envMap: { value: this.envFbo.texture },
        uReso: { value: new...