본문 바로가기
Ect./Library

Three.js 라이브러리 배워보기

by 쿠리의일상 2023. 6. 12.

https://threejs.org/

Three.js 는

웹 페이지에 3D 객체를 쉽게 렌더링할 수 있도록 도와주는 자바스크립트 3D 라이브러리

WebGL 기술을 기반으로 렌더링과 카메라, 조명 등의 3D프로그래밍 기술을 간단하게 사용할 수 있도록 해준다.

 

설치하기

npm 을 통해 설치 (CDN도 있긴 하지만 권장은 npm)

https://threejs.org/docs/index.html#manual/en/introduction/Installation

npm install three
npm install -D vite

vite 는 실행도구이다.

vite
빠르고 간결한 개발 경험에 초점을 맞춘 번들러
Webpack 과 동일한 기능을 하지만 공식문서에서 Vite 를 권장하길래 사용해보았다.
기본적으로 ES Modules 를 사용하며 뷰를 위해 만들어졌으나 다른 프레임웤에도 지원하기 시작함.
패키지와 소스 코드를 분리하여 빌드하는 특징이 있음. (패키지는 설치 후 내용이 바뀌지 않지만 소스 코드는 빈번히 바뀐다)

npx vite 로 매번 확인해주면 된다.

HTML

script 태그에 type 을 module 로 주자.

<script type="module" src="./app.js"></script>

그다음, Three.js 라이브러리를 사용하기 위한 three 모듈을 불러와준다.

  import * as THREE from 'three';

 

Three.js 은 크게 Scene, Camera, Renderer 로 나뉜다.

Scene

렌더링할 객체와 광원을 저장하는 공간

scene 이 없으면 어떤 객체든 표시할 수 없으므로 필수적

three 모듈의 Scene() 메서드를 사용하여 Scene 객체를 생성해준다.

let scene = new THREE.Scene();

 

scene.background = new THREE.Color('#ccc');

scene 의 배경색은 위처럼 THREE.Color() 로 지정해줄 수 있다.

 

Camera

Scene 객체를 어떻게 촬영하여 보여줄 것인가 결정

  • 원근법을 무시하는 OrthographicCamera 
  • 원근법을 적용하는 PerspectiveCamera
let camera = new THREE.PerspectiveCamera(
	45, // field of view (시야각)
    window.innerWidth / window.innerHeight, // aspect (비율)
    0.1, // near
    1000, // far
);

카메라의 설정 파라미터 중

  1. 시야각은 해당 시점이 화면에 보일 정도를 의미
  2. 비율은 웹 브라우저로 보기 때문에 화면의 너비를 높이로 나눈 값을 전달해준다.
  3. near 는 해당 수치보다 가까운 경우 보이지 않게 설정
  4. far 은 해당 수치보다 먼 경우 보이지 않게 설정

프로그램 성능 향상을 위해 near 와 far 를 조정해준다.

 

Renderer

Scene 과 Camera 객체를 넘겨받아 카메라의 절두체 안 3D 씬의 일부를 평면(2D)으로 렌더링

const renderer = new THREE.WebGLRenderer({
  canvas: document.getElementById('canvas'), // canvas 가 있는 경우
  antialias: true, // 안티앨리어싱 처리
});

렌더러 인스턴스는 WebGLRenderer() 메서드로 불러올 수 있으며,

 

렌더러 인스턴스를 생성하면서 앱을 렌더링할 크기를 설정해줘야 한다.

앱으로 채우려는 영역의 너비와 높이를 사용(브라우저의 너비와 높이)

renderer.setSize( window.innerWidth, window.innerHeight );

// canvas 가 없는 경우 넣어주면 canvas 가 추가됨
document.body.appendChild( renderer.domElement );

setSize() 메서드로 브라우저 크기에 맞추고 body 의 자식돔으로 넣어준다.

여기서 setSize() 메서드의 3번째 인자에 false 를 넣으면 앱 크기를 유지하면서 더 낮은 해당도로 렌더링이 가능하다.

 

그다음 큐브 모양을 직접 만들어보자

BoxGeometry

큐브의 모든 정점과 채우기를 포함한 객체로, 

const geometry = new THREE.BoxGeometry( width, height, depth );

큐브의 너비 높이 깊이의 크기를 지정해줄 수 있다.

const material = new THREE.MeshBasicMaterial( { color: 0xcccccc } );

MeshBasicMaterial 로 재질을 적용한 속성을 줄 수 있다.

다음은 Mesh 정보로 3D 화면을 구성하는 물체이다.

geometry 와 material 정보를 넣어서 적용해줄 수 있다.

geometry : 기하하 객체의 정점 데이터
material : 기하학 객체를 그리는데 사용하는 표면 속성 (색/밝기 등)
const cube = new THREE.Mesh( geometry, material );

만들어준 큐브는 scene 에 add 해줘야 볼 수 있다.

scene.add( cube );

여기서 카메라 이동이 따로 없을 경우 같은 위치에 있어서 볼 수 없다.

camera.position.set(0, 0, 5);

카메라의 position은 set해준다. x, y, z 순서이며 카메라 위치를 지정해줄 수있음!

 

아직 끝이 아님.. 이제 장면 렌더링 과정이 필요하다.

render / animate loop 

function animate() {
	requestAnimationFrame( animate );
    renderer.render( scene, camera );
}

