PerfectVector
Autor: Irene Kim9 min czytania

Three.js SVGLoader: zamień płaską grafikę w siatkę 3D

Wczytaj ścieżki SVG do Three.js, zachowaj rzeczywisty otwór, popraw orientację pionową i ustal wymiary siatki po wyciągnięciu na niewielkim, odtwarzalnym przykładzie przeglądarkowym.

Na tej stronie

Aby zamienić płaską grafikę SVG w siatkę Three.js, wczytaj SVG, przekształć jego ścieżki w kształty, a następnie przekaż te kształty do ExtrudeGeometry. Zanim dodasz efekty, sprawdź otwory, orientację pionową i wymiary. Rozpoznawalna sylwetka nadal może mieć wypełnione światło wewnętrzne lub narożnik odwrócony do góry nogami.

W tym przewodniku używamy oryginalnej, ręcznie przygotowanej odznaki z jednym kwadratowym otworem i ściętym prawym górnym narożnikiem. Asymetria pozwala zauważyć błąd orientacji. To przykład geometrii w przeglądarce, a nie wynik konwersji w PerfectVector ani test wytwarzania.

Zacznij od kształtów, którym chcesz nadać głębokość

Do pierwszej próby użyj niewielkiego SVG z zamkniętymi, wypełnionymi konturami. Zapisz go jako 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>

W jednym elemencie ścieżki znajdują się dwie zamknięte podścieżki: zewnętrzna granica i wewnętrzny kwadrat. Reguła wypełnienia evenodd sprawia, że ten wewnętrzny obszar staje się otworem. Osobny biały prostokąt jedynie zasłoniłby powierzchnię na płaskim obrazie; nie opisywałby tego samego pustego obszaru.

Grafika zajmuje 200 na 140 jednostek współrzędnych wewnątrz viewBox o wymiarach 240 na 180. Pamiętaj o tej różnicy przy ustalaniu wymiarów siatki. Puste marginesy są częścią obszaru wyświetlania SVG, a nie sylwetki po wyciągnięciu.

Jeśli masz już edytowalny oryginał w postaci grafiki wektorowej, użyj go. Jeśli jedynym źródłem jest PNG lub JPG, proces konwersji obrazu na grafikę wektorową w PerfectVector może pomóc Ci przygotować grafikę złożoną ze ścieżek SVG. Zanim wczytasz plik do Three.js, sprawdź podgląd i pobrane kontury, zwłaszcza puste przestrzenie. Wektoryzacja dostarcza grafikę 2D; poniższy kod dodaje głębokość, materiały, oświetlenie i położenie.

Wczytaj SVG i wyrenderuj siatkę

Przykład przetestowano z Three.js 0.186.0. Używaj tej samej wersji biblioteki głównej i dodatku. W projekcie przeglądarkowym z importami pakietów zainstaluj three@0.186.0, udostępnij pliki przez HTTP i utwórz kontener:

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

Twoje narzędzie do budowania projektu musi rozwiązywać importy three. W przypadku zwykłej strony przeglądarkowej bez narzędzia do budowania umieść tę mapę importów przed skryptem modułowym, korzystając z plików z tego samego zainstalowanego pakietu:

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

Umieść badge.svg obok strony HTML. Poniższy względny adres URL wczytywanego pliku jest rozwiązywany względem adresu URL tej strony. Następnie zapisz ten kod jako 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 to dodatek importowany osobno. loadAsync() odczytuje plik; parse(svgText) jest alternatywą, gdy masz już kod SVG w postaci ciągu znaków.

W r186 używaj path.toShapes(). Starsze przykłady często wywołują SVGLoader.createShapes(path). Tę funkcję pomocniczą oznaczono jako przestarzałą w r185, a w testowanej wersji deleguje ona działanie do toShapes(). Zanim połączysz fragmenty z różnych poradników, sprawdź zainstalowane wydanie.

Pętla celowo pomija fill="none". Obsługuje jednolite wypełnienia w tym przykładzie; nie jest pełnym mechanizmem renderowania SVG. Gradienty, maski, osadzone obrazy, obrysy, krycie i nakładające się warstwy kolorów wymagają osobnych decyzji zamiast założenia, że płaski wygląd przełoży się na identyczną geometrię 3D.

