Three.js
는 웹페이지에 3D 객체를 쉽게 랜더링할 수 있도록 도와주는 Javascript 3D 라이브러리이다. WebGL
기술을 기반으로 랜더링과 카메라, 조명 등의 3D 프로그래밍 기술을 간단하게 사용할 수 있도록 한다.
WebGL
Web Graphics Library의 약자로, 웹 상에서 2D 및 3D 그래픽을 사용할 수 있도록 한다.OepnGL ES 2.0
을 기반으로 하고,HTML5
의<canvas>
요소를 사용한다.
하지만WebGL
만을 사용하여 3D 요소를 구현하려면 상당히 복잡한 코드를 작성해야 하는데,Three.js
는 이런 3D 요소의 처리를 도와 보다 직관적인 코드를 작성하도록 도와준다.
Three.js
는 npm
을 통해 설치하거나, CDN
서비스를 통해 사용이 가능하다. 해당 포스팅에서는 CDN
을 활용했지만, Three.js
공식 문서에서는 npm을 통한 설치를 권장하고 있다.
만약 three npm module
을 설치하고자 한다면 사용하고자 하는 프로젝트 폴더의 명령 프롬프트를 열고 다음과 같이 입력한다.
npm install three
만약 node.js
환경에서 프로젝트를 진행한다면, 터미널 창에 위의 명령어를 입력할 수 있다.
WebGL
은 HTML의 Canvas
요소를 사용한다. 그러므로 canvas
태그 호출이 필수적이다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Three.js</title>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
다음으로 Three.js
라이브러리의 기능을 사용하기 위해 three
모듈을 불러온다.
// CDN import
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.138.3/build/three.module.js",
"GLTFLoader":
"https://unpkg.com/three@0.141.0/examples/jsm/loaders/GLTFLoader.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
</script>
이 때, script의 type 속성에 "module"
키워드를 적용하여 해당 스크립트가 모듈이란 것을 브라우저가 알 수 있도록 해야 한다.
Three.js
는 크게 Scene
, Camera
그리고 Renderer
로 나뉜다.
그 중 Scene
은 랜더링할 모든 객체와 광원을 저장하는 공간이다. Scene
이 없으면 어떠한 객체도 표시할 수 없기 때문에 필수적으로 생성해주어야 한다.
three
모듈의 Scene()
메서드를 활용하여 새로운 Scene
객체를 생성해주자.
let scene = new THREE.Scene();
이 때, THREE.Color()
를 통해 Scene
의 배경색을 설정할 수 있다.
scene.background = new THREE.Color("skyblue");
Camera
는 Scene
객체를 어떻게 촬영하여 보여줄 것인가를 결정한다.
카메라에는 원근법을 무시하는 OrthographicCamera
와, 원근법을 적용하여 일상생활에서 우리가 보는 것처럼 장면을 보여주는 PerspectiveCamera
가 있다. 여기서는 PerspectiveCamera
를 사용하도록 하겠다.
let camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
)
// 카메라의 위치 설정 (x: 0, y: 0, z: 5)
camera.position.set(0, 0, 5);
매개변수가 의미하는 것은 다음과 같다.
field of view
: 시야각으로, 해당 시점의 화면이 보일 정도를 의미한다.aspect
: 비율을 의미한다. 우리는 웹 브라우저를 통해 사물을 바라보기 때문에 보통 화면의 width를 height로 나눈 값을 전달한다.near
: 해당 수치보다 가까이 있는 물체는 보이지 않는다.far
: 해당 수치보다 멀리 있는 물체는 보이지 않는다.near
과 far
는 프로그램의 성능 향상을 위해 사용할 수 있다.
Three.js
에서는 Mesh
생성자를 활용하여 Scene
에 객체를 추가해줄 수 있다. Mesh
생성자는 매개변수로 geometry
와 material
정보를 전달받는다.
하지만 여기서는 Loader
객체를 통해 3D 모델을 사용해볼 것이다.
먼저 CDN으로 받아온 GLTFLoader
모듈에서 GLTFLoader
객체를 임포팅한다. 해당 객체를 loader
변수에 저장하고, 내장 메서드인 load()
를 활용해서 미리 준비한 3D 모델을 불러온다. 이 때, 모델을 불러오는 데에 성공했다면, 이를 Scene
에 추가할 수 있도록 콜백 함수를 활용하여 scene.add()
메서드를 실행한다. 해당 콜백 함수의 첫번째 인자는 항상 불러온 3D 모델을 가리킨다.
import { GLTFLoader } from "GLTFLoader";
let loader = new GLTFLoader();
loader.load("duck/scene.gltf", function (gltf) {
scene.add(gltf.scene);
});
웹 3D Viewer 사이트 Sketchfab에서 다양한 3D 모델을 무료로 제공하고 있다. 해당 프로젝트에서는 귀여운 오리를 GLTF 파일로 내려 받아 사용해보았다.
우리가 물체를 볼 때 빛이 있어야 하는 것처럼, Three.js
에서도 물체를 보기 위해서 조명을 추가해주어야 한다.
Three.js
에서는 다양한 종류의 조명을 지원한다.
AmbientLight
는 광원, 즉 빛의 시작점이 없이, 물체의 모든 면을 골고루 비춰주는 빛이다. 매개 변수로 빛의 색상인 color
와 빛의 강도를 의미하는 intensity
를 받는다. 선언한 뒤 색상과 강도를 변경하고 싶을 경우 아래와 같이 사용할 수 있다.
// THREE.AmbientLight(color, intensity)
let light = new THREE.AmbientLight();
light.color = 0xffff00;
light.intensity = 5;
하늘과 바닥 두 곳의 광원을 가지는 빛이다.
// THREE.HemisphereLight(skyColor, groundColor, intensity);
let light = new THREE.HemisphereLight(0xffff00, 0xff0000, 0.3);
태양과 같이 무한대의 먼 거리에서 모든 물체에 같은 각도로 비추는 빛이다.
// THREE.DirectionalLight(color, intensity);
let light = new THREE.DirectionalLight(0xffff00, 0.7);
전구처럼 한 지점에서 모든 방향으로 방출하는 빛을 제공한다. 매개변수로 distance
를 넘겨 받아 빛이 방출되는 거리를 지정할 수 있고, decay
를 통해 빛이 거리에 따라 얼마나 어두워지는지를 결정할 수 있다. 기본값은 각각 0과 1이다.
// Three.PointLight(color, intensity, distance, decay);
let light = new THREE.PointLight(0xffffff, 0.5, 15, 3);
한 지점에서 한 방향으로 원뿔형으로 방출하는 빛을 의미한다. 다른 종류의 빛과는 다르게 매개변수로 angle
, penumbra
,를 추가로 받는다. angle
은 빛이 퍼지는 각도를 의미하며 최댓값은 PI / 2
이다. penumbra
는 빛의 가장자리의 선명도를 결정할 수 있다. 기본값은 0이고, 숫자가 커질수록 가장자리가 흐릿해진다.
// THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
let light = new THREE.SpotLight(0x00ff00, 1, 30, Math.PI * 0.2, 0.1, 1);
사각 평면에서 균일하게 방출되는 빛이다. 매개변수로 받는 width
와 height
는 각각 광원의 가로 크기, 세로 크기를 의미하며, 기본값은 둘 다 10이다.
// THREE.RectAreaLight(color, intensity, width, height);
let light = new THREE.RectAreaLight(0xffff00, 0.5, 20, 20);
해당 프로젝트에서는 AmbientLight
와 PointLight
, 두 개의 빛을 사용할 것이다. Light
객체를 생성한 뒤, PointLight
의 광원의 위치를 light.position.set
메서드를 활용하여 옮겨주도록 하자. 그런 다음 Scene
에 조명을 추가하면 준비는 모두 끝이 난다.
let PLight = new THREE.PointLight();
let ALight = new THREE.AmbientLight();
PLight.position.set(50, 50, 50);
scene.add(PLight, ALight);
Renderer
는 Three.js
의 핵심 객체라고 할 수 있다. Scene
과 Camera
객체를 넘겨 받아 랜더링한다.
먼저 WebGLRenderer
를 통해 renderer
객체를 생성해준다. 이 때, 생성자 함수는 랜더링한 것들을 보여줄 Canvas
태그를 매개변수로 받는다. 그리고 3D 모델의 테두리를 부드럽게 하기 위해 antialias: true
를 추가해준다.
let renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("#canvas"),
antialias: true,
});
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setSize(window.innerWidth, window.innerHeight);
보다 정확한 색상을 랜더링하기 위해 outputEncoding
값을 설정해주었고, setSize
메서드를 활용하여 renderer
의 크기를 조절해주었다.
이제 loader
객체의 콜백 함수 내에서 renderer
의 render
메서드를 통해 랜더링해준다.
loader.load("duck/scene.gltf", function (gltf) {
scene.add(gltf.scene);
renderer.render(scene, camera); // 랜더링
});
결과는 다음과 같다.
--
🙇🏻♂️ https://horangi.tistory.com/406
🙇🏻♂️ https://velog.io/@chloeee/3-Three.js%EB%9E%80
🙇🏻♂️ https://cyberx.tistory.com/177