WEEK 10 : Interface and Application Programming/p>

Soo this week I took a complete detour for my final project which was a bit refreshing and fun to be honest! and hopefully I can get this link running before class starts because its not really working at the moment, but if not see below what my endeavour was for this week. So I was interested in creating some sort of 3d graphic based on Neils lecture last week, and decided to give Three.js a try.

.

During the break while I was doom scrolling and ignoring my family I came across this video in instagram and I was determined to somehow replicate it. Given that my coding skills pertain only the knowledge acquired this semester so mega beginner this would be quite the feat to accomplish. Soo I watched a lot of tutorials like this one or this one aaand this one and to be honest like 5 more. I then ended up purchasing a subscription in google cloud to get an ip and run my web server I also created a github account to create my own repo that felt like a big coder step and finally I managed to somehow clone threejs repo which might be easy for all of you but it was madness for me. So I started pretty simple with some primitive forms and tried to make them into particles.

.

I then managed to link an OBJ onto the scene and loaded my final project the running table, I also started tweaking a bit the code in order to have more control over the particles. This was done with the help of my good friend chat gpt. I was trying to make it so the particles would jitter and disperce and then eventually they would go back into place showing the object

Here is the initial code with some jitter and a linked obj


                  import * as THREE from '../build/three.module.js';
import { OBJLoader } from '../build/jsm/loaders/OBJLoader.js';
import { OrbitControls } from '../build/jsm/controls/OrbitControls.js';

let SCREEN_WIDTH = window.innerWidth;
let SCREEN_HEIGHT = window.innerHeight;
let aspect = SCREEN_WIDTH / SCREEN_HEIGHT;

let container, scene, camera, renderer;
let particleSystem;

init();
animate();

function init() {
    container = document.createElement('div');
    document.body.appendChild(container);

    scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    container.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
    camera.position.set(0, 5, 15); // Adjusted for better visibility

    new OrbitControls(camera, renderer.domElement);

    const objLoader = new OBJLoader();
    objLoader.load('../models/mf.obj', function (object) {
        const particles = new THREE.BufferGeometry();
        let vertices = [];

        object.traverse(function (child) {
            if (child.isMesh) {
                const mesh = child;
                const posArray = mesh.geometry.attributes.position.array;
                for (let i = 0; i < posArray.length; i += 3) {
                    vertices.push(posArray[i], posArray[i + 1], posArray[i + 2]);
                }
            }
        });

        particles.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));

        const particleMaterial = new THREE.PointsMaterial({
            color: 0xFFFFFF,
            size: 0.1 // Adjust as needed
        });

        particleSystem = new THREE.Points(particles, particleMaterial);
        scene.add(particleSystem);
    });

    window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
    SCREEN_WIDTH = window.innerWidth;
    SCREEN_HEIGHT = window.innerHeight;
    aspect = SCREEN_WIDTH / SCREEN_HEIGHT;

    camera.aspect = aspect;
    camera.updateProjectionMatrix();

    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
}

function animate() {
    requestAnimationFrame(animate);
    render();
}

function render() {
    if (particleSystem) {
        particleSystem.rotation.y += 0.005;
    }

    renderer.render(scene, camera);
}
                        

            

Then I decided to have some fun and forget my final project for a bit, so I went back in time and decided to scan myself and try to use my geometry as the obj for the particles test I used polycam's free trial to scan myself the scan was not a hundred percent accurate but it was good enough for the task at hand.

.

Then things started to get crazy and fun, I started playing with the colors of the particles mapped onto the obj geometry and with the rate of the particles jittering in order to create different effects

Finally this part took me SO LONG! I kept getting so many errors and since my coding skills are limited there is only so far videos and chat gpt could take me. But my intention was to map the particles onto the obj mesh and make them move and rotate at the same time after countless tries and hours I finally managed to do it, how I did it was basically creating a duplicate of the mesh and offsetting it then converting that mesh onto the particles and then mapping the movements of both the mesh and the particles togeather