Rzeczywisty zrzut z przeglądarki porównujący oryginalną odznakę SVG z kwadratowym otworem i ściętym prawym górnym narożnikiem z jej siatką po wyciągnięciu w Three.js
Rzeczywisty render WebGL ręcznie przygotowanej grafiki testowej w Three.js r186. Otwór pozostaje pusty, ścięty narożnik pozostaje u góry po prawej, a jednolite skalowanie daje siatkę o wymiarach 4 na 2,8 na 0,4.

Ustaw orientację przed pomiarem i wyśrodkowaniem

Pionowe współrzędne SVG rosną w dół. W tej scenie patrzymy na grafikę przy dodatniej osi y skierowanej w górę, więc artwork.scale.y = -1 przywraca oczekiwaną orientację. Ścięty narożnik służy do sprawdzenia: powinien znajdować się u góry po prawej.

Ta zmiana znaku dotyczy tego układu współrzędnych. Nie dodawaj drugiego odwrócenia tylko dlatego, że po obróceniu kamery lub obiektu nadrzędnego siatka nadal wygląda źle. Najpierw porównaj znany asymetryczny szczegół ze źródła z wyrenderowaną sceną.

Kolejne wiersze mierzą odwróconą grupę, obliczają jednolitą skalę i przesuwają jej przeskalowany środek do początku układu współrzędnych. Box3.setFromObject() mierzy granice z uwzględnieniem obiektów potomnych i ich transformacji. Tutaj podczas pomiaru grupa nie ma przekształconego obiektu nadrzędnego, a grafika nie jest obrócona.

Pierwotna geometria po wyciągnięciu ma wymiary 200 na 140 na 20. Ustawienie szerokości na 4 daje skalę 4 / 200, czyli 0.02. Ponieważ ta skala dotyczy wszystkich trzech osi, wynikowe wymiary wynoszą 4 na 2,8 na 0,4. Głębokość 20 podana w konstruktorze nie pozostaje równa 20 po przeskalowaniu grupy.

Są to jednostki sceny. Przykład nie zakłada, że jedna jednostka to jeden milimetr. Jeśli Twoja aplikacja wymaga określonej interpretacji fizycznej, zdefiniuj ją wprost i zweryfikuj w docelowym procesie.

Sprawdź otwór przed ozdobieniem powierzchni

Test dał jedną wczytaną ścieżkę, jeden kształt i jeden otwór. Przeprowadziliśmy też test przecięcia promienia przechodzącego przez kwadratowy otwór oraz drugiego przechodzącego przez pełny obszar: dla otworu nie wykryto przecięcia z siatką, a dla pełnego obszaru wykryto przecięcie. Promienie przechodzące przez ścięty prawy górny narożnik i pełny prawy dolny narożnik potwierdziły orientację pionową niezależnie od widoku pod kątem.

We własnej scenie możesz zacząć od prostszego sprawdzenia wizualnego: obejrzyj przednią powierzchnię na wprost, a następnie pod kątem na kontrastującym tle. Przez otwór powinno być widać tło, a przy ruchu kamery powinny pojawiać się jego boczne ścianki. Biała powierzchnia zakrywająca ten obszar oznacza inny wynik.

Jeśli światło wewnętrzne zostaje wypełnione, wyodrębnij ten jeden kształt. Sprawdź, czy źródło używa podścieżek, osobnych wypełnionych obiektów, ścieżki przycinającej czy maski. Nie próbuj ukrywać problemu kolorem materiału. Przewodnik po otworach SVG, które zostają wypełnione wyjaśnia różnice w plikach źródłowych; diagnostyka wyciągania omawia naprawę konturów, gdy sama grafika jest uszkodzona.

Dodaj efekty głębokości dopiero wtedy, gdy zwykłe wyciągnięcie działa

W przykładzie fazowanie jest wyłączone, aby początkowe wymiary dało się łatwo sprawdzić. Opcje ExtrudeGeometry osobno sterują głębokością, rozmiarem fazy, grubością fazy i próbkowaniem krzywych. Po włączeniu fazowania ponownie sprawdź małe otwory i wymiary. Dekoracyjna krawędź zmienia właśnie zmierzoną geometrię.

W przypadku grafiki z krzywymi sprawdź sylwetkę siatki przy rozmiarze grafiki i odległości kamery przewidzianych w Twojej aplikacji. Zwiększ próbkowanie krzywych tylko wtedy, gdy wymaga tego widoczna kanciastość; usunięcie zbędnych węzłów z grafiki wejściowej to osobne zadanie edycyjne. Ani zwiększenie próbkowania, ani usunięcie zbędnych węzłów nie przywróci szczegółów, których nie było w źródle.

