레슨 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)을 사용하는 것이 안정적입니다. 가변 타임스텝을 사용하면 프레임 드랍 시 물체가 벽을 통과하는 터널링 현상이 발생할 수 있습니다.