본문 바로가기
FrontEnd/THREE.js

물리공간 구축

by oncerun 2023. 4. 24.
반응형

 

 

질량을 갖는 공을 가지고 평면에 떨어지는 공을 만들어보자. 

 

cannon-es를 활용하여 물리공간을 구축할 것이다. 

 

three.js 외에도 cannon-es를 위한 별도의 추가 코드가 필요하다. 

 

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import * as CANNON from 'cannon-es';


export default function () {
  const renderer = new THREE.WebGLRenderer({
    alpha: true,
    antialias: true,
  });
  renderer.setClearColor(0x333333, 1);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  const container = document.querySelector('#container');

  container.appendChild(renderer.domElement);

  const canvasSize = {
    width: window.innerWidth,
    height: window.innerHeight,
  };

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    75,
    canvasSize.width / canvasSize.height,
    0.1,
    100
  );
  camera.position.set(5, 7, 5);

  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.1;



  const world = new CANNON.World();
  world.broadphase = new CANNON.SAPBroadphase(world);
  world.gravity.set(0, -9.82, 0);
  world.allowSleep = true;


  const worldObjects = [];


  const createLight = () => {
    const light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.set(0, 10, 0);
    light.castShadow = true;
    scene.add(light);
  };


  const createFloor = () => {
    const geometry =  new THREE.BoxGeometry(6, 1, 6);
    const material = new THREE.MeshStandardMaterial({ 
      color: 0xffffff,
      emissive: 0xffffff,
      emissiveIntensity: 0.3,
   });

    const mesh = new THREE.Mesh(geometry, material);
    mesh.receiveShadow = true;


    const shape = new CANNON.Box(new CANNON.Vec3(6 / 2, 1 /2, 6 / 2 ));
    const floorMaterial = new CANNON.Material({
      friction: 0.1,
      restitution: 0.7,
    }); 

    const floorBody = new CANNON.Body({
      shape,
      material: floorMaterial,
      mass: 0,
    });
    world.addBody(floorBody);

    worldObjects.push({mesh, body: floorBody});
    
    scene.add(mesh);
  };





  const createObject = () => {
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const geometry = new THREE.PlaneGeometry(1, 1);
    const mesh = new THREE.Mesh(geometry, material);

    scene.add(mesh);
  };

  const resize = () => {
    canvasSize.width = window.innerWidth;
    canvasSize.height = window.innerHeight;

    camera.aspect = canvasSize.width / canvasSize.height;
    camera.updateProjectionMatrix();

    renderer.setSize(canvasSize.width, canvasSize.height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  };

  const addEvent = () => {
    window.addEventListener('resize', resize);
  };

  const draw = () => {
    controls.update();
    renderer.render(scene, camera);
    world.step(1 / 60);

    worldObjects.forEach((worldObject) => {
      worldObject.mesh.position.copy(worldObject.body.position);
      worldObject.mesh.quaternion.copy(worldObject.body.quaternion);
    });


    requestAnimationFrame(() => {
      draw();
    });
  };

  const initialize = () => {
    createObject();
    addEvent();
    resize();
    draw();
    createFloor();
    createLight();
  };

  initialize();
}

 

물리 공간을 만들고 Three.js를 통해 3D 객체를 추가할 때 빛을 상당히 많이 활용한다. 

 

따라서 성능에 따라 다양한 Material을 고민해야 할 것 같다.

 

 

    const material = new THREE.MeshStandardMaterial({ color: 0xeeeeee });
    const geometry = new THREE.SphereGeometry(0.3, 32, 32);
    const mesh = new THREE.Mesh(geometry, material);
    mesh.castShadow = true;
    mesh.receiveShadow = false;

    mesh.position.y = 5;

    const shape = new CANNON.Sphere(0.3);
    const sphereMaterial = new CANNON.Material({
      friction: 0.1,
      restitution: 0.7,
    });

    const sphereBody = new CANNON.Body({
      shape,
      material: sphereMaterial,
      mass: 1,
    });
    sphereBody.position.copy(mesh.position);
    world.addBody(sphereBody);
    worldObjects.push({mesh, body: sphereBody});

    scene.add(mesh);

 

CANNON 객체를 활용하여 three.js mesh와 동일한 반지름을 갖는 shape와 마찰력과 반환값을 주고 다음으로는 Body를 만들어 준다  Body에는 이전에 만든 shape와 material을 주고 중요한 질량을 부여한다. 

 

body의 포지션은 기본값은 0이다. 

 

이러면 다음과 같은 공이 튀기는 현상이 보인다. 

 

 

물리적인 힘은 어떻게 줘야 할까?

 

이를 위해선 body의 이름을 짓고 draw 시 힘을 가하는 방법이 있다.

 

    sphereBody.name = 'sphere';


    worldObjects.forEach((worldObject) => {

      if (worldObject.body.name === 'sphere') {
        worldObject.body.applyForce(new CANNON.Vec3(0, 0, 2), worldObject.body.position);
      }

      worldObject.mesh.position.copy(worldObject.body.position);
      worldObject.mesh.quaternion.copy(worldObject.body.quaternion);
    });

 

힘의 벡터와 질량의 중심이 되는 상대적인 포인트를 주는데, 일반적으로 body의 포지션을 준다. 

 

 

일정하게 힘을 주는 것 말고도 applyImpulse로 충격을 줄 수도 있다. 

 

https://pmndrs.github.io/cannon-es/docs/index.html

 

cannon-es

Getting Started cannon-es is a lightweight and easy to use 3D physics engine for the web. It is inspired by the three.js' simple API, and based upon ammo.js and the Bullet physics engine. The first thing to set up is our physics world, which will hold all

pmndrs.github.io

 

 

반응형

'FrontEnd > THREE.js' 카테고리의 다른 글

flat, smooth shading  (1) 2023.05.06
Three.js 라이브러리를 확장한 객체지향 구조  (0) 2023.04.26
3D Computer Graphics (4)  (0) 2023.04.15
Post Processing  (0) 2023.04.13
Setting  (0) 2023.04.11

댓글