[Three.js journey 강의노트] 05

9rganizedChaos·2021년 7월 6일
1
post-thumbnail

🙌🏻 해당 글은 Three.js Journey의 강의 노트입니다.

05 Transform objects

scene에서 object를 변형하는 4 가지 속성이 있다.

  • position : 위치 변경
  • scale : 크기 변경
  • rotation : 회전
  • quaternion : 회전과 관련된 속성 (후에 자세히 다룸)

perspectiveCamera나 Mesh 등 Object3D를 상속받은 모든 Class는 위와 같은 속성을 갖는다. 그리고 해당 속성들은 matrices라고 부르는 것으로 컴파일된다. 물론 위와 같은 속성들이 있기 때문에 우리가 matrices를 직접 건들일 필요는 없다.

position

position은 x, y, z라는 주요한 속성을 갖는다. 세 속성은 3D공간에서의 각 축을 나타낸다. 1이라는 단위는 코드 작성자에게 달려있다. 마음대로 생각해도 좋다. 속성들을 이용해 object를 다양한 곳으로 이동시킬 수 있지만, 중요한 것은 render 메서드를 호출하기 전에 이동시켜야 한다는 점!

//...

const cube1 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
cube1.position.x = 0.25;
cube1.position.y = 0.75;
cube1.position.z = 1.2;

//...

position 속성은 Vector3의 인스턴스이다.

x, y, z 말고도 다양한 유용한 속성들이 존재한다!

  • mesh.position.length(): (0, 0, 0)부터, 점, 혹은 mesh까지의 거리
  • mesh.position.distanceTo(mesh): object로부터 mesh까지의 거리
  • mesh.position.normalize(): 방향을 유지하면서 length를 1로 설정하는 메소드
console.log(mesh.position.length())
console.log(mesh.position.distanceTo(camera.position))
console.log(mesh.position.normalize())

  • mesh.position.set(x, y, z): x, y, z를 한 번에 설정하는 메소드
mesh.position.set(0.7, - 0.6, 1)

Axes helper

축을 시각화해서 보여주는 속성이다. 인자로 축의 길이를 넘겨줄 수 있다.

const axesHelper = new THREE.AxesHelper(2)
scene.add(axesHelper)

파랑-z, 초록-y, 빨강-x

위 이미지의 경우 z축이 보이지 않는데, 이는 카메라를 기준으로 완벽하게 align되어있기 때문이다.

scale

scale도 Vector3이므로, 기본적으로, x, y, z, 기본값 1인 속성들을 갖는다. (위에서 언급한 Vector3의 메소드도 모두 사용 가능하다.) scale은 사이즈를 다루는 속성이다. 0.5로 설정하면 기본 1을 기준으로 했을 때보다 반인 크기로, 2로 설정하면 기본 1을 기준으로 했을 때보다 두 배 큰 크기로 오브젝트가 설정된다.

mesh.scale.x = 2
mesh.scale.y = 0.25
mesh.scale.z = 0.5 

음수 값을 사용할 수는 있지만, 축이 logical direction에 위치하지 않게 되고, 이로 인해 나중에 버그가 발생할 수 있으므로 사용을 지양하는 편이 좋다.

rotation

회전은 전에 다룬 스케일이나 포지션에 비해 까다롭다. 회전을 다루는 두 가지 속성이 있다. 좀 더 명확한 것이 rotation, 조금 모호한 것이 quaternion이다. 물론 두 속성은 하나를 업데이트하면, 나머지 하나도 자동으로 업데이트된다. (선호하는 방식을 사용하면 된다.)

rotation 역시 x, y, z 속성을 갖고 있지만, 이들은 Vector3가 아니라, Euler이다.

  • y축 회전: 회전목마
  • x축 회전: 정면에서 다가오는 자동차의 바퀴
  • z축 회전: 비행기를 정면에서 바라보았을 때 프로펠러의 회전

단위는 라디안을 사용하므로 Math.PI를 이용한다.

mesh.rotation.x = Math.PI * 0.25

mesh.rotation.y = Math.PI * 0.25

mesh.rotation.z = Math.PI * 0.25

회전할 때 유념해야 할 것은 x축을 회전하면 다른 축의 방향도 동시에 변한다는 점이다.
아래는 x, y축 90도 회전을 동시에 적용한 모습

mesh.rotation.x = Math.PI * 0.25
mesh.rotation.y = Math.PI * 0.25

회전은 x, y, z의 순서로 적용된다. 이것은 짐벌잠금이라고 부르는 이상한 결과를 낳을 수 있다! (한 축이 돌고 나면 나머지 축의 상태가 변경되어 원치 않는 결과를 낳는...!) 그래서 우리는 reorder메서드를 통해, object.rotation.reorder ('yxz') 이런식으로 순서를 변경해줄 수 있다.
Euler는 이해하기 쉽지만, 위와 같은 문제가 발생할 수 있다. 그래서 Quaternion을 쓰는 것이다. Quternion은 다음에 더 자세하게 다룰 것!

lookAt

Object3D 인스턴스에는 오브젝트에게 다른 어떤 오브젝트를 바라보도록 요청하는 lookAt()이라는 메서드가 있다. (매개변수는 그 대상이며, Vector3여야 한다.) 이 메서드를 사용하면 오브젝트는 -z축을 메서드의 매개변수에 넘겨준 대상을 향하게끔 만들어준다.

이 메서드를 활용해서 카메라가 어떤 오브젝트를 바라보도록 해줄 수 있다. 게임으로 치면 총구가 적을 겨누도록 할수도 있으며, 캐릭터의 눈이 특정 물체를 바라보도록 해줄 수도 있다.

camera.lookAt(cube1.position)
camera.lookAt(new THREE.Vector3(0, 0, 0))

큐브가 올라간듯 보이지만, 사실은 카메라가 밑으로 내려간 것이다!

Scene graph

위에서 살펴본 요소들을 우리는 여러 오브젝트에 적용해줄 수 있다. 그러나 일일이 모든 오브젝트의 위 속성들의 값을 변경해주는 것은 번거로운 작업일 것이다. 우리는 오브젝트들을 그룹화할 수 있다!

1) 그룹을 먼저 인스턴스화한다.
2) 인스턴스화한 그룹을 scene에 추가한다.
3) 그룹에 들어갈 다른 오브젝트는 scene에 바로 add하지 않고, group에 add한다.
4) 이제 그룹에 대해 한 번에 변형을 시도할 수 있다.

const group = new THREE.Group();
group.scale.y = 1;
group.rotation.y = 1;
scene.add(group);

const cube1 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
cube1.position.x = -1.5;
group.add(cube1);

const cube2 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
cube2.position.x = 0;
group.add(cube2);

const cube3 = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
cube3.position.x = 1.5;
group.add(cube3);

그룹 자체도 Object3D의 인스턴스 이므로, 위의 모든 속성을 상속받는다!
그룹은 그냥 묶는 것이 아니라 scene 속의 작은 scene과 같이서 회전을 하면 각각이 회전을 똑같이 하는 것이 아니라 전체가 한 번에 회전한다.

const group = new THREE.Group();
group.scale.y = 0.5;
group.rotation.x = 1;
group.rotation.y = 0.5;
scene.add(group);

profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!

0개의 댓글