animate();

requestAnimationFrame() : 화면이 새로 고쳐질 때마다 렌더러가 장면을 그리는 루프가 생성, 다른 브라우저 탭으로 이동할 때 일시 중지되므로 연산 비용과 배터리를 낭비하지 않을 수 있다!

renderer.render() 로 씬과 카메라를 넣어주면 쨘. 아래처럼 보인다.

정육면체지만 애니메이션이 없기에 가만히 있음

큐브 애니메이션

	cube.rotation.x += 0.01;
	cube.rotation.y += 0.01;

모든 프레임으로 실행되며 큐브에 회전 애니메이션을 제공해준다.

가장 기초적인 큐브는 불러와봤으니, 이젠..

3D 모델 불러와보기

여기 사이트에서 3D Model 을 gltf 로 구해와준다.

https://sketchfab.com/models?features=downloadable&sort_by=-likeCount&type=models 

 

Explore 3D Models

Find all kinds of 3D models. From low poly assets to animated rigs & digital scans. All for your 3D, Virtual Reality, and Augmented Reality projects.

sketchfab.com

Mesh 생성자를 활용하여 Scene 에 객체를 추가해줄 수 있다. Mesh 생성자는 위에서 봤듯이 geometry 와 material 정보를 전달 받는다.

또는 Loader 객체를 통해 3D 모델을 생성할 수도 있다.

 

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

기본적으로 GLTFLoader 객체를 임포팅 할 수 있으며, 해당 객체의 load() 메서드로 미리 준비된 3D 모델을 불러올 수 있다.

gltf 란?
3차원 장면과 모델을 표현하는 파일 포멧 중 하나.
메인이 되는 JSON 형식의 파일, 형태 동작 등 버퍼 데이터가 저장되는 바이너리 파일(bin) 그리고 모델마다 상이하지만 다수의 텍스처 이미지로 구성된다.

 

let loader = new GLTFLoader();
loader.load('./sources/models/scene.gltf', (gltf) => {
  scene.add(gltf.scene);
})

위처럼 GLTFLoader() 를 통해 만들어준 로더가 load 되면 실행해줄 콜백 함수 안에 scene 에 add 해준다.

 

광원(조명)

물체를 보기 위해선 조명을 추가해줘야 한다. (유니티에서.. 많이 본 것들)

color 는 광원의 색

intensity 는 빛의 강도

  • AmbientLight : 모든 면을 골고루 비춰주는 빛
let light = new THREE.AmbientLight(color, intensity);

// OR

let light = new THREE.AmbientLight();
light.color = 0xffff00;
light.intensity = 5;
  • HemisphereLight : 땅과 하늘, 두 곳의 광원을 가지는 빛
let light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
  • DirectionalLight : 태양과 같이 무한대의 먼 거리에서 모든 물체에 같은 각도로 비추는 빛
let light = new THREE.DirectionalLight(color, intensity);
  • PointLight : 전구같이 한 지점에서 모든 방향으로 방출되는 빛
let light = new THREE.PointLight(color, intensity, distance, decay);

distance 는 빛이 방출되는 거리

decay 를 통해 빛이 거리에 따라 얼마나 어두워지는지 결정

  • SpotLight : 한 지점에서 단방향 원뿔형으로 방출되는 빛
let light = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);

angle 은 빛이 퍼지는 각도로 PI / 2 가 최댓값이다.

penumbra 는 빛의 가장자리의 선명도 설정, 커질수록 가장자리가 흐릿해진다.

  • RectAreaLight : 사각 평면에서 균일하게 방출되는 빛
let light = new THREE.RectAreaLight(color, intensity, width, height);

width와 height 는 광원의 가로/세로 크기를 의미(기본값은 10)

let light = new THREE.DirectionalLight(0xffffff,10);
scene.add(light);

나는 태양광으로 사용해줬다.

애니메이션

자바스크립트에서 애니메이션을 구현하는 방식에는

  • setInterval
  • requestAnimationFrame

으로 두가지가 있다고 한다. 하지만 알다시피 setInterval 은 애니메이션을 위한 메서드가 아니고 쓸데없는 콜스택이 많이 발생하여 딜레이가 생길 가능성이 크므로 requestAnimationFrame 을 사용해준다!

반복할 인자를 매개변수로 받아주어 해당 인자를 1초에 최대 60번까지 실행하여 호출 스택이 과도하게 커지는 것을 방지하고

딜레이 없이 부드러운 애니메이션을 확인할 수 있다.

그리고 위에 기재했듯이 requestAnimationFrame 메서드를 페이지가 비활성 상태라면 렌더링을 중지하므로 CPU 리소스와 배터리를 낭비하지 않아 성능 향상에 도움이된다고 한다.

requestAnimationFrame 메서드는 재귀적으로 호출시켜주는 방식으로 사용해준다.

 

GLTFLoader 쪽에 애니메이션 추가해줬다. (gltf.scene 에 애니메이션을 추가해줘서 사용한다)

  function animate(){
    requestAnimationFrame(animate);

    gltf.scene.rotation.y += 0.010;
    renderer.render(scene,camera);  
  }
  animate();

 

일단은 여기까지! 귀여운 시바를 빙글빙글 돌렸다.

다음에는 클릭해서 여러 각도로 볼 수 있게끔(카메라 조정) 공부해볼 요량이다.