PerfectVector
작성: Irene Kim7분 읽기

Three.js SVGLoader: 평면 아트워크를 3D 메시로 만들기

작고 재현 가능한 브라우저 예제로 SVG 경로를 Three.js에 불러오고 실제 구멍을 유지하며 세로 방향을 수정하고 돌출 메시 크기를 설정하세요.

목차

평면 SVG 아트워크를 Three.js 메시로 만들려면 SVG를 불러오고 경로를 도형으로 변환한 뒤 그 도형을 ExtrudeGeometry에 전달하세요. 효과를 추가하기 전에 구멍, 세로 방향, 치수를 확인하세요. 알아볼 수 있는 실루엣이어도 내부 공간이 채워지거나 모서리가 뒤집힐 수 있습니다.

이 설명은 사각형 구멍 하나와 잘린 오른쪽 위 모서리가 있는, 직접 작성한 배지를 사용합니다. 비대칭 덕분에 방향 오류가 드러납니다. 브라우저 기하학 예제이며 PerfectVector 변환 결과나 제조 테스트가 아닙니다.

깊이를 줄 도형으로 시작하기

처음에는 닫히고 채워진 윤곽이 있는 작은 SVG를 사용하세요. badge.svg로 저장하세요.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 180">
  <path fill="#e64b35" fill-rule="evenodd"
    d="M20 20H160L220 80V160H20Z M65 60H115V110H65Z"/>
</svg>

하나의 path 요소 안에 바깥 경계와 안쪽 사각형이라는 두 닫힌 하위 경로가 있습니다. evenodd 채우기 규칙이 안쪽 영역을 구멍으로 만듭니다. 별도의 흰색 직사각형은 평면 그림에서 표면을 덮을 뿐 같은 빈 영역을 설명하지 않습니다.

아트워크는 240 × 180 viewBox 안에서 좌표 단위 200 × 140을 차지합니다. 메시 크기를 설정할 때 이 차이를 기억하세요. 빈 여백은 SVG 뷰포트의 일부이며 돌출 실루엣의 일부가 아닙니다.

편집 가능한 벡터 원본이 있다면 사용하세요. PNG나 JPG가 유일한 원본이라면 PerfectVector의 이미지 벡터화 작업 흐름으로 SVG 경로 아트워크를 준비할 수 있습니다. Three.js에 불러오기 전에 미리보기와 다운로드한 윤곽을 검사하고, 특히 도형 안이나 사이의 빈 공간을 확인하세요. 벡터화는 2D 아트워크를 제공하며 아래 코드는 깊이, 재질, 조명, 배치를 제공합니다.

SVG 불러오기와 메시 렌더링하기

예제는 Three.js 0.186.0으로 테스트했습니다. 핵심 라이브러리와 애드온을 같은 버전으로 유지하세요. 패키지 가져오기를 사용하는 브라우저 프로젝트에서 three@0.186.0을 설치하고 HTTP로 파일을 제공하며 컨테이너를 만드세요.

<div id="viewer"></div>
<script type="module" src="./main.js"></script>

빌드 도구가 three 가져오기를 해석해야 합니다. 빌드 도구 없는 일반 브라우저 페이지에는 같은 설치 패키지의 파일을 사용해 모듈 스크립트 전에 이 가져오기 맵을 넣으세요.

<script type="importmap">
{
  "imports": {
    "three": "./node_modules/three/build/three.module.js",
    "three/addons/": "./node_modules/three/examples/jsm/"
  }
}
</script>

HTML 페이지 옆에 badge.svg를 배치하세요. 아래의 상대 로딩 URL은 페이지 URL을 기준으로 해석됩니다. 그런 다음 다음을 main.js로 저장하세요.

import * as THREE from 'three';
import { SVGLoader } from 'three/addons/loaders/SVGLoader.js';
 
const data = await new SVGLoader().loadAsync('./badge.svg');
const artwork = new THREE.Group();
for (const path of data.paths) {
  if (path.userData.style.fill === 'none') continue;
  for (const shape of path.toShapes()) {
    const geometry = new THREE.ExtrudeGeometry(shape, {
      depth: 20, bevelEnabled: false, steps: 1
    });
    const material = new THREE.MeshStandardMaterial({
      color: path.color, roughness: 0.65, metalness: 0
    });
    artwork.add(new THREE.Mesh(geometry, material));
  }
}
if (!artwork.children.length) throw new Error('No filled shapes found');
 
// SVG y grows down; this scene uses y up.
artwork.scale.y = -1;
const bounds = new THREE.Box3().setFromObject(artwork);
const size = bounds.getSize(new THREE.Vector3());
const center = bounds.getCenter(new THREE.Vector3());
const scale = 4 / size.x;
artwork.scale.multiplyScalar(scale);
artwork.position.copy(center).multiplyScalar(-scale);
 
