[Three.js] 3D 그래픽 입문해보기

seongminn·2022년 6월 9일
22

JavaScript

목록 보기
2/5
post-thumbnail

📌 Three.js

Three.js는 웹페이지에 3D 객체를 쉽게 랜더링할 수 있도록 도와주는 Javascript 3D 라이브러리이다. WebGL 기술을 기반으로 랜더링과 카메라, 조명 등의 3D 프로그래밍 기술을 간단하게 사용할 수 있도록 한다.

WebGL

Web Graphics Library의 약자로, 웹 상에서 2D 및 3D 그래픽을 사용할 수 있도록 한다. OepnGL ES 2.0을 기반으로 하고, HTML5<canvas> 요소를 사용한다.

하지만 WebGL만을 사용하여 3D 요소를 구현하려면 상당히 복잡한 코드를 작성해야 하는데, Three.js는 이런 3D 요소의 처리를 도와 보다 직관적인 코드를 작성하도록 도와준다.


준비 단계

Installing

Three.jsnpm을 통해 설치하거나, CDN 서비스를 통해 사용이 가능하다. 해당 포스팅에서는 CDN을 활용했지만, Three.js 공식 문서에서는 npm을 통한 설치를 권장하고 있다.

만약 three npm module을 설치하고자 한다면 사용하고자 하는 프로젝트 폴더의 명령 프롬프트를 열고 다음과 같이 입력한다.

npm install three

만약 node.js 환경에서 프로젝트를 진행한다면, 터미널 창에 위의 명령어를 입력할 수 있다.

HTML

WebGL은 HTML의 Canvas 요소를 사용한다. 그러므로 canvas 태그 호출이 필수적이다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Three.js</title>
  </head>
  <body>
    <canvas id="canvas"></canvas>
  </body>
</html>

Importing

다음으로 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" 키워드를 적용하여 해당 스크립트가 모듈이란 것을 브라우저가 알 수 있도록 해야 한다.

시작 !

Scene 생성

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 생성

CameraScene 객체를 어떻게 촬영하여 보여줄 것인가를 결정한다.

카메라에는 원근법을 무시하는 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);

매개변수가 의미하는 것은 다음과 같다.

  1. field of view : 시야각으로, 해당 시점의 화면이 보일 정도를 의미한다.
  2. aspect : 비율을 의미한다. 우리는 웹 브라우저를 통해 사물을 바라보기 때문에 보통 화면의 width를 height로 나눈 값을 전달한다.
  3. near : 해당 수치보다 가까이 있는 물체는 보이지 않는다.
  4. far : 해당 수치보다 멀리 있는 물체는 보이지 않는다.

nearfar는 프로그램의 성능 향상을 위해 사용할 수 있다.

3D 모델 불러오기

Three.js에서는 Mesh 생성자를 활용하여 Scene에 객체를 추가해줄 수 있다. Mesh 생성자는 매개변수로 geometrymaterial 정보를 전달받는다.

하지만 여기서는 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 파일로 내려 받아 사용해보았다.

Light 추가

우리가 물체를 볼 때 빛이 있어야 하는 것처럼, Three.js에서도 물체를 보기 위해서 조명을 추가해주어야 한다.

Three.js에서는 다양한 종류의 조명을 지원한다.

AmbientLight

AmbientLight는 광원, 즉 빛의 시작점이 없이, 물체의 모든 면을 골고루 비춰주는 빛이다. 매개 변수로 빛의 색상인 color와 빛의 강도를 의미하는 intensity를 받는다. 선언한 뒤 색상과 강도를 변경하고 싶을 경우 아래와 같이 사용할 수 있다.

// THREE.AmbientLight(color, intensity)

let light = new THREE.AmbientLight();

light.color = 0xffff00;
light.intensity = 5;

HemisphereLight

하늘과 바닥 두 곳의 광원을 가지는 빛이다.

// THREE.HemisphereLight(skyColor, groundColor, intensity);

let light = new THREE.HemisphereLight(0xffff00, 0xff0000, 0.3);

DirectionalLight

태양과 같이 무한대의 먼 거리에서 모든 물체에 같은 각도로 비추는 빛이다.

// THREE.DirectionalLight(color, intensity);

let light = new THREE.DirectionalLight(0xffff00, 0.7);

PointLight

전구처럼 한 지점에서 모든 방향으로 방출하는 빛을 제공한다. 매개변수로 distance를 넘겨 받아 빛이 방출되는 거리를 지정할 수 있고, decay를 통해 빛이 거리에 따라 얼마나 어두워지는지를 결정할 수 있다. 기본값은 각각 0과 1이다.

// Three.PointLight(color, intensity, distance, decay);

let light = new THREE.PointLight(0xffffff, 0.5, 15, 3);

SpotLight

한 지점에서 한 방향으로 원뿔형으로 방출하는 빛을 의미한다. 다른 종류의 빛과는 다르게 매개변수로 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);

RectAreaLight

사각 평면에서 균일하게 방출되는 빛이다. 매개변수로 받는 widthheight는 각각 광원의 가로 크기, 세로 크기를 의미하며, 기본값은 둘 다 10이다.

// THREE.RectAreaLight(color, intensity, width, height);

let light = new THREE.RectAreaLight(0xffff00, 0.5, 20, 20);

해당 프로젝트에서는 AmbientLightPointLight, 두 개의 빛을 사용할 것이다. 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);

랜더링

RendererThree.js의 핵심 객체라고 할 수 있다. SceneCamera 객체를 넘겨 받아 랜더링한다.

먼저 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 객체의 콜백 함수 내에서 rendererrender 메서드를 통해 랜더링해준다.

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

profile
돌멩이도 개발 할 수 있다

0개의 댓글