javascripthtmlanimationthree.jsp5.js

(Intergrate P5.js and Three.js) --- Create a ThreeJS scene with animations from P5.js library?


Before we begin, you may want to read my previous post which lead to the creation of this question:

enter image description here Drawing/Rendering 3D objects with epicycles and fourier transformations [Animation]

Context:

Using the P5.js library and following a tutorial from The Coding Train (Coding Challenge #130.1 --> #130.3) i was able to animate and recreate any parametric drawing using epicycles and fourier transforms. (Read the Previous Post, trust me, it will help)

I am now looking to expand this to three Dimensions!

A helpful community member suggested breaking the 3D drawing into two planes. This way, i dont have to write new code, and could use my preexisting 2D code! cool right!

Another User suggested using the Three.JS library to create a 3D scene for this process.

So Far i have created 3 planes. I would like to essentially use these planes as TV Screens. TV screens where i can then display my 2D version from written in P5js and project a new point in 3D space to generate/draw a new 3D drawing.

enter image description here

<html>
  <head>
    <title>Epicyclic Circles</title>
    <style>
      body { margin: 0; }
      canvas { width: 100%; height: 100% }
    </style>
  </head>
  <body>

    <script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>

    <script>

      // Set up the basic scene, camera, and lights.

      var scene = new THREE.Scene();
      scene.background = new THREE.Color( 0xf0f0f0 );

      var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
      scene.add(camera)

      var light = new THREE.PointLight( 0xffffff, 0.8 );
      camera.add( light );

      camera.position.z = 50;

      var renderer = new THREE.WebGLRenderer();
      renderer.setSize( window.innerWidth, window.innerHeight );
      document.body.appendChild( renderer.domElement );

      // Add the orbit controls to permit viewing the scene from different angles via the mouse.

      controls = new THREE.OrbitControls( camera, renderer.domElement );
      controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
      controls.dampingFactor = 0.25;
      controls.screenSpacePanning = false;
      controls.minDistance = 0;
      controls.maxDistance = 500;

      // Create center and epicyclic circles, extruding them to give them some depth.

      var plane = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
var helper = new THREE.PlaneHelper( plane, 50, 0x696969 );
scene.add( helper );


var plane2 = new THREE.Plane( new THREE.Vector3( 0, 1, 0 ), 0 );
var helper2 = new THREE.PlaneHelper( plane2, 50, 0xE06666 );
scene.add( helper2 );


var plane3 = new THREE.Plane( new THREE.Vector3( 0, 0, 1 ), 0 );
var helper3 = new THREE.PlaneHelper( plane3, 50, 0xD85C6 );
scene.add( helper3 );

var size = 10;
var divisions = 10;

var gridHelper = new THREE.GridHelper( size, divisions );
scene.add( gridHelper );






      var animate = function () {
        requestAnimationFrame( animate );

        // During each animation frame, let's rotate the objects on their center axis,  
        // and also set the position of the epicyclic circle.

        renderer.render( scene, camera );
      };

      animate();
    </script>
  </body>
</html>

Any other suggestions/Methods are welcomed too! :D

Recap:


Solution

  • Dabbled a bit more with what I think is your basic concept. Believe it or not, more than 50% of the effort involved working around dithering issues associated with overlapping transparent objects in motion, an area where three.js is a bit weak. But, after a bit of searching, one can mitigate the dithering issues with adjustments to the Z alignment of the objects and the renderOrder.

    In any event, take a look at the code below, which extends your effort, introducing 5 randomly sized and rotating transparent circles. For drawing lines, take a look at the following link https://threejs.org/docs/#manual/en/introduction/How-to-update-things .

    This might look better full screen rather than in the small viewport within Stackoverflow.

    Hope this helps you along your way.

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Epicyclic Circles</title>
        <style>
          html, body { height: 100%; margin: 0; }
          canvas { display: block; width: 100%; height: 100%; }
        </style>
      </head>
      <body>
        <!-- Pin Three.js + OrbitControls to a specific version -->
        <script src="https://unpkg.com/three@0.126.1/build/three.min.js"></script>
        <script src="https://unpkg.com/three@0.126.1/examples/js/controls/OrbitControls.js"></script>
    
        <script>
          // Scene, camera, renderer
          const scene = new THREE.Scene();
          scene.background = new THREE.Color(0xf0f0f0);
    
          const camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
          );
          scene.add(camera);
          camera.position.z = 200;
    
          const renderer = new THREE.WebGLRenderer({ antialias: true });
          renderer.setPixelRatio(window.devicePixelRatio);
          renderer.setSize(window.innerWidth, window.innerHeight);
          document.body.appendChild(renderer.domElement);
    
          // Lights (MeshStandardMaterial needs real lighting)
          const light = new THREE.PointLight(0xffffff, 0.9);
          camera.add(light);
          const ambient = new THREE.AmbientLight(0xffffff, 0.3);
          scene.add(ambient);
    
          // Controls
          const controls = new THREE.OrbitControls(camera, renderer.domElement);
          controls.enableDamping = true;
          controls.dampingFactor = 0.25;
          controls.screenSpacePanning = false;
          controls.minDistance = 0;
          controls.maxDistance = 500;
    
          // Helper to create an extruded ring with a small hole
          function createCircleMesh(plane, radius, depth, meshColor, meshOpacity) {
            const extrudeSettings = {
              depth: depth,
              bevelEnabled: true,
              bevelSegments: 2,
              steps: 2,
              bevelSize: 0.25,
              bevelThickness: 0.25
            };
    
            const arcShape = new THREE.Shape();
            arcShape.moveTo(0, 0);
            arcShape.absarc(0, 0, radius, 0, Math.PI * 2, false);
    
            const holePath = new THREE.Path();
            const holeR = radius * 0.20;
            const holeY = radius * 0.75;
            holePath.moveTo(0, holeY);
            holePath.absarc(0, holeY, holeR, 0, Math.PI * 2, true);
            arcShape.holes.push(holePath);
    
            // Use ExtrudeGeometry (Buffer variant is merged)
            const geometry = new THREE.ExtrudeGeometry(arcShape, extrudeSettings);
            const material = new THREE.MeshStandardMaterial({
              color: meshColor,
              roughness: 1,
              metalness: 0,
              transparent: meshOpacity !== 1,
              opacity: meshOpacity
            });
            const mesh = new THREE.Mesh(geometry, material);
            scene.add(mesh);
    
            if (plane === "XZ") {
              mesh.rotateX(Math.PI / 2);
            } else if (plane === "YZ") {
              mesh.rotateY(Math.PI / 2);
            }
            return mesh;
          }
    
          // Circles
          const circle = [];
          for (let i = 0; i < 5; i++) {
            const r = 5 - i;
            circle[i] = {
              radius: r * 10 + Math.random() * r * 10,
              rotationRate: (Math.random() * 2 * Math.PI - Math.PI) / 100
            };
    
            // RenderOrder is stepped to reduce dithering.
            circle[i].mesh = createCircleMesh("XY", circle[i].radius, 5, 0xff0000, 0.5);
            circle[i].mesh.renderOrder = i;
    
            circle[i].centerMesh = createCircleMesh("XY", 5, 2, 0xff0000, 1);
            if (i === 0) {
              circle[i].centerX = circle[i].radius;
              circle[i].centerY = circle[i].radius;
              circle[i].centerZ = i; // z-step to reduce z-fighting
            } else {
              circle[i].centerX = circle[i - 1].centerX + circle[i - 1].radius;
              circle[i].centerY = circle[i - 1].centerY;
              circle[i].centerZ = i; // z-step to reduce z-fighting
            }
            circle[i].rotated = 0;
          }
    
          // Viewing planes / helpers
          const plane = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0);
          const helper = new THREE.PlaneHelper(plane, 500, 0x696969);
          scene.add(helper);
    
          const plane2 = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
          const helper2 = new THREE.PlaneHelper(plane2, 500, 0xE06666);
          scene.add(helper2);
    
          const plane3 = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
          const helper3 = new THREE.PlaneHelper(plane3, 500, 0x0D85C6); // fixed: 6-hex-digit color
          scene.add(helper3);
    
          const gridHelper = new THREE.GridHelper(250, 10);
          scene.add(gridHelper);
    
          // Resize handler
          window.addEventListener("resize", () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
          });
    
          // Animate
          function animate() {
            requestAnimationFrame(animate);
    
            for (let i = 0; i < 5; i++) {
              const c = circle[i];
              c.rotated += c.rotationRate;
    
              if (i > 0) {
                c.centerX =
                  circle[i - 1].centerX + circle[i - 1].radius * Math.cos(circle[i - 1].rotated);
                c.centerY =
                  circle[i - 1].centerY + circle[i - 1].radius * Math.sin(circle[i - 1].rotated);
              }
    
              // Flip z step if the camera is behind the scene
              const zStep = c.centerZ * Math.sign(camera.position.z);
              c.mesh.rotateZ(c.rotationRate);
              c.mesh.position.set(c.centerX, c.centerY, zStep);
              c.centerMesh.position.set(c.centerX, c.centerY, zStep);
            }
    
            controls.update(); // required when enableDamping = true
            renderer.render(scene, camera);
          }
    
          animate();
        </script>
      </body>
    </html>