const scene = new THREE.Scene();
scene.background = new THREE.Color('#f1f3f5');
scene.add(artwork);
scene.add(new THREE.HemisphereLight(0xffffff, 0x596477, 2));
const light = new THREE.DirectionalLight(0xffffff, 3);
light.position.set(-3, 5, 7);
scene.add(light);
const camera = new THREE.PerspectiveCamera(35, 800 / 600, 0.1, 100);
camera.position.set(4, 3, 9);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(800, 600);
document.querySelector('#viewer').append(renderer.domElement);
renderer.render(scene, camera);

SVGLoader는 별도로 가져오는 애드온입니다. loadAsync()는 파일을 읽습니다. SVG 마크업을 이미 문자열로 가지고 있다면 parse(svgText)를 대신 사용하세요.

r186에서는 path.toShapes()를 사용하세요. 예전 예제는 종종 SVGLoader.createShapes(path)를 호출합니다. 그 헬퍼는 r185에서 사용 중단 예정으로 지정되었으며 테스트 버전에서는 toShapes()에 처리를 위임합니다. 다른 튜토리얼의 코드 조각을 섞기 전에 설치한 릴리스를 확인하세요.

반복문은 의도적으로 fill="none"을 건너뜁니다. 이 예제의 단색 채우기를 처리하며 완전한 SVG 렌더러는 아닙니다. 그레이디언트, 마스크, 삽입 이미지, 스트로크, 불투명도, 겹치는 색상 레이어에는 평면 외관이 동일한 3D 기하학적 형태가 된다는 가정 대신 각각의 결정이 필요합니다.

사각형 구멍과 잘린 오른쪽 위 모서리가 있는 원본 SVG 배지와 Three.js 돌출 메시를 비교한 실제 브라우저 캡처
Three.js r186에서 직접 작성한 예제 파일의 실제 WebGL 렌더링입니다. 구멍은 비어 있고 잘린 모서리는 오른쪽 위에 유지되며 균일한 확대·축소로 4 × 2.8 × 0.4 메시가 만들어집니다.

측정과 중앙 배치 전에 방향 설정하기

SVG의 세로 좌표는 아래쪽으로 증가합니다. 이 장면은 양의 y가 위쪽인 상태로 아트워크를 바라보므로 artwork.scale.y = -1이 예상한 방향을 복원합니다. 잘린 모서리로 확인하세요. 오른쪽 위에 있어야 합니다.

이 부호 변경은 이 좌표 설정에 해당합니다. 카메라나 부모 객체를 회전한 뒤 메시가 여전히 잘못 보인다고 두 번째 뒤집기를 추가하지 마세요. 먼저 원본의 알려진 비대칭 특징을 렌더링한 장면과 비교하세요.

다음 줄들은 뒤집은 그룹을 측정하고 균일 배율을 계산하며 확대·축소된 중심을 원점으로 이동합니다. Box3.setFromObject()는 자식 객체와 변환을 포함한 경계를 측정합니다. 여기서는 측정할 때 그룹에 변환된 부모가 없고 아트워크 회전도 없습니다.

원래 돌출 형태의 치수는 200 × 140 × 20입니다. 너비를 4로 설정하면 배율은 4 / 200, 즉 0.02입니다. 세 축에 모두 적용되므로 결과 경계는 4 × 2.8 × 0.4입니다. 생성자에서 깊이를 20으로 설정해도 그룹을 확대·축소한 뒤 20으로 남지는 않습니다.

이는 장면 단위입니다. 이 예제는 단위 하나가 1밀리미터라고 주장하지 않습니다. 애플리케이션에 특정한 물리적 해석이 필요하다면 명시적으로 정의하고 대상 작업 흐름을 통해 검증하세요.

표면 장식 전에 구멍 확인하기

테스트 결과는 불러온 경로 1개, 도형 1개, 구멍 1개였습니다. 사각형 구멍과 채워진 영역을 각각 통과하는 광선도 쏘았습니다. 구멍에서는 메시 충돌이 없었고 채워진 영역에서는 충돌이 반환되었습니다. 잘린 오른쪽 위 모서리와 채워진 오른쪽 아래 모서리를 통과하는 광선은 비스듬한 시점과 독립적으로 세로 방향을 확인했습니다.

자신의 장면에서는 더 단순한 시각적 확인부터 시작할 수 있습니다. 표면을 정면으로 본 뒤 대비되는 배경 위에서 비스듬히 보세요. 구멍으로 배경이 보여야 하며 카메라가 움직이면 측면 벽이 나타나야 합니다. 그 영역을 덮는 흰색 표면은 다른 결과입니다.

내부 공간이 채워지면 해당 도형만 분리하세요. 원본이 하위 경로, 별도의 채워진 객체, 클리핑 경로, 마스크 중 무엇을 쓰는지 검사하세요. 재질 색상으로 문제를 숨기려 하지 마세요. SVG 구멍이 채워지는 문제 가이드는 원본 파일의 차이를 설명하고 돌출 진단은 기반 아트워크가 손상되었을 때의 윤곽 수정을 다룹니다.

기본 돌출이 작동한 뒤에만 깊이 효과 추가하기

