body{
  overflow: hidden;
  margin: 0;
}
console.clear();
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";
import {TWEEN} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/libs/tween.module.min.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10).multiplyScalar(2);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);

let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(20);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));

let forceFieldColor = 0x00eeff;

let forceFieldObject = new THREE.Mesh(new THREE.SphereBufferGeometry(1, 36, 18), new THREE.MeshBasicMaterial({color: forceFieldColor, transparent: true, opacity: 0.125, wireframe: true}));
forceFieldObject.scale.setScalar(0.0001);
scene.add(forceFieldObject)

let uniforms = {
  forceField: {value: new THREE.Vector4(0, 0, 0, 0.0001)},
  forceFieldColor: {value: new THREE.Color(forceFieldColor)}
}

const count = 100;
for (let i = 0; i < count; i++){
  let box = new THREE.Mesh(new THREE.BoxBufferGeometry(1, 1, 1), getMaterial(Math.random() * 0x7f7f7f + 0x7f7f7f));
  box.position.random().subScalar(0.5).multiplyScalar(20);
  box.rotation.set(
    Math.random() * Math.PI,
    Math.random() * Math.PI,
    Math.random() * Math.PI
  );
  box.scale.random().multiplyScalar(3);
  scene.add(box);
}

runTweening();

renderer.setAnimationLoop(()=>{
  TWEEN.update();
  renderer.render(scene, camera);
});

function runTweening(){
  let radius = 5 + Math.random() * 5;
  let position = new THREE.Vector3().random().subScalar(0.5).multiplyScalar(20);
  forceFieldObject.position.copy(position);
  uniforms.forceField.value.copy(position);
  let t = new TWEEN.Tween({val: 0.0001}).to({val: 1}, 5000).onUpdate(
    value => {
      //console.log(value);
      forceFieldObject.scale.setScalar(value.val * radius);
      uniforms.forceField.value.w = value.val * radius;
    }
  ).onComplete(
    ()=>{
      runTweening();
    }
  ).start();
}

function getMaterial(color){
  return new THREE.MeshStandardMaterial({
    color: color,
    roughness: 0.25,
    metalness: 0.5,
    onBeforeCompile: shader => {
      shader.uniforms.forceField = uniforms.forceField;
      shader.uniforms.forceFieldColor = uniforms.forceFieldColor;
      shader.vertexShader = `
        varying vec4 vWorldPos;
        ${shader.vertexShader}
      `.replace(
        `#include <worldpos_vertex>`,
        `#include <worldpos_vertex>
          vWorldPos = vec4( transformed, 1.0 );
          #ifdef USE_INSTANCING
            vWorldPos = instanceMatrix * vWorldPos;
          #endif
          vWorldPos = modelMatrix * vWorldPos;
        `
      );
      //console.log(shader.vertexShader);
      
      shader.fragmentShader = `
        uniform vec4 forceField;
        uniform vec3 forceFieldColor;
        varying vec4 vWorldPos;
        ${shader.fragmentShader}
      `.replace(
        `#include <dithering_fragment>`,
        `#include <dithering_fragment>
        
          float worldDist = distance(forceField.xyz, vWorldPos.xyz);
          
          float fadeoutWidth = 0.25;
          vec3 posf = fwidth(vWorldPos.xyz);
          float f = length(posf);
          
          float fadeout = smoothstep(forceField.w - fadeoutWidth, forceField.w, worldDist);
          fadeout -= smoothstep(forceField.w, forceField.w + f, worldDist);
          
          gl_FragColor.rgb = mix(gl_FragColor.rgb, forceFieldColor, fadeout);
        
        `
      );
      //console.log(shader.fragmentShader)
    }
  });
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.