본문 바로가기
공부/etc

Three.js and TypeScript (8)

by Piva 2024. 11. 21.
  • 24. GLTF Loader : Part 1 부터 26. Raycaster 까지.

GLTF Loader

GLTF (GL Transmission Format)

  • 3D Scene과 모델들의 효율적인 전송과 로딩을 위한 파일 형식.
  • 3D 에셋의 크기 및 에셋들을 분석하는데 걸리는 시간을 최소화한다.
  • 하나의 GLTF 파일은 하나 혹은 그 이상의 Scene, Meshes, Materials, Textures, Skeletons, Cameras, 기타 등등을 포함할 수 있다.
  • 에셋은 JSON 형식(gltf) 혹은 이진 파일(glb)의 형태로 제공될 수 있다.
  • 3D 모델링 툴인 블렌더(Blender)를 통해 gltf 파일을 내보낼 수 있다.

 

구성

  • GLTFLoader로 gltf 파일을 불러온 후, 콘솔로 확인해보면 아래와 같은 각종 프로퍼티들을 확인할 수 있다.

  • scene: Three.js의 Group 객체이며, Object3D를 확장한다.
    • 객체 내부의 childeren에 gltf파일을 구성하는 각종 Mesh에 대한 정보를 알 수 있다.
Group: 복수의 물체를 묶어 하나로 취급할 수 있도록 만들어진 클래스이다.

 

 

Lensflare

  • addons에서 사용할 수 있는 객체.
  • 빛(Light)을 추적하는 lens flare 효과를 만든다.
import * as THREE from 'three';
import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js'

const light = new THREE.SpotLight(undefined, Math.PI * 1000);

const lensflare = new Lensflare();
lensflare.addElement(new LensflareElement(textureFlare0, 1000, 0));
light.add(lensflare);

 

 

  • Scene에 이미 존재하는 Light에 add 메서드를 통해 lens flare를 추가한다.

 

 

Blender

  • 3D 모델링 툴(링크).
  • Blender에서 수정한 모델을 Three.js에서 적용할 수 있다. 이걸 활용해서 Blender에서 미리 위치를 잡거나, 광원을 설정하는 것이 가능하다.
  • Three.js에서 Blender의 gltf파일을 import 시 각 속성들을 읽어 MeshStandardMaterial을 만든다.
    • 기본적으로는 MeshStandardMaterial로 변환하지만, 어떤 속성이 존재하는지에 따라 그 속성을 갖는 다른 Material로 변환한다.
    • Blender와 Three.js가 완벽하게 1대1로 매칭되진 않는다(Light 경우 제대로 export 되지 않는 듯).
    • 그러나 애니메이션이나 Mesh, 일부 Material은 어느정도 export될 수 있는 것 같다.

 

 

Raycaster

  • Scene 내에 3D 좌표를 생성하여, 이 좌표와 교차하는 물체를 감지할 수 있다.
  • 아래와 같은 것들을 감지하는 것이 가능하다.
    1. Reycaster 위치와 교차 지점까지의 거리
    2. 3D Scene에서의 교차점의 좌표
    3. 교차된 물체의 면(face)
    4. 면(faces normal)의 방향
    5. 면 위 교차지점의 UV 좌표
    6. 교차한 물체 자체에 대한 참조

 

Raycaster()

  • Raycaster를 생성하는 생성자. 아래와 같은 인자를 받을 수 있다.
  • origin: Raycast의 좌표.
  • direction: ray의 방향을 설정할 방향 벡터.
  • near / far: Raycaster의 탐지 거리를 설정한다. near 보단 크고 far보단 작은 거리 내에서 감지한다. near는 음수가 될 수 없으며, far는 near보다 작을 수 없다.

 

메서드

  • setFromCamera(): ray를 새로운 origin과 direction으로 업데이트 한다. 이 때 2개의 인자를 받는다.
    • coords: 마우스의 2D 좌표를 정규화된 좌표(Normalized Device Coordinate)로 받는다.
    • camera: ray가 비롯되는 카메라 객체를 가리킨다.
