const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 100)
PerspectiveCamera에선 4개의 인자가 존재한다.
순서대로 fov
, aspect ratio
, near
& far
aspect ratio (종횡비)
화면의 너비와 높이의 비율을 의미
카메라가 렌더링할 장면의 왜곡을 방지하는 데 중요한 역할을 한다!
예를 들어, 화면이 정사각형(1:1 비율)인데 16:9 비율의 종횡비를 설정하면 렌더링된 장면에서 왜곡이 발생할 수 있음
Near & Far
Near and far는 카메라가 얼마나 멀리까지 볼 수 있는지, 그리고 얼마나 가까이에 있는 물체를 보여줄지 결정해주는 인자이다. 범위 밖에 존재하는 오브젝트들은 아예 렌더되지 않는다. 애초에 카메라가 얼마나 가까이에 있는 물체까지만 인지할 것인지, 어느 정도 거리까지의 물체들까지만 렌더할지를 설정해줄 수 있는 것이다. 0.1에서 100사이의 합리적인 숫자 내에서 해당 값들을 설정해볼 수 있다. 이 인자는 z-fighting 버그를 방지하기 위해 유용한 인자이다. (출처: https://velog.io/@9rganizedchaos)
👆 자세한 예제는 해당 벨로그를 통해 확인하자.
원근감이 배제되어있는 카메라이다 마치 isometric vue처럼 거리에 따라 크기가 변하지 않고, 실제 크기를 기반으로 표시
const camera = new THREE.OrthographicCamera(- 1, 1, 1, - 1, 0.1, 100)
인자는 순서대로 left
, right
, top
, bottom
, near
, far
처음 4개의 인자가 바로 카메라가 보는 영역의 경계이다.
만약 위 코드로 큐브를 바라본다면 높이가 찌그러져있는데 (원래 정육면체) 그 이유는 우리의 캔버스 사이즈가
size = {
width: 800
height: 600
}
이기 때문에 캔버스는 가로 길이가 더 긴데,
위 코드로 인해 카메라 영역 (- 1, 1, 1, - 1)
은 정사각형(정방형) 공간을 렌더하고있음. 즉 정방형 공간이 우리의 캔버스에 맞게 늘려져서 높이가 낮은 직육면체처럼 보이는 것이다.
이로 인해 left, right에 종횡비를 곱해주자
const aspectRatio = sizes.width / sizes.height
const camera = new THREE.OrthographicCamera(
- 1 * aspectRatio,
1 * aspectRatio,
1,
- 1,
0.1,
100
)
잘 나온다!
만약 마우스를 왼쪽으로 옮기면 카메라도 왼쪽으로 가게끔 하려면 어떻게해야할까?
우선 마우스의 위치를 알아야하는데 이는 window.addEventListener
를 통해 알 수 있다.
window.addEventListener('mousemove',(e) => {
console.log(e.clientX/Y)
})
브라우저 가장 왼쪽이 e.clientX === 0
오른쪽으로 갈수록 값이 커진다
브라우저 최상단일때 e.clientY === 0
아래로 갈수록 더 값이 커진다.
const cursor = {
x: 0,
y: 0,
}
마우스 커서의 x,y 위치를 먼저 0으로 미리 지정!
해당 검은 canvas 화면에서 커서 x,y의 위치가 -0.5 ~ +0.5
의 범위로 지정해주고싶다
=> e.clientX/Y를 canvas의 w/h 로 나눈뒤 -0.5 해주면된다
window.addEventListener('mousemove', e => {
cursor.x = e.clientX / sizes.width - 0.5 // 마우스의 cursor.x 값이 -0.5 ~ +0.5
cursor.y = - (e.clientY / sizes.height - 0.5) // 마우스의 cursor.y 값이 -0.5 ~ +0.5
})
const tick = () => {
// Update camera
camera.position.x = cursor.x * 3 //3을 곱해준 이유는
camera.position.y = cursor.y * 3
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
cursor.y에 -를 붙인 이유는, e.clientY가 아래로 갈수록 더 값이 커지기때문
위 gif처럼 카메라가 커서의 방향에따라 잘 이동하고있다.
하지만 정육면체의 뒷면은 볼 수 없는데, 정육면체를 중심으로 회전목마처럼 4면을 보도록 회전하는 카메라를 만드려면 어떻게 해야할까?
우선 camera.position
x
와 z
를 변경해야하는데 그 이유는
3차원에서 노란 원점을 중심으로 회전목마처럼 돌려면 x,z의 position으로 빙그르르 돌기때문이다....
이 gif 처럼 카메라가 이동해야함
이 3차원 axis를 보시라... 우리는 y축을 기준으로 x,z방향으로 돌아야한다. 2d 평면에서 원점을 중심으로 시계반대방향으로 돌아야하므로, z x축에서도 시계반대방향으로 돌아야한다.
즉, 위 gif 2d 평면으로 봤을때
- 2d 평면 y축이 -> x
- 2d 평면 x축->z로 됨
z = r cos(α)
x = r sin(α)
각도 α는 결국 cursor.x의 값이고.. 커서가 더 오른쪽으로 갈수록 각도 C->B의 이동과 같다
const tick = () => {
const elapsedTime = clock.getElapsedTime()
// Update objects
//mesh.rotation.y = elapsedTime
// Update camera
camera.position.x = Math.sin(cursor.x * 10) * 3
camera.position.z = Math.cos(cursor.x * 10) * 3
camera.lookAt(mesh.position)
//3은 반지름 크기.. 얼마큼 멀리서 찍을건가
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
그래서 camera.position.x 가 Math.sin()
camera.position.z가 Math.cos()이 되는것이다.
근데 위의 코드는 10을 곱하고있어서
마우스 커서가 왼 -> 오른쪽으로 갈때 정육면체 회전이 빠르게 이루어지는데
마우스 커서가 왼 -> 오른쪽으로 갈때 딱 4면체만 도는 한바퀴만 돌고싶다면
2 * Math.PI
를 곱해주면된다.
2π * 반지름 = 원 둘레니까
const tick = () => {
const elapsedTime = clock.getElapsedTime()
// Update objects
//mesh.rotation.y = elapsedTime
// Update camera
camera.position.x = Math.sin(cursor.x * 2 * Math.PI) * 3
camera.position.z = Math.cos(cursor.x * 2 * Math.PI) * 3
camera.lookAt(mesh.position)
//3은 반지름 크기.. 얼마큼 멀리서 찍을건가
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
예시 : https://threejs.org/examples/#misc_controls_orbit
import { OrbitControls } from 'three/examples/jsm/Addons.js'
const controls = new OrbitControls(camera, canvas)
첫번째 인자에는 카메라를, 두번째 인자에는 orbitcontrols가 적용될 화면을 넣는다. 현재 canvas를 이용하여 화면에 렌더링하고있으므로 canvas넣음.
기본적으로 카메라는 scene의 중앙을 바라보는데 target 프로퍼티를 이용해 이를 변경해줄 수 있다. 해당 프로퍼티 역시 Vector3를 값으로 받으므로, x, y, z 각각을 조정해줄 수 있다. 마지막에 update 함수를 꼭 실행시켜야한다
const controls = new OrbitControls(camera, canvas)
controls.target.x = -1
controls.update()
OrbitControls에 더 스무스한 애니메이션을 제공한다
또한 컨트롤이 controls.update()를 통해 매 프레임마다 업데이트되어야 한다.
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
const tick = () => {
controls.update()
}
https://discourse.threejs.org/t/sine-and-cosine-in-positioning-elements/64705