Here is the full code to do all of the above, my documentation this week is not the best but that is because I underminded how long making this would take me :p! More to come!!


                          import * as THREE from '../build/three.module.js';
                          import { OBJLoader } from '../build/jsm/loaders/OBJLoader.js';
                          import { OrbitControls } from '../build/jsm/controls/OrbitControls.js';
                          
                          let SCREEN_WIDTH = window.innerWidth;
                          let SCREEN_HEIGHT = window.innerHeight;
                          let aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
                          
                          let container, scene, camera, renderer, particleSystem, particlePositions, originalPositions, mesh;
                          let jitter = true;
                          let elapsedTime = 0;
                          let isMeshVisible = true;
                          
                          init();
                          animate();
                          
                          function init() {
                              container = document.createElement('div');
                              document.body.appendChild(container);
                          
                              scene = new THREE.Scene();
                          
                              renderer = new THREE.WebGLRenderer({ antialias: true });
                              renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
                              container.appendChild(renderer.domElement);
                          
                              camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
                              camera.position.set(0, 5, 15);
                          
                              new OrbitControls(camera, renderer.domElement);
                          
                              // Add particles to the scene
                              const objLoader = new OBJLoader();
                              objLoader.load('../models/mateo face.obj', function (object) {
                                  const particles = new THREE.BufferGeometry();
                                  let vertices = [];
                          
                                  object.traverse(function (child) {
                                      if (child.isMesh) {
                                          const mesh = child;
                                          const posArray = mesh.geometry.attributes.position.array;
                                          for (let i = 0; i < posArray.length; i += 3) {
                                              vertices.push(posArray[i], posArray[i + 1], posArray[i + 2]);
                                          }
                                      }
                                  });
                          
                                  particles.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
                                  particlePositions = particles.attributes.position.array;
                                  originalPositions = Array.from(particlePositions);
                          
                                  const particleMaterial = new THREE.PointsMaterial({
                                      color: 0x00ff00,
                                      size: 0.05 // Adjust the size of the particles
                                  });
                          
                                  particleSystem = new THREE.Points(particles, particleMaterial);
                                  scene.add(particleSystem);
                              });
                          
                              // Add the mesh with the shader material to the scene
                              const chromaticAberrationShader = {
                                  uniforms: {
                                      viewVector: { type: "v3", value: camera.position }
                                  },
                                  vertexShader: `
                                      varying vec3 vNormal;
                                      varying vec3 vViewPosition;
                          
                                      void main() {
                                          vNormal = normalize(normalMatrix * normal);
                                          vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
                                          vViewPosition = -mvPosition.xyz;
                                          gl_Position = projectionMatrix * mvPosition;
                                      }
                                  `,
                                  fragmentShader: `
                                      varying vec3 vNormal;
                                      varying vec3 vViewPosition;
                                      void main() {
                                          float fresnel = dot(normalize(vViewPosition), vNormal);
                                          fresnel = clamp(1.0 - fresnel, 0.0, 1.0);
                                          vec3 color = vec3(0.94, 0.54, 0.0) + vec3(0.0, 0.5, 0.7) * fresnel;
                                          gl_FragColor = vec4(color, 1.0);
                                      }
                                  `
                              };
                          
                              objLoader.load('../models/mateo face.obj', function (object) {
                                  const shaderMaterial = new THREE.ShaderMaterial(chromaticAberrationShader);
                                  object.traverse(function (child) {
                                      if (child.isMesh) {
                                          child.material = shaderMaterial;
                                      }
                                  });
                                  scene.add(object);
                                  mesh = object;
                                  mesh.visible = isMeshVisible; // Set initial visibility
                              });
                          
                              window.addEventListener('resize', onWindowResize, false);
                          }
                          
                          function onWindowResize() {
                              SCREEN_WIDTH = window.innerWidth;
                              SCREEN_HEIGHT = window.innerHeight;
                              aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
                          
                              camera.aspect = aspect;
                              camera.updateProjectionMatrix();
                          
                              renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
                          }
                          
                          function animate() {
                              requestAnimationFrame(animate);
                              render();
                          }
                          
                          function render() {
                              if (particleSystem) {
                                  particleSystem.rotation.y += 0.005;
                          
                                  const jitterMagnitude = 0.5; // Jitter magnitude
                                  const returnSpeed = 0.1; // Speed of returning to original position
                          
                                  if (jitter) {
                                      for (let i = 0; i < particlePositions.length; i += 3) {
                                          particlePositions[i] += (Math.random() - 0.5) * jitterMagnitude;
                                          particlePositions[i + 1] += (Math.random() - 0.5) * jitterMagnitude;
                                          particlePositions[i + 2] += (Math.random() - 0.5) * jitterMagnitude;
                                      }
                                  } else {
                                      for (let i = 0; i < particlePositions.length; i += 3) {
                                          particlePositions[i] += (originalPositions[i] - particlePositions[i]) * returnSpeed;
                                          particlePositions[i + 1] += (originalPositions[i + 1] - particlePositions[i + 1]) * returnSpeed;
                                          particlePositions[i + 2] += (originalPositions[i + 2] - particlePositions[i + 2]) * returnSpeed;
                                      }
                                  }
                                  particleSystem.geometry.attributes.position.needsUpdate = true;
                          
                                  // Update mesh position and rotation to match particles
                                  mesh.position.copy(particleSystem.position);
                                  mesh.rotation.copy(particleSystem.rotation);
                          
                                  // Toggle mesh visibility
                                  if (isMeshVisible) {
                                      mesh.visible = false;
                                  } else {
                                      mesh.visible = true;
                                  }
                              }
                          
                              elapsedTime += 0.01;
                              if (elapsedTime >= 2) { // Increase this value for longer duration in each state
                                  jitter = !jitter;
                                  elapsedTime = 0;
                                  isMeshVisible = !isMeshVisible; // Toggle mesh visibility
                              }
                          
                              renderer.render(scene, camera);
                          }