Learning
레슨 6 / 8·20분

물리와 인터랙션

3D 물리 엔진 연동

Three.js 자체에는 물리 엔진이 없지만, Cannon.js나 Rapier 같은 물리 라이브러리를 연동하여 중력, 충돌, 반발 등 현실적인 물리 시뮬레이션을 구현할 수 있습니다. 물리 월드에서 계산된 위치와 회전 값을 Three.js 메시에 매 프레임 동기화합니다.

javascript
import * as THREE from 'three';
import * as CANNON from 'cannon-es';

// 물리 월드 생성
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);

// 바닥 (물리)
const groundBody = new CANNON.Body({
  mass: 0,
  shape: new CANNON.Plane(),
});
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0);
world.addBody(groundBody);

// 바닥 (Three.js)
const groundMesh = new THREE.Mesh(
  new THREE.PlaneGeometry(20, 20),
  new THREE.MeshStandardMaterial({ color: 0x333333 })
);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);

// 공 (물리)
const sphereBody = new CANNON.Body({
  mass: 1,
  shape: new CANNON.Sphere(0.5),
  position: new CANNON.Vec3(0, 5, 0),
  restitution: 0.7,
});
world.addBody(sphereBody);

// 공 (Three.js)
const sphereMesh = new THREE.Mesh(
  new THREE.SphereGeometry(0.5, 32, 32),
  new THREE.MeshStandardMaterial({ color: 0xff4444 })
);
scene.add(sphereMesh);
javascript
// 물리 시뮬레이션 루프
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);
  const delta = clock.getDelta();

  // 물리 월드 업데이트
  world.step(1 / 60, delta, 3);

  // 물리 -> Three.js 동기화
  sphereMesh.position.copy(sphereBody.position);
  sphereMesh.quaternion.copy(sphereBody.quaternion);

  renderer.render(scene, camera);
}
animate();

레이캐스팅으로 마우스 인터랙션

javascript
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

window.addEventListener('click', (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(scene.children, true);

  if (intersects.length > 0) {
    const clicked = intersects[0].object;
    console.log('Clicked:', clicked.name);

    // 색상 변경
    if (clicked.material) {
      clicked.material.color.setHex(Math.random() * 0xffffff);
    }

    // 물리 힘 적용
    const body = clicked.userData.physicsBody;
    if (body) {
      body.applyImpulse(
        new CANNON.Vec3(0, 5, 0),
        body.position
      );
    }
  }
});
  • CANNON.World — 물리 계산을 수행하는 가상 세계
  • CANNON.Body — 질량, 모양, 위치를 가진 물리 객체
  • mass: 0 — 정적 객체 (바닥, 벽)
  • Raycaster — 광선과 3D 객체의 교차점 계산
  • applyImpulse — 물리 바디에 순간적인 힘을 가함
💡

물리 시뮬레이션의 step() 메서드는 고정 타임스텝(1/60)을 사용하는 것이 안정적입니다. 가변 타임스텝을 사용하면 프레임 드랍 시 물체가 벽을 통과하는 터널링 현상이 발생할 수 있습니다.