0. 목차
1. 물리세계
2. 어떤 라이브러리를 쓸 것인가? 2D vs 3D
3. 물리 라이브러리 종류
4. 물체를 물리 세계에 놓아보자 CANNON
5. 공과 바닥이 부딪혔을 때, 공이 튀지 않을까? Material
6. 공은 사실 데구르르 구른다 applyLocalForce
1. 물리세계
- three.js의 mesh가 생성될 때 물리세계에서도 mesh가 생성된다.
- 프레임마다 렌더링 전에 물리세계에서도 업데이트가 이루어지기 때문에, 물리 세계의 좌표가 먼저 지정된 후에, three.js의 mesh에 그 좌표가 적용되는 식으로 진행된다.
- 물리세계와 threejs의 mesh세계를 별도로 생각하면 이해하기 쉽다.
2. 어떤 라이브러리를 쓸 것인가? 2D vs 3D
- 3d의 물리법칙을 2d로 축약할 수 있다면 2d 물리 라이브러리를 써도 상관없다.
- 가령 당구장의 모션을 공은 원으로, 벽은 직사각형으로 생각하여 물리 법칙을 적용할 수 있다.
- 실제로 아래 사이트는 모든 충돌과 애니메이션을 2d 물리 라이브러리로 표현했다.
- 하지만 3d와 다르게 축을 다르게 생각해야하기 때문에 주의가 필요하다.
3. 물리 라이브러리 종류
- 3D
Ammo.js | http://schteppe.github.io/ammo.js-demos/ |
Cannon.js | https://schteppe.github.io/cannon.js/ |
Oimo.js | https://lo-th.github.io/Oimo.js/ |
- 2D
Matter.js | https://brm.io/matter-js/ |
P2.js | https://schteppe.github.io/p2.js/ |
Planck.js | https://piqnt.com/planck.js/ |
Box2D.js | http://kripken.github.io/box2d.js/demo/webgl/box2d.html |
4. 물체를 물리 세계에 놓아보자 [CANNON]
1) terminal에 cannon 라이브러리를 설치
npm install --save cannon
2) cannon 라이브러리 가져오기
import CANNON from 'cannon'
3) 먼저 세계를 구축한다.
- Textures 주석 아래에 Physics 주석을 배치한다.
- -9.82 : 지구의 중력 상수 → 다른 행성의 경우/ 또는 더 느리게 하고 싶을 때 다른 값 사용 가능
//----------------------------------------------
/**
* Physics
*/
// World
const world=new CANNON.World()
world.gravity.set(0,-9.82,0) //중력방향으로 vec3의 개체 만들어줌
//----------------------------------------------
4) 그 위에 물체를 배치한다.
- 물체의 경우 Sphere, Box, Cylinder, Plane 등이 있다. 먼저 기본 도형을 지정한다.
- 그 뒤에 만들어진 Body에 대해 질량과 위치를 지정해준다.
- addBody를 통해 세계에 추가 완료가 된다.
- 물리 세계 Cannon 업데이트 후 → threejs 세계가 업데이트 되므로 아래 코드를 써도 아무 일도 안 일어난다.
//----------------------------------------------
/**
* Physics
*/
// World
// Sphere
// 기본 도형
const sphereShape=new CANNON.Sphere(0.5)
// 질량과 위치
const sphereBody=new CANNON.Body({
mass:1,
position:new CANNON.Vec3(0,3,0), //떨어지도록 설정
shape:sphereShape,
})
//추가
world.addBody(sphereBody)
//----------------------------------------------
5) threejs 세계에 cannon 세계를 업데이트 한다.
- Three.js의 Clock 메소드를 이용해 업데이트 이전과 이후의 시간차를 deltaTime으로 구한다.
- step의 매개변수
- 프레임레이트
- 반복횟수
- 델타 시간 : 마지막 프레임 이후 경과된 시간 deltaTime
/**
* Animate
*/
const clock = new THREE.Clock()
let oldElapsedTime=0
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
const deltaTime=elapsedTime-oldElapsedTime
oldElapsedTime=elapsedTime
// console.log(deltaTime);
world.step(1/60,deltaTime,3)
// Update controls
// Render
// Call tick again on the next frame
}
tick()
- 중력에 의해 떨어지게 하기 위해선, threejs의 sphere가 Cannonjs의 sphereBody에 의해 업데이트 되게끔 한다.
- sphere의 position x,y,z를 직접 sphereBody의 position x,y,z에 대입해주는 방법과
- copy를 통해 전달하는 방법이 있다.
/**
* Animate
*/
const clock = new THREE.Clock()
let oldElapsedTime=0
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
const deltaTime=elapsedTime-oldElapsedTime
oldElapsedTime=elapsedTime
// console.log(deltaTime);
world.step(1/60,deltaTime,3)
sphere.position.copy(sphereBody.position)
// sphere.position.x=sphereBody.position.x
// sphere.position.y=sphereBody.position.y
// sphere.position.z=sphereBody.position.z
// Update controls
// Render
// Call tick again on the next frame
}
tick()
6) 공이 튕길 바닥을 추가한다.
- 이때 바닥은 중력의 영향을 받지 않아야 하므라 mass를 0으로 지정한다.
- 이번에 Body객체를 별개로 선언하고, 객체의 프로퍼티(.)에 값을 지정하는 식으로 해줌
//----------------------------------------------
/**
* Physics
*/
// World
// Sphere
//floor
const floorShape=new CANNON.Plane()
const floorBody=new CANNON.Body()
floorBody.mass=0 // 움직이지 않게, position도 건들 필요 없음
floorBody.addShape(floorShape)
world.addBody(floorBody)
//----------------------------------------------
7) 바닥의 방향을 올바르게 한다.
- 기본적으로 카메라 방향으로 움직임 → 회전 시켜 우리가 원하는 대로 만들어준다
- quaternion을 이용, 이때 매개변수의 경우
- 축은 x축(Vec3(-1,0,0))이 기준이 되며
- 각도는 90도(Math.PI*0.5)를 회전하면 된다
//floor
const floorShape=new CANNON.Plane()
const floorBody=new CANNON.Body()
floorBody.mass=0 // 움직이지 않게, position도 건들 필요 없음
floorBody.addShape(floorShape)
//추가!
floorBody.quaternion.setFromAxisAngle(
new CANNON.Vec3(-1,0,0), //x축을 기준으로 돌려야함
Math.PI*0.5
)
world.addBody(floorBody)
5. 공과 바닥이 부딪혔을 때, 공이 튀지 않을까? [Material]
1) 각각의 재질을 지정해줘서 적용하기
- 공의 재질인 concreteMaterial과, 바닥의 재질인 plasticMaterial을 지정해줘서
- 둘이 부딪혔을 때 특성을 가지고 움직일 수 있도록 한다.
- 얼마나 많이 저항하는가? friction
- 얼마나 많이 튀는가? restitution
- 물리 세계에 추가하는 함수는 addContactMaterial을 통해 처리한다.
//----------------------------------------------
/**
* Physics
*/
// World
// Material - 콘크리트와 플라스틱이 부딪히면 ?
const concreteMaterial=new CANNON.Material('concrete')
const plasticMaterial=new CANNON.Material('plastic')
const concretePlasticContactMaterial = new CANNON.ContactMaterial(
concreteMaterial,
plasticMaterial,
{
friction: 0.1,
restitution: 0.7
}
)
world.addContactMaterial(concretePlasticContactMaterial)
//----------------------------------------------
- sphereBody와 floorBody에 marterial 속성을 추가한다.
const sphereBody = new CANNON.Body({
// ...
material: plasticMaterial
})
// ...
const floorBody = new CANNON.Body()
floorBody.material = concreteMaterial
2) 한번에 재질을 지정해주기
- 기본적으로 사용되는 재질 하나를 설정
const defaultMaterial = new CANNON.Material('default')
const defaultContactMaterial = new CANNON.ContactMaterial(
defaultMaterial,
defaultMaterial,
{
friction: 0.1,
restitution: 0.7
}
)
world.addContactMaterial(defaultContactMaterial)
// ...
// sphereBody과 floorBody의 material을 따로 지정 !!
const sphereBody = new CANNON.Body({
// ...
material: defaultMaterial
})
// ...
floorBody.material = defaultMaterial
- 재질 자체를 world의 기본 재질로 설정할 수도 있다.
- 그렇게 되면 위의 sphereBody과 floorBody의 material을 따로 지정안해줘도 기본적으로 적용된다
world.defaultContactMaterial = defaultContactMaterial
6. 공은 사실 데구르르 구른다 [applyLocalForce]
- body에 힘을 적용하는 방법 : applyForce, applyImpulse, applyLocalForce, applyLocalImpulse
1) 데구르르 굴러가도록 하기
- applyLocalForce를 이용해 작은 임펄스를 부여
// Sphere
const sphereShape=new CANNON.Sphere(0.5)
const sphereBody=new CANNON.Body({
...
})
sphereBody.applyLocalForce(new CANNON.Vec3(150,0,0),new CANNON.Vec3(0,0,0))
world.addBody(sphereBody)
- 바람을 넣어보자
- 각 프레임마다 바람의 모션이 적용되도록
- 마냥 데구르르 굴러가는 것이 아닌, 굴러가다가 반대의 방향으로 돌아오게 하는 코드이다.
const tick = () =>
{
// ...
// Update physics
sphereBody.applyForce(new CANNON.Vec3(- 0.5, 0, 0), sphereBody.position)
world.step(1 / 60, deltaTime, 3)
// ...
}
해당 글은 아래의 강의를 참고하여 작성된 글입니다 :)
다음 번에는 위 물리 엔진 개념을 활용해 직접 프로젝트를 만들어 포스팅 해보겠습니다.
반응형