Normalized Device Coordinate(NDC): -1에서 1 사이의 값으로 표준화된 좌표계. 좌표 계산을 단순하게 하기 위해 사용한다는 듯 하다.
  • intersectObjects(): ray와 인자로 주어지는 objects들 사이의 교차 정보를 반환한다.
    • 여기서, objects로 넘긴 물체들에 대한 교차 정보’만’ 반환한다. 반대로 말하면 여기에 포함되지 않은 물체와의 교차 정보는 얻을 수 없다.

이런 식으로 교차한 물체들 정보가 나온다

 

 

Raycaster를 사용해 마우스 커서에 닿는 물체들을 탐지하는 예시는 아래와 같다.

import * as THREE from 'three';

const renderer = new THREE.WebGLRenderer({ antialias: true });
const raycaster = new THREE.Raycaster();
const pickables: THREE.Mesh[] = [];
const mousePos = new THREE.Vector2();

renderer.domElement.addEventListener('mousemove', (e) => {
  mousePos.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1);
  raycaster.setFromCamera(mousePos, camera);
	
  const intersects = raycaster.intersectObjects(pickables, false);
  if (intersects.length) {
    console.log(intersects);
  }
});
  • Raycaster 생성자로 raycaster를 생성한다.
  • mouse의 좌표를 기록할 mousePos를 선언한다.
  • 교차 여부를 관찰할 물체들이 담길 pickables 배열을 선언한다.
  • mousemove 이벤트에 대해 이벤트 리스너를 등록한다.
    • 콜백 내에는 마우스의 좌표를 실시간으로 갱신한다. 이 때, 캔버스 크기를 사용하여 정규화한 좌표로 업데이트 한다.
    • setFromCamera 를 통해 카메라와 origin(여기서는 마우스 좌표) 사이 거리를 계산하도록 한다.
    • intersectObjects를 통해, raycaster와 교차하는 물체들의 목록을 받는다.

 

활용 예제

강의에서 실습하는 예제 코드에 대해 간략하게 기록한다.

  • 아래 코드는 Raycaster를 통해, 마우스 커서와 맞닿은 물체의 한 면에 수직선을 긋는 예제이다.
    • 전체 코드가 아닌 일부만 기록한다.
import * as THREE from 'three';

const renderer = new THREE.WebGLRenderer({ antialias: true });
const raycaster = new THREE.Raycaster();
const pickables: THREE.Mesh[] = [];
const mousePos = new THREE.Vector2();

const arrowHelper = new THREE.ArrowHelper();
arrowHelper.setLength(0.5);
scene.add(arrowHelper);

renderer.domElement.addEventListener('mousemove', (e) => {
  mousePos.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1);
  raycaster.setFromCamera(mousePos, camera);
	
  const intersects = raycaster.intersectObjects(pickables, false);
  if (intersects.length) {
    const n = new THREE.Vector3();
    n.copy((intersects[0].face as THREE.Face).normal);
    n.transformDirection(intersects[0].object.matrixWorld);

    arrowHelper.setDirection(n);
    arrowHelper.position.copy(intersects[0].point);
  }
});

 

Z fighting
: 복수의 물체의 Z 좌표가 거의 비슷하거나 같아서, 깊이를 파악하기 어려워짐에 따라 물체들의 렌더링 순위가 바뀌는 것을 ‘싸움’에 빗댄 단어이다. 마치 화면이 깨지는 것처럼 깜빡이는 현상을 일컫는다.

'공부 > etc' 카테고리의 다른 글

Three.js and TypeScript (10)  (0) 2024.11.25
Three.js and TypeScript (9)  (0) 2024.11.22
Three.js and TypeScript (7)  (2) 2024.11.20
Three.js and TypeScript (6)  (0) 2024.11.19
Three.js and TypeScript (5)  (2) 2024.11.18