Rysunek złożony wyłącznie z obrysów również wymaga innej decyzji. Moduł wczytujący udostępnia pointsToStroke() do tworzenia geometrii obrysu, ale powyższa pętla przetwarzająca wypełnione kształty celowo pomija takie ścieżki. Jeśli widoczna linia ma stać się wyciągniętą wstęgą, najpierw zdecyduj, jaki zamknięty kontur definiuje tę wstęgę. Jeśli ma pozostać linią w scenie, utwórz zamiast tego reprezentację linii lub obrysu.

W aplikacji React wykonuj wczytywanie i operacje renderera wymagające przeglądarki po stronie klienta, a po usunięciu widoku zwalniaj zarządzaną przez niego geometrię, materiały i renderer. Ten zwięzły przykład renderuje raz przy stałym rozmiarze; przeglądarka modeli przeznaczona do użytku produkcyjnego wymaga także obsługi zmiany rozmiaru i własnego cyklu zarządzania zasobami.

Sprawdzaj źródło i wynik docelowy osobno

Siatka w przeglądarce przydaje się do interaktywnej odznaki, podglądu logo lub dekoracji sceny. Nie dowodzi jednak, że wynik jest szczelną częścią o odpowiednich wymiarach. Jeśli celem jest wytwarzanie, skorzystaj z osobnego procesu konwersji obrazu na SVG do druku 3D i sprawdź tam zaimportowany model.

W procesie przeznaczonym do wyświetlania na ekranie rozdziel trzy pliki lub etapy: edytowalny źródłowy SVG, utworzoną na jego podstawie siatkę i prezentację sceny. Dzięki temu łatwiej ustalisz, czy zły wynik wynika z geometrii ścieżek, konstrukcji siatki, czy kamery i oświetlenia. Więcej przewodników po pracy z SVG omawia etap źródłowy, gdy grafika wymaga naprawy przed wczytaniem.

Najczęściej zadawane pytania

Dlaczego mój SVG jest odwrócony do góry nogami w Three.js? Pionowe współrzędne SVG rosną w dół. W scenie, w której dodatnia oś y jest skierowana w górę i oglądasz grafikę od przodu, zmiana znaku skali grupy grafiki w osi y może przywrócić orientację źródłową. Zanim dodasz kolejne odwrócenie lub obrót, sprawdź asymetryczny szczegół.

Czy używać createShapes czy toShapes? Ten przykład używa path.toShapes() z Three.js 0.186.0. SVGLoader.createShapes() oznaczono jako przestarzałe w r185. Używaj API udokumentowanego dla wersji zainstalowanej w Twoim projekcie, z pakietem głównym i dodatkiem w tym samym wydaniu.

Czy SVGLoader zamienia PNG osadzony w SVG w siatkę? Osadzony obraz rastrowy nie dostarcza wypełnionych konturów używanych w tym przykładzie. Najpierw przygotuj odpowiednią grafikę złożoną ze ścieżek albo użyj obrazu jako tekstury, jeśli rzeczywistym celem jest zachowanie jego wyglądu.

Czy wyciąganie zachowuje szerokość obrysu SVG? Ten przykład wyciąga wypełnione kształty i pomija ścieżki z fill ustawionym na none. Geometria obrysu wymaga osobnego procesu; gruba namalowana linia nie staje się automatycznie zamkniętym konturem wyciągniętej wstęgi.

Źródła

  1. Three.js — SVGLoader — Dokumentuje import dodatku, metody parsowania i wczytywania, funkcję pomocniczą do obrysów oraz oznaczenie createShapes jako przestarzałego.
  2. Three.js — ShapePath — Dokumentuje przekształcanie zbiorów ścieżek w kształty za pomocą toShapes.
  3. Three.js — ExtrudeGeometry — Definiuje wyciąganie kształtów oraz opcje głębokości, fazowania i próbkowania krzywych.
  4. Three.js — Box3 — Dokumentuje pomiar granic przekształconych obiektów oraz uzyskiwanie ich rozmiaru i środka.

Pracujesz nad emblematem dostępnym wyłącznie jako obraz rastrowy? Przygotuj wstępną wersję SVG, sprawdź jej wypełnione kontury i otwory, a następnie przetestuj ten plik w tej niewielkiej scenie, zanim zbudujesz pełną przeglądarkę modeli.

Więcej z bloga

Zacznij od czystszego SVG,
który łatwiej edytować