예제는 초기 치수를 검사하기 쉽게 베벨을 끕니다. ExtrudeGeometry 옵션은 깊이, 베벨 크기, 베벨 두께, 곡선 샘플링을 각각 제어합니다. 베벨을 켜면 작은 구멍과 경계를 다시 확인하세요. 장식용 가장자리는 방금 측정한 기하학적 형태를 바꿉니다.

곡선 아트워크에서는 애플리케이션이 사용할 크기와 카메라 거리에서 메시 실루엣을 검사하세요. 각진 면이 보여 필요할 때만 곡선 샘플링을 늘리세요. 입력의 불필요한 노드를 줄이는 것은 별도의 편집 작업입니다. 어느 설정도 원본에 없던 디테일을 복원하지 않습니다.

스트로크만 있는 그림에도 다른 결정이 필요합니다. 로더는 스트로크 기하학적 형태를 위한 pointsToStroke()를 제공하지만 위의 채워진 도형 반복문은 의도적으로 그 경로들을 제외합니다. 보이는 선을 돌출 리본으로 만들려면 먼저 어떤 닫힌 윤곽이 그 리본을 정의하는지 결정하세요. 장면에서 선으로 유지해야 한다면 선이나 스트로크 표현을 만드세요.

React 애플리케이션에서는 브라우저 전용 로딩과 렌더러 작업을 클라이언트에서 실행하고 뷰가 제거될 때 소유한 기하학적 형태, 재질, 렌더러를 해제하세요. 간결한 예제는 고정 크기로 한 번 렌더링합니다. 프로덕션 뷰어에는 크기 변경 처리와 자체 리소스 수명 주기도 필요합니다.

원본과 대상 확인 구분하기

브라우저 메시는 인터랙티브 배지, 로고 미리보기, 장면 장식에 유용합니다. 결과가 빈틈없이 닫히고 치수상 적합한 부품임을 입증하지는 않습니다. 제작이 목적이라면 별도의 3D 프린팅용 이미지-SVG 작업 흐름을 사용하고 그곳에서 가져온 모델을 검사하세요.

화면 작업에서는 편집 가능한 원본 SVG, 파생 메시, 장면 표시의 세 파일 또는 단계를 구분하세요. 그러면 잘못된 결과가 경로 기하학적 형태, 메시 구성, 카메라와 조명 중 어디에서 비롯되었는지 알기 쉽습니다. 더 많은 SVG 작업 흐름 가이드는 불러오기 전에 아트워크 수정이 필요한 경우의 원본 측 작업을 다룹니다.

FAQ

Three.js에서 SVG가 뒤집히는 이유는 무엇인가요? SVG 세로 좌표는 아래쪽으로 증가합니다. 양의 y를 위쪽으로 쓰고 아트워크 앞면을 보는 장면에서는 아트워크 그룹의 y 배율을 뒤집어 원본 방향을 복원할 수 있습니다. 추가 뒤집기나 회전 전에 비대칭 특징을 확인하세요.

createShapes와 toShapes 중 무엇을 사용해야 하나요? 이 예제는 Three.js 0.186.0에서 path.toShapes()를 사용합니다. SVGLoader.createShapes()는 r185에서 사용 중단 예정으로 지정되었습니다. 핵심 패키지와 애드온을 같은 릴리스로 유지하고 프로젝트에 설치한 버전의 문서에 있는 API를 사용하세요.

SVGLoader가 SVG 안의 PNG를 메시로 바꾸나요? 삽입된 래스터 그림은 이 예제에서 사용하는 채워진 윤곽을 제공하지 않습니다. 먼저 적합한 경로 아트워크를 얻거나 외관 유지가 실제 목적이라면 이미지를 텍스처로 사용하세요.

돌출은 SVG 스트로크 너비를 유지하나요? 이 예제는 채워진 도형을 돌출하고 fill이 none인 경로는 건너뜁니다. 스트로크 기하학적 형태에는 별도 작업 흐름이 필요합니다. 두껍게 칠한 선이 자동으로 돌출 리본의 닫힌 윤곽이 되지는 않습니다.

Sources

  1. Three.js — SVGLoader — 애드온 가져오기, 파싱 및 로딩 메서드, 스트로크 헬퍼, createShapes 사용 중단 예정을 설명합니다.
  2. Three.js — ShapePath — toShapes로 경로 집합을 도형으로 변환하는 방법을 설명합니다.
  3. Three.js — ExtrudeGeometry — 도형 돌출, 깊이, 베벨, 곡선 샘플링 옵션을 정의합니다.
  4. Three.js — Box3 — 변환된 객체 경계 측정 및 크기와 중심을 얻는 방법을 설명합니다.

래스터 엠블럼으로 작업하나요? SVG 후보를 준비하고 채워진 윤곽과 구멍을 검사한 뒤 전체 뷰어를 구축하기 전에 이 작은 장면에서 파일을 테스트하세요.

블로그의 다른 글

편집하기 쉬운
더 깔끔한 SVG로 다시 시작하세요