[그래픽스] three.js최적화, 3D에서의 lazy loading 구현

Date:     Updated:

카테고리:

태그:

🚀Introduction

오늘은 three.js에서의 lazy loading에 대해 알아볼 것이다. material, texture, geometry에 대해 잘 모르겠다면 이전글을 읽고 오는것이 좋을 것 같다


🚀최적화: 3D lazy loading

프론트엔드 개발자라면, 혹은 눈썰미가 좋은 스마트 지식인(?) 이라면, 유튜브에 들어갈 때 다음과 같은 모습을 본적이 있을 것이다.

lazy

웹사이트에 3d파일을 넣고자 three.js등을 사용했겠지만, 3d 파일 특성상, 혹은 사용자의 인터넷 환경 특성상 꽤나 오랜 시간이 소요되는 경우가 많다. google.com에 접속하는 시간보다는 훨씬 오래걸릴 것이다. 유튜브 등의 사이트에서 해주는것처럼 lazy loading을 생각해볼 수 있을 것이다. 3d 상의 lazy loading인 것이다

⭐️texture lazy loading

texturelazy loading하려면 어떻게 해야 할까? 리사이즈된 이미지를 따로 가지고 있어야 한다. 2023년에 새롭게 추가된 OffScreenCanvas와 같은 API들을 사용하면 조금더 좋은 사용성을 보여줄 수 있을 것이다. 카카오 등에서도 해당 api를 사용하고 있는것으로 보아, 실 서비스에도 충분히 적용가능한 안정적인 API로 보인다.

하지만 조금 더 생각해보면, lazy loading을 구현한다는 것은 무언가를 서버에서 불러올 때 일단 빠르게 보여주기 위해서 이다. 그렇다면 서버에서 불러올 무언가를 클라이언트에서 처리할 필요가 있을까? 서버에서 리사이즈를 하고 저장하는게 더 편할것 같다. 클라이언트에서 원본texture이미지를 리사이즈 시키고, 원본texture이미지와 리사이즈된 texture이미지를 같이보내주는 것 보다는… 원본 texture를 보내주고, 서버에서 해당 texture를 리사이즈해서 원본과 리사이즈된 이미지를 저장하는 로직이 훨씬 간단할 것 같다. 다음 그림처럼 될 것이다. 서버를 담당하는사람과 특별히 사이가 안좋은게 아니라면, lazy loading을 위해서는 2번 아키텍쳐가 더 좋은 방법이라고 생각한다.

lazy-texture

sketchfab과 같은 유명한 (3d에셋을 판매하는)사이트들 등에서는 다음처럼 lazy loading을 적용해둔걸 볼 수 있다.


⭐️geometry lazy loading

geometry를 경량화 시키기위해서는 SimplifyModifier를 사용하면 된다. Three.js에서 제공하는 예제도 있으며, 코드도 간단하다. 혹시 Three.js를 사용하지 않고 단순 geomtery만 줄이고 싶다면, Three.js내부 코드를 단순화 시켜둔 코드를 깃헙에서 확인해볼 수 있다.

const geometry = new THREE.SphereGeometry(1, 32, 32); // 줄이고자 하는 geometry
const simplifyModifier = new THREE.SimplifyModifier();
const simplifiedGeometry = simplifyModifier.modify(geometry, Math.floor(geometry.vertices.length * 0.1));

하지만 조금 더 성능이 좋은 국산! 네이버의 mesh-simplify도 있다. 다음은 예시코드이다.

    import { FastQuadric, ThreeGeometry } from 'mesh-simplifier';

    const geometry = new THREE.SphereGeometry(1, 32, 32);  // 줄이고자 하는 geometry
    const simplifyModifier = new FastQuadric({targetPercentage: 0.1});
    const simplifiedGeometry = simplifyModifier.simplify(geometry);

둘중 어떤 알고리즘을 써야할까?


⭐️mesh simplifier( 네이버 vs three.js )

결론부터 말하자면, 네이버에서 만든 mesh-simplify를 사용하는것이 더 좋다. 이는 이미 존재하는, 1997년에 제안된 Fast-Quadric알고리즘을 사용했다. 그리고 three.js는… 무슨 알고리즘을 사용하는지 찾을 수 없었다!! 하지만 소스코드를 파보니 적혀있었다. 1998년에 제안된 Progressive Mesh알고리즘을 사용하고 있다.

three-simplifier

그렇다면 두 알고리즘 중 어떤 알고리즘을 사용하는게 맞을까? 스탠포드 simplification강의자료와 QEM, vertexClustering등에 대해 잠시 공부해봤지만… 결론을 내릴 수 없었다! 그냥 도찐개찐인것 같았다. 하나의 학문 수준으로 들어갈 정도로 방대한 지식이 필요했기에, 실제로 사용해보기로 했다.

사용결과 너무나 명확하게 fast-quadric 알고리즘이 더 좋았다. three에서 기본제공하는 progressive mesh보다 mesh가 덜 손상되는것 처럼 보였다. 얼굴 등 섬세한 곡선등이 있는 부분에서 너무나 쉽게 손상되는 모습을 보였다. 구멍이 뚫리기도 하고, 부자연스럽게 geometry가 변하기도 했다. 다양한 에셋으로 테스트해봤지만… 네이버 승!
개미굴로 빠져서 상당시간 공부하는것보다, 직접 빠르게 사용해보는게 명확하고 직관적인 결과를 주는 것 같기도 하다.

(three에서 제공하는 함수를 이용해서 너무 부자연스럽게 simplify된 geometry) damaged-hulk


⭐️3D lazy loading 최종 결과물

그렇다면 한번 최종 결과물을 봐보도록 하자. 이전글에서 GLTF와 GLB의 형식에 대해서 자세히 다루었는데, 이처럼 GLB형식은 texture를 참조하고 있을 뿐이니, 해당하는 texture를 갈아껴주는 방식으로 생각보다 쉽게 구현할 수 있다. 구현난이도에 비해, 유저 경험이 훨씬 좋아지게 될 것 같다. texture를 단순 참조만 하고있을 뿐이니, 원본 데이터가 오기 전 3D에셋에 다양한 조작을 하여도, 단순 이미지정보가 바뀐것이기 때문에 크게 우려할만한 일들이 일어나지는 않는다.


🚀마무리 정리

이전글에서는 3D에셋데이터의 형식에 대해 알아보았고, 오늘은 이 형식의 참조값을 두번 불러오는 방식으로 웹에서의 사용자경험을 올리는 방법을 알아 보았다.(일단 경량화된 값을 빠르게 가져오고, 원본값을 한번 더) 사실 2D에서와 완전히 동일한 개념이다.

BIM데이터(건물관련) 이라면, geometry가 단순하고 texture부분에서 lazy loading을 먼저 고려해볼수도 있을 것이다. 하지만 geometry의 크기가 texture보다 큰경우도 충분히 많이 존재한다. 이런경우 본인이 필요한 부분들에 대해 생각을 해봐야 할 것이다. three.js에서 geometry정보를 단순화할때는 Fast-Quadric 알고리즘을 사용하는것이 더 좋으며, 클라에서 texture정보를 단순화할때는 OffScreenCanvas가 좋은 선택이 될 수 있다는 사실을 알게 되었다.

3D lazy loading, 이해하고 나면, 크게 어려운 개념은 아닐 것이다. 오히려 3D데이터가 어떻게 구성되어있는지에 대해 익숙해지는 부분이 더 중요하지 않을까 싶다.


맨 위로 이동하기

graphics 카테고리 내 다른 글 보러가기

댓글 남기기