threejs를 typescript에서 이용하기 위한 초기 세팅을 알아보도록 한다.
github 등에 다른 사람들이 올려놓은 template를 가져와서 사용하는 것이 빠르고 좋다.
하지만 그 template의 기본적인 설정 조차도 ground부터 하나하나씩 올려놓고 싶은, 백지 상태에서 출발해보고자 한다면 다음과 같은 방법으로 threejs를 typescript를 이용해서 시작해보도록 한다.
package.json 만들기
npm init
npm으로 package.json을 만들어준다.
Threejs 설치
npm i three
typescript threejs를 사용하기 위해서는 three 모듈 뿐 아니라 @types/three라는 threejs type을 개발 package로 받아야한다.
TypeScript Threejs 설치
npm i @types/three
types/three를 설치해주면 이제 typescript에서 threejs를 사용하기 위한 기초 준비는 끝났다.
개발 서버 설치
개발 서버 구동을 위해서 vite를 사용한다.
npm i vite
npx vite
단순히 vite를 npm으로 설치하고 실행하면 로컬 서버가 구동된다.
package.json depenencies와 devDependencies 차이
"devDependencies": {
"@types/three": "^0.163.0",
"typescript": "^5.4.5",
"vite": "^5.2.9"
},
"dependencies": {
"lil-gui": "^0.19.2",
"three": "^0.163.0"
}
간혹 보면 package.json에 dependency와 devDependency가 따로 있다.
"dependencies": {
"@types/three": "^0.164.0",
"three": "^0.164.1",
"vite": "^5.2.11"
}
방금 위에 명령어들로 설치하면 이렇게 dependencies에만 추가되는데 무슨 차이일까
dev가 붙어있다는 것은 개발 전용인데, 개발과 구분되는 개념은 빌드이다.
즉 build시 포함되는 것은 dependencies에 빌드에 포함되지 않아야 하는 것은 구분해서 devDependencies로 설치한다.
devDependencies로 분리하기
"dependencies": {
"@types/three": "^0.164.0",
"three": "^0.164.1",
"vite": "^5.2.11"
}
그렇다면 방금 설치한 것들 중에서 개발에만 필요한게 무엇이 있을까.
일단 개발 전용 호스팅 서버인 vite가 있다.
vite 삭제
npm uninstall vite
vite를 dependencies에서 삭제하고
npm i --save-dev vite
devDependencies로 설치한다.
typescript의 dependencies
그리고 나서 typescript를 사용하고 있다는 점을 유의해야한다.
typescript는 무엇인가?
typescript라는 것은 컴파일 할 때 javascript로 변환된다. 즉 런타임에서는 typescript는 아무런 의미가 없는 것이다.
오로지 컴파일을 위해서 사용되는 규칙과 같은 것이기 때문에, typescript도 마찬가지로 개발할 때만 사용하는 패키지이다.
npm i --save-dev typescript
--save-dev를 이용해서 devDependencies로 설치한다.
threejs typescript 삭제
npm uninstall @types/three
마찬가지로 삭제하고,
npm i --save-dev @types/three
devDependencies로 설치
threejs의 경우는 빌드에서 필요한 패키지이므로 dependencies에 남겨둔다.
package.json
{
"name": "three.js-fluid-simulation",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@types/three": "^0.163.0",
"typescript": "^5.4.5",
"vite": "^5.2.9"
},
"dependencies": {
"gl-matrix": "^3.4.3",
"lil-gui": "^0.19.2",
"three": "^0.163.0"
}
}
이렇게 현재 package.json의 상태가 되었고,
위 코드를 복사해서 package.json을 만들어서 npm i 를 통해 한 번에 모듈을 설치해도 된다.
index html 화면 canvas 띄우기
이제 index에 canvas element 하나를 type/three를 이용해서 화면을 띄워 볼 것이다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>three.js project</title>
</head>
<body>
<canvas id="scene"></canvas>
<script type="module" src="/src/test-scene.ts"></script>
</body>
</html>
위 규격의 html 코드에 canvas id에 접근하여 rendering을 하자
프로젝트 폴더 안에 index.html을 만들어서 위 코드를 넣고
이제 ts파일을 만든다
scene.ts 추가하기
src폴더를 만들고 그 안에 test-scene.ts 파일을 만든다.
import * as THREE from 'three'
가장 우선은 three.js import 코드이다.
이걸 가지고 모든 three.js 요소들을 사용하자.
//#region render setting (scene, canvas, renderer)
const canvas_id = 'scene'
const canvas: HTMLElement = document.querySelector(`canvas#${canvas_id}`)!
const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
const scene: THREE.Scene = new THREE.Scene()
//#endregion
먼저 화면을 렌더링할 render setting을 해준다.
이걸로 scene, canvas, renderer를 만들어준다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>three.js project</title>
<style>
body,
html {
height: 100%;
}
canvas {
height: 100%;
width: 100%;
outline: none;
background: rgb(34, 193, 195);
background: linear-gradient(
0deg,
rgb(8, 163, 166) 0%,
rgba(79, 166, 167, 0.849) 8%,
rgba(61, 79, 94, 0.885) 40%,
rgb(17, 19, 23)
);
}
</style>
</head>
<body>
<canvas id="scene"></canvas>
<script type="module" src="/src/test-scene.ts"></script>
</body>
</html>
캔버스의 크기를 페이지의 전체 100%로 설정해준다.
그리고 캔버스의 경계가 어디까진지 잘 보이도록 css로 채색해주자.
이건 three.js에서 설정한게 아니라 CSS 스타일로 적용한 배경색이다.
three.js 에서도 배경색을 설정할 수는 있다.
object 렌더링하기
//#region camera
const camera = new THREE.PerspectiveCamera(50, canvas.clientWidth / canvas.clientHeight, 0.1, 1000)
camera.position.set(0,0,5)
//#endregion
//#region object
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial({color: 'red'})
)
scene.add(cube)
//#endregion
//#region animate
function animate() {
requestAnimationFrame(animate);
// 박스 회전
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
//#endregion
물체를 띄우기 위해 camera와 object,
그리고 scene을 지속적으로 update해줄 animation 함수가 필요하다.
위 코드를 적용하면 이렇게 빨간 박스가 회전하는 모습을 보인다.
근데 왜인지 화질이 구리다.
해상도를 조정하도록 하자.
해상도 조정하기 resize
function resizeRendererToDisplaySize(renderer: THREE.WebGLRenderer) {
const canvas = renderer.domElement
const width = canvas.clientWidth
const height = canvas.clientHeight
const needResize = canvas.width !== width || canvas.height !== height
if (needResize) {
renderer.setSize(width, height, false)
}
return needResize
}
위 함수에서 renderer.setSize가 해상도를 조절해서 이렇게 깔끔하게 해상도가 바뀌게 해준다.
요 resizeRendererToDisplaySize 함수를 가지고 animation안에서 camera가 변경된 canvas크기에 맞추어 update해주도록 설정해주어야한다.
//#region animate
function animate() {
requestAnimationFrame(animate);
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement
camera.aspect = canvas.clientWidth / canvas.clientHeight
camera.updateProjectionMatrix()
}
// 박스 회전
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
//#endregion
animate 함수에 위 코드를 추가한다.
이렇게 기본적인 three.js 화면을
typescript로 띄우기 성공했다.