🙌🏻 해당 글은 Three.js Journey의 강의 노트입니다.
현재 우리는 정해진 크기의 캔버스에 WebGL를 맞춰 활용하고 있다. 그러나 때로는 immersive experience를 선사하기 위해 우리는 Full Screen을 활용해고 싶을 수 있다. 그러나 Full Screen에서 ThreeJS를 활용한다면, 물론이지 반응형에도 잘 대응해야 한다. 이번 레슨에서는 이 부분에 대해서 다뤄볼 것이다.
window.innerWidth
와 window.innerHeight
를 이용하면, 캔버스가 뷰포트에 딱 들어맞게끔 조정할 수 있다. (기본으로 생성되어있는 마진, 패딩은 style.css
를 통해 제거해준다.)
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
// styles.css
* {
margin: 0;
padding: 0;
}
html,
body
{
overflow: hidden;
}
.webgl {
position: fixed;
top: 0;
left: 0;
outline: none;
// ThreeJS에서 renderer.setSize를 사용해주고 있기 때문에,
// 따로 정확한 width와 height 속성을 입력해주지 않아도 된다.
}
이제 Full screen에서 ThreeJS를 경험할 수 있게 되었다. 그러나 여전히 리사이즈에는 대응이 불가능하다.
윈도우 리사이즈에 대응하기 위해서 우선 이벤트 리스너를 통해 리사이즈를 감지할 수 있도록 해준다. 그리고, 윈도우 사이즈가 변화함게 따라, sizes
, camera.aspect
을 수정해준다.
camera.aspect
는 카메라 절두체의 종횡비인데, 이 때 절두체란 사각뿔의 윗부분을 밑면에 평행하게 잘라낸 입체 형상을 가리킨다. 아래와 같은 형태! 일반적으로 캔버스 너비를 캔버스의 높이로 나눈 값이다.
추가로, camera.aspect
를 업데이트 해줄 때는 camera.updateProjectionMatrix()
메서드를 활용해서 프로젝션의 매트릭스를 같이 업데이트 해줘야 되는데, 이 부분에 대해서는 추후에 다루도록 한다.
그리고 마지막으로, renderer
까지 업데이트 해주면 리사이즈가 되는 모습을 살펴볼 수 있다!
window.addEventListener('resize', () => {
sizes.width = window.innerWidth
sizes.height = window.innerHeight
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
renderer.setSize(sizes.width, sizes.height)
})
모두가 그러한 것은 아니지만 우리들 중 일부는 오브젝트의 가장자리에 계단모양의 흐릿한 모습을 살펴볼 수 있을 것이다.
이는 픽셀 비율이 1보다 큰 화면에서 테스트를 하고 있기 때문에 그런 것이다. 픽셀 비율(Pixel Ratio)이란 소프트웨어의 한 픽셀 유닛에 대해 당신이 실제 스크린 위에 몇 개의 픽셀을 갖고 있는가를 말한다.
몇 년 전만 해도, 모든 스크린들을 pixel ratio 1의 스크린을 갖고 있었다. 스크린에 정말 가까이 다가가면 그 픽셀들을 살펴볼 수가 있었고, 그것이 곧 얼마나 이미지를 정확히 표현할 수 있는지, 그리고 폰트가 얼마나 가늘어질 수 있는지를 의미했다. 애플이 retina 디스플레이를 발표하면서, pixel ratio 2의 디스플레이가 등장하기 시작했다.
더 나은 이미지 퀄리티를 위해 개발된 이 retina 디스플레이는 결국 네 배 더 많은 픽셀을 렌더링 할 수 있게 되었다. pixel ratio가 3인 경우는 결과적으로 9배 더 많은 픽셀을 렌더링할 수 있는 것. 현재는 가장 작은 디바이스인 모바일의 경우에 가장 높은 pixel ratio를 도입하고 있다.
우리는 window.devicePixelRatio
를 통해서 스크린의 pixel ratio를 알 수 있고, pixel ratio를 세팅해주기 위해서는 renderer.setPixelRatio(...)
를 활용하면 된다. pixel ratio가 2인 경우도, 3인 경우도 있는데, 보통 2를 초과하는 값의 경우 사람의 눈으로 차이를 분간하기 어렵고, 실상은 마케팅이라 봐도 무방하다. 우리는 Math.min()
을 활용해 값을 제한해줄 것이다.
window.addEventListener('resize', () => {
sizes.width = window.innerWidth
sizes.height = window.innerHeight
camera.aspect = sizes.width / sizes.height
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
(현재 내가 사용중인 디바이스들은 모두 pixel ratio가 2이기 때문에 위 코드를 활성화했을 때와 아닐 때의 차이를 분간하기 어렵다.)
✨ Pixel Ratio에 대해서 다시 공부하기!
이번에는 fullscreen과 그렇지 않은 모드를 유저가 직접 선택할 수 있도록 지원해보려고 한다. 다양한 트리거를 활용할 수 있지만 이 레슨에서는 더블클릭을 통해 실습해보자. 더블클릭을 통해 전체화면 모드를 토글할 수 있도록 하는 것이다.
우리는 document.fullscreenElement
속성과 requestFullscreen()
, exitFullscreen()
메서드를 활용할 것이다.
window.addEventListener('dblclick', () => {
if(!document.fullscreenElement) {
canvas.requestFullscreen()
} else {
document.exitFullscreen()
}
})
아쉽게도 사파리에서는 위 코드가 유효하지 않다.
그래서 우리는 아래와 같이 코드를 수정해줄 것이다.
window.addEventListener('dblclick', () => {
const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement
if(!fullscreenElement) {
if(canvas.requestFullscreen) {
canvas.requestFullscreen()
} else if(canvas.webkitRequestFullscreen) {
canvas.webkitRequestFullscreen()
}
} else {
if(document.exitFullscreen) {
document.exitFullscreen()
} else if(document.webkitExitFullscreen) {
document.webkitExitFullscreen()
}
}
})