block by harrystevens 0f0f2c9a82ac054693190ed832b45682

Stacks On Stacks On Stacks

Full Screen

Stacking coins in THREE.js. Click to stop and start the rotation.

index.html

<html>
<head>
  <style>
    body {
      margin: 0;
    }
  </style>
</head>
<body>
  <div id="scene"></div>
  <script src="https://unpkg.com/three@0.105.2/build/three.min.js"></script>
  <script src="https://unpkg.com/geometric@1.1.1/build/geometric.min.js"></script>
  <script>
    const scene = new THREE.Scene();

    const camera = (_ => {
      const camera = new THREE.PerspectiveCamera(35, innerWidth / innerHeight, 0.1, 1000);

      camera.position.set(0, 50, -100);
      camera.lookAt(0, 10, 0);

      return camera;
    })();

    const renderer = (_ => {
      const renderer = new THREE.WebGLRenderer();

      renderer.setClearColor("#eee");
      renderer.setPixelRatio(devicePixelRatio);
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      renderer.gammaOutput = true;
      renderer.gammaFactor = 2.2;

      document.getElementById("scene").appendChild(renderer.domElement);

      return renderer;
    })();

    const plane = (_ => {
      const geo = new THREE.PlaneBufferGeometry(2000, 2000);
      const mat = new THREE.MeshPhongMaterial({ color: "#888" });
      const plane = new THREE.Mesh(geo, mat);

      plane.position.set(0, 0, 0);
      plane.rotation.x = -.5 * Math.PI;
      scene.add(plane);
      plane.receiveShadow = true;

      return plane;
    })();

    for (let i = 0; i < 25; i++){
      drawCoin(i);   
    }

    for (let i = 0; i < 10; i++){
      drawCoin(i, innerWidth / 60, true);
    }

    for (let i = 0; i < 5; i++){
      drawCoin(i, innerWidth / -60, true);
    }

    const ambient = (_ => {
      const light = new THREE.AmbientLight(0x000000, 0.1);
      scene.add(light);
      return light;
    })();

    const spotBehind = (_ => {
      const light = new THREE.SpotLight("#fff", 0.3);

      light.position.set(-8, 50, 19);
      light.castShadow = true;
      light.shadow.mapSize.width = 1024;
      light.shadow.mapSize.height = 1024;

      scene.add(light);

      return light;
    })();

    const spotLeft = (_ => {
      const light = new THREE.SpotLight("#fff", 1);

      light.position.set(50, 30, -40);
      light.castShadow = true;
      light.shadow.mapSize.width = 1024;
      light.shadow.mapSize.height = 1024;

      scene.add(light);

      return light;
    })();

    const spotRight = (_ => {
      const light = new THREE.SpotLight("#fff", .8);

      light.position.set(-58, 5, -40);
      light.castShadow = true;
      light.shadow.mapSize.width = 1024;
      light.shadow.mapSize.height = 1024;

      scene.add(light);

      return light;
    })();
   
    const spotOverhead = (_ => {
      const light = new THREE.SpotLight("#fff", 1);

      light.position.set(0, 800, 0);
      light.castShadow = true;
      light.shadow.mapSize.width = 1024;
      light.shadow.mapSize.height = 1024;

      scene.add(light);

      return light;
    })();


    let clicked = false;
    document.addEventListener("click", _ => {
      clicked = !clicked;
    });

    function animate(){
      requestAnimationFrame(animate);

      if (!clicked){
        scene.traverse((e) => {
          if (e instanceof THREE.Mesh && e != plane) {
            const origin = [0, 0];
            [e.position.x, e.position.z] = geometric.pointRotate([e.position.x, e.position.z], 2, origin);
          }
        });
      }

      camera.aspect = innerWidth / innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(innerWidth, innerHeight);
      renderer.render(scene, camera);  
    }
    animate();

    function drawCoin(i, x = 0, receiveShadow = false){
      const geo = new THREE.CylinderBufferGeometry(5, 5, 1, 32);
      const mat = new THREE.MeshStandardMaterial({
        color: "#ffe685",
        metalness: 1,
        roughness: .4
      });
      const cylinder = new THREE.Mesh(geo, mat);

      cylinder.position.x = x + Math.random();
      cylinder.position.y = i;

      cylinder.castShadow = true;
      cylinder.receiveShadow = receiveShadow;
      scene.add(cylinder);    
    }
  </script>
</body>
</html>