- 27. Orbit Controls 부터 28. Lerp 까지.
Orbit Controls
- 카메라가 타켓 근처 궤도를 돌도록 한다.
- 별도의 설정을 하지 않았을시, 마우스를 통해 다양한 조작을 할 수 있다.
- 마우스 왼쪽 클릭 시 카메라를 회전한다.
- 마우스 휠로 줌 인/줌 아웃을 할 수 있다.
- 마우스 오른쪽 클릭 시 Panning(pan: ’카메라가 대상을 따라다니며 보여주다, 찍다’ 라는 의미) 한다.
- Orbit Controls가 설정된 카메라가 바라보는 타겟을 이동시키는 행위인 듯.
- addons에서 import 하여 사용할 수 있다.
- 생성자는 2개의 인자를 받는다.
- object: 컨트롤될 카메라 객체. Scene 이외의 다른 객체의 자식이어선 안 된다.
- domElement: 이벤트 리스너에 사용될 HTML 요소.
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
new OrbitControls(camera, renderer.domElement);
OrbitControls을 사용할 때 주의할 점은, 카메라를 lookAt으로 컨트롤하는 동시에 OrbitControls을 사용하는 것은 제대로 동작하지 않는다는 점이다.
만약 OrbitControls을 사용하는 동시에 카메라가 바라보는 지점을 바꾸고 싶을 경우, camera.lookAt을 사용하는 것을 지양하되 OrbitControls.target.set 메서드를 사용한 후 OrbitControls.update를 호출해야 한다.
속성
- autoRotate: 타겟을 중심으로 자동 회전하게끔 하는 속성.
- 이 속성을 사용하기 위해선 애니메이션 루프에서 꼭 OrbitControls을 업데이트해야 한다(즉, update 함수를 호출해야 한다).
- autoRotateSpeed: 회전 속도를 나타내는 속성.
- enableDamping: 물체의 회전에 관성(inertia)을 추가한다. 이것을 활성화하면 관성이 적용되어 타겟을 회전시킬 때 약간의 무게를 느끼게 한다.
- dampingFactor: 관성이 적용되어있을 시, 관성의 정도를 나타낸다.
- 위의 두 관성 관련 속성 또한, autoRotate처럼 애니메이션 루프에서 OrbitControls의 업데이트가 이루어져야 한다.
- listenToKeyEvents: 인자로 넘겨진 DOM 요소에 키보드 이벤트 리스너를 추가한다. 공식적으로 추천되는 인자는 window 이다.
- 별도로 설정하지 않는다면, 방향키로 움직일 시 타겟이 이동한다.
- keys: 키보드를 사용해서 조작할 때, 사용할 키를 선택할 수 있다.
- 아래 코드는 타겟 이동 키로 WASD를 설정한다.
import * as THREE from 'three';
const controls = new OrbitControls(camera, renderer.domElement);
controls.listenToKeyEvents(window);
controls.keys = {
LEFT: 'KeyA', // default 'ArrowLeft'
UP: 'KeyW', // default 'ArrowUp'
RIGHT: 'KeyD', // default 'ArrowRight'
BOTTOM: 'KeyS' // default 'ArrowDown'
};
- mouseButtons: keys와 비슷하게 마우스로 조작할 때 각 마우스 버튼이 어떤 기능을 담당할지 선택할 수 있다.
- 각 기능에 대한 정보는 Three.js에 Enum으로 정의되어 있다.
import * as THREE from 'three';
const controls = new OrbitControls(camera, renderer.domElement);
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 타겟 회전
MIDDLE: THREE.MOUSE.DOLLY, // 카메라 확대/축소
RIGHT: THREE.MOUSE.PAN // Panning
};
- touches: 스마트폰 등으로 터치했을 때의 조작을 설정한다.
import * as THREE from 'three';
const controls = new OrbitControls(camera, renderer.domElement);
controls.touches = {
ONE: THREE.TOUCH.ROTATE, // 한 손가락으로 터치했을 때의 동작
TWO: THREE.TOUCH.DOLLY_PAN // 두 손가락으로 터치했을 때의 동작
};
- screenSpacePanning: Panning 중 카메라의 위치가 어떻게 바뀔지를 결정한다. true일 시 카메라는 Scene 공간 내에서 Panning하며, false면 카메라의 위쪽 방향과 직교하는 평면으로 Panning 한다.
- 기본값은 true로 설정되어 있으며, false로 설정할 경우 카메라가 앞/뒤로 이동하는 모습을 보인다.
- minAzimuthAngle / maxAzimuthAngle: 수평적으로 얼마나 회전을 시킬 수 있는지를 결정한다. -2 PI와 2PI 사이 값을 가져야 하며, max - min이 2 PI보다 작아야 한다.
Azimuth: 방위각을 의미한다.
- minPolarAngle / maxPolarAngle: 수직적으로 얼마나 회전을 시킬 수 있는지 결정한다. 0 ~ PI 사이 값을 가져야 한다.
- minDistance / maxDistance: 얼마나 확대/축소 할 수 있는지 그 거리를 결정한다.
- enabled: Orbit Control을 활성화할지, 하지 않을지를 결정한다.
- enablePan / enableRotate / enableZoom: 각각 Panning, 회전, 확대를 활성화할지, 하지 않을지 결정한다.
메서드
- getPolarAngle(): 현재의 수직 회전각을 반환한다.
- getAzimuthalAngle(): 현재의 수평 회전각을 반환한다.
강의에서는 위 두 메서드를 살폈는데, 이외에도 다양한 메서드들이 존재한다. (문서)
Lerp
- 물체를 애니메이팅 하는 방법 중 하나가 Lerp이다.
- 이 강의를 비롯한 앞으로의 몇 강의들에서는 물체를 애니메이팅 하는 방법들에 대해서 다뤄볼 예정.
- 이번 강좌에서는 Three.js에서 제공하는 기본 클래스를 확장 구현하는 예제도 다룰 예정.
Lerp란?
- Linear Interpolation(선형 보간법)을 의미하며, 여기서는 이를 이용한 애니메이팅을 뜻한다.
- Three.js의 다양한 클래스들이 lerp 메서드를 갖고 있으며, 이를 직접 구현하는 것도 가능하다.
커스텀 클래스 만들기
- 강의에서 만드는 클래스는 아래와 같은 구조를 가진다.
class Pickable extends Mesh {
hovered = false
clicked = false
colorTo: Color
defaultColor: Color
geometry: BufferGeometry
material: MeshStandardMaterial
v = new Vector3()
constructor(geometry: BufferGeometry, material: MeshStandardMaterial, colorTo: Color) {
// 생성자. 패러미터로 받은 인자들을 속성에 할당한다.
// geometry와 material은 Mesh 클래스의 기본 인자로, colorTo만 새로이 추가된 인자이다.
super()
this.geometry = geometry
this.material = material
this.colorTo = colorTo
this.defaultColor = material.color.clone()
this.castShadow = true
}
update(delta: number) {
// 애니메이션 루프에서 실행될 함수이다.
// 물체의 현재 상태에 따른 애니메이션을 수행한다.
// 물체가 클릭되었다면 MathUtils.lerp를 사용해 물체를 위/아래로 이동한다
this.clicked ? (this.position.y = MathUtils.lerp(this.position.y, 1, delta * 5)) : (this.position.y = MathUtils.lerp(this.position.y, 0, delta * 5))
}
}
- update 메서드를 살펴보면, MathUtils.lerp 메서드를 사용하는 것을 알 수 있다.
- MathUtils.lerp는 인자로 들어온 start와 end값을 주어진 보간 계수(delta * 5)로 보간하여 반환한다.
- 우선은 쉽게 말해, MathUtils.lerp가 호출될 때마다 start값이 end에 점점 더 가까워진다고 이해하면 된다.
- 단, 가까워질 뿐이지 end 값에 도달한다는 것은 아니다(그저 무한히 가까워지기만 한다).
- 따라서 구체적으로 특정 값에 도달하는 것을 원한다면, 직접 lerp함수를 구현하여 사용하는 방법이 있다.
const lerp = (from: number, to: number, speed: number) => {
const amount = (1 - speed) * from + speed * to;
// 어느 정도 목표 값에 가까워지면 목표 값을 반환한다
return Math.abs(from - to) < 0.001 ? to : amount;
};
- 점점 수학적 내용이 늘어가는 것 같다. 각오는 했지만 끙끙 앓고 있다...
'공부 > etc' 카테고리의 다른 글
Three.js and TypeScript (11) (2) | 2024.12.03 |
---|---|
Three.js and TypeScript (10) (0) | 2024.11.25 |
Three.js and TypeScript (8) (1) | 2024.11.21 |
Three.js and TypeScript (7) (2) | 2024.11.20 |
Three.js and TypeScript (6) (0) | 2024.11.19 |