PerfectVector
Bởi Irene Kim10 phút đọc

Three.js SVGLoader: Biến hình phẳng thành lưới 3D

Tải đường dẫn SVG vào Three.js, giữ lỗ thực, sửa hướng dọc và đặt kích thước lưới đùn qua ví dụ nhỏ có thể tái tạo trong trình duyệt.

Trên trang này

Để biến hình SVG phẳng thành lưới Three.js, tải SVG, chuyển đường dẫn thành hình rồi truyền các hình vào ExtrudeGeometry. Kiểm tra lỗ, hướng dọc và kích thước trước khi thêm hiệu ứng. Đường bao vẫn nhận ra được có thể có lòng chữ bị tô kín hoặc một góc bị lộn ngược.

Hướng dẫn này dùng huy hiệu tự tạo bằng tay, có một lỗ vuông và góc trên phải vát. Sự bất đối xứng giúp thấy lỗi hướng. Đây là ví dụ hình học trong trình duyệt, không phải kết quả chuyển đổi PerfectVector hay thử nghiệm chế tạo.

Bắt đầu với các hình bạn muốn tạo chiều sâu

Lần đầu hãy dùng SVG nhỏ với đường bao đóng, có màu tô. Lưu thành 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>

Có hai đường dẫn con đóng trong một phần tử đường dẫn: ranh giới ngoài và hình vuông trong. Quy tắc tô evenodd khiến vùng trong đó thành lỗ. Một hình chữ nhật trắng riêng chỉ che bề mặt trong ảnh phẳng; không mô tả cùng vùng rỗng.

Phần hình chiếm 200 × 140 đơn vị tọa độ trong viewBox 240 × 180. Nhớ khác biệt đó khi đặt kích thước lưới. Lề trống là phần của khung nhìn SVG, không phải phần đường bao được đùn.

Nếu đã có bản vector gốc chỉnh sửa được, hãy dùng nó. Nếu PNG hoặc JPG là nguồn duy nhất, quy trình ảnh thành vector của PerfectVector có thể giúp chuẩn bị hình dạng đường dẫn SVG. Kiểm tra bản xem trước và đường bao tải xuống, nhất là không gian âm, trước khi tải tệp vào Three.js. Vector hóa cung cấp phần hình 2D; mã bên dưới cung cấp chiều sâu, vật liệu, ánh sáng và vị trí.

Tải SVG và kết xuất lưới

Ví dụ được thử với Three.js 0.186.0. Giữ thư viện lõi và addon cùng phiên bản. Trong dự án trình duyệt có import gói, cài three@0.186.0, phục vụ tệp qua HTTP và tạo vùng chứa:

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

Công cụ xây dựng phải phân giải các import three. Với trang trình duyệt thuần không có công cụ xây dựng, đặt import map này trước tập lệnh mô-đun, dùng tệp từ cùng gói đã cài:

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

Đặt badge.svg cạnh trang HTML. URL tải tương đối bên dưới được phân giải theo URL trang đó. Sau đó lưu đoạn này thành 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 là addon được import riêng. loadAsync() đọc tệp; parse(svgText) là lựa chọn khi bạn đã có mã SVG dạng chuỗi.

Trong r186, dùng path.toShapes(). Ví dụ cũ thường gọi SVGLoader.createShapes(path). Hàm hỗ trợ đó bị đánh dấu ngừng khuyến nghị từ r185 và chuyển tiếp sang toShapes() trong phiên bản đã thử. Kiểm tra bản đã cài trước khi trộn đoạn mã từ các hướng dẫn khác nhau.

Vòng lặp chủ động bỏ fill="none". Nó xử lý màu tô đặc trong ví dụ này; không phải bộ kết xuất SVG đầy đủ. Gradient, mặt nạ, ảnh nhúng, đường viền, độ đục (opacity) và lớp màu chồng lên nhau cần quyết định riêng, thay vì giả định diện mạo phẳng sẽ thành hình học 3D giống hệt.

Ảnh chụp trình duyệt thực tế đối chiếu huy hiệu SVG tự tạo có lỗ vuông và góc trên phải vát với lưới đùn Three.js của nó
Bản kết xuất WebGL thực tế của mẫu tạo bằng tay trong Three.js r186. Lỗ vẫn rỗng, góc vát vẫn ở trên phải và co giãn đồng đều tạo lưới 4 × 2.8 × 0.4.

Đặt hướng trước khi đo và căn giữa

Tọa độ dọc của SVG tăng xuống dưới. Cảnh này nhìn về phần hình với y dương hướng lên, nên artwork.scale.y = -1 khôi phục hướng mong muốn. Góc vát là điểm kiểm tra: nó phải ở trên phải.

Việc đổi dấu thuộc cấu hình tọa độ này. Đừng thêm lần lật thứ hai vì lưới vẫn trông sai sau khi xoay camera hoặc đối tượng cha. Trước tiên đối chiếu chi tiết bất đối xứng đã biết trong nguồn với cảnh kết xuất.

Các dòng tiếp theo đo nhóm đã lật, tính tỷ lệ đồng đều và dịch tâm đã co giãn về gốc. Box3.setFromObject() đo giới hạn gồm đối tượng con và phép biến đổi của chúng. Ở đây nhóm không có cha đã biến đổi và phần hình không xoay khi đo.

Khối đùn ban đầu có kích thước 200 × 140 × 20. Đặt chiều rộng 4 cho tỷ lệ 4 / 200, tức 0.02. Vì tỷ lệ áp dụng cho cả ba trục, giới hạn kết quả là 4 × 2.8 × 0.4. Chiều sâu 20 trong hàm khởi tạo không còn là 20 sau khi co giãn nhóm.

Đây là đơn vị cảnh. Ví dụ không tuyên bố một đơn vị là một milimét. Nếu ứng dụng cần cách hiểu vật lý cụ thể, hãy định nghĩa rõ và xác minh qua quy trình đích.

Xác nhận lỗ trước khi trang trí bề mặt

Phép thử tạo một đường dẫn tải được, một hình và một lỗ. Chúng tôi cũng chiếu tia xuyên lỗ vuông và tia khác qua vùng đặc: tia qua lỗ không giao với lưới, còn tia qua vùng đặc thì có. Tia qua góc trên phải vát và góc dưới phải đặc xác nhận hướng dọc độc lập với góc nhìn nghiêng.

Bạn có thể bắt đầu bằng kiểm tra thị giác đơn giản hơn trong cảnh của mình: nhìn trực diện mặt hình, rồi nhìn nghiêng trên nền tương phản. Lỗ cần cho thấy nền và thành bên của nó cần xuất hiện khi camera di chuyển. Một mặt trắng che vùng đó là kết quả khác.

Nếu lòng chữ bị tô kín, cô lập hình đó. Kiểm tra nguồn dùng đường dẫn con, đối tượng tô riêng, đường dẫn cắt hay mặt nạ. Đừng cố giấu lỗi bằng màu vật liệu. Hướng dẫn lỗ SVG bị tô kín giải thích khác biệt tệp nguồn; chẩn đoán đùn khối trình bày sửa đường bao khi hình gốc bị lỗi.

Chỉ thêm hiệu ứng chiều sâu sau khi đùn đơn giản hoạt động

Ví dụ tắt vát mép để kích thước đầu dễ kiểm tra. Tùy chọn ExtrudeGeometry kiểm soát riêng chiều sâu, kích thước vát, độ dày vát và lấy mẫu đường cong. Kiểm tra lại lỗ nhỏ và giới hạn khi bật vát. Mép trang trí thay đổi hình học vừa đo.

Với hình cong, kiểm tra đường bao lưới ở kích thước và khoảng cách camera ứng dụng sẽ dùng. Chỉ tăng lấy mẫu đường cong khi các mặt gãy cần điều đó; giảm nút không cần thiết của đầu vào là nhiệm vụ chỉnh sửa riêng. Không thiết lập nào khôi phục chi tiết vắng mặt trong nguồn.

Hình chỉ có đường viền cũng cần quyết định khác. Bộ tải cung cấp pointsToStroke() cho hình học đường viền, nhưng vòng lặp hình tô phía trên chủ động bỏ những đường dẫn đó. Nếu nét nhìn thấy cần thành dải đùn, trước tiên quyết định đường bao đóng nào định nghĩa dải đó. Nếu nó cần giữ là nét trong cảnh, hãy dựng biểu diễn dạng đường hoặc đường viền.

Với ứng dụng React, chạy việc tải và kết xuất chỉ dùng trong trình duyệt ở phía client, đồng thời giải phóng hình học, vật liệu và bộ kết xuất do bạn tạo khi bỏ khung xem. Ví dụ gọn kết xuất một lần ở kích thước cố định; trình xem dùng trong môi trường thực tế cũng cần xử lý đổi kích thước và vòng đời tài nguyên riêng.

Giữ kiểm tra nguồn và đích riêng

Lưới trình duyệt hữu ích cho huy hiệu tương tác, xem trước logo hoặc trang trí cảnh. Nó không xác nhận kết quả là chi tiết kín hoàn toàn và có kích thước phù hợp. Nếu đích là chế tạo, dùng riêng quy trình ảnh thành SVG cho in 3D và kiểm tra mô hình đã nhập ở đó.

Với quy trình màn hình, tách ba tệp hoặc giai đoạn: SVG nguồn chỉnh sửa được, lưới tạo từ nó và phần trình bày cảnh. Điều này giúp xác định kết quả sai đến từ hình học đường dẫn, dựng lưới hay camera và ánh sáng. Các hướng dẫn quy trình SVG khác trình bày phần nguồn khi hình cần sửa trước tải.

Câu hỏi thường gặp

Vì sao SVG bị lộn ngược trong Three.js? Tọa độ dọc SVG tăng xuống dưới. Trong cảnh có y dương hướng lên và nhìn mặt trước hình, lật tỷ lệ y của nhóm hình có thể khôi phục hướng nguồn. Kiểm tra chi tiết bất đối xứng trước khi thêm lần lật hoặc xoay khác.

Tôi nên dùng createShapes hay toShapes? Ví dụ này dùng path.toShapes() với Three.js 0.186.0. SVGLoader.createShapes() bị đánh dấu ngừng khuyến nghị từ r185. Dùng API được tài liệu mô tả cho phiên bản đã cài trong dự án, với gói lõi và addon cùng bản phát hành.

SVGLoader có biến PNG trong SVG thành lưới không? Ảnh raster nhúng không cung cấp đường bao tô mà ví dụ này dùng. Lấy hình dạng đường dẫn phù hợp trước, hoặc dùng ảnh làm texture khi giữ diện mạo mới là mục tiêu thực tế.

Đùn có giữ chiều rộng đường viền SVG không? Ví dụ này đùn hình tô và bỏ đường dẫn đặt fill là none. Hình học đường viền cần quy trình riêng; nét tô dày không tự là đường bao đóng của dải đùn.

Nguồn tham khảo

  1. Three.js — SVGLoader — Mô tả import addon, phương thức phân tích và tải, hàm hỗ trợ đường viền và việc ngừng khuyến nghị createShapes.
  2. Three.js — ShapePath — Mô tả chuyển tập đường dẫn thành hình bằng toShapes.
  3. Three.js — ExtrudeGeometry — Định nghĩa đùn hình, chiều sâu, vát và tùy chọn lấy mẫu đường cong.
  4. Three.js — Box3 — Mô tả đo giới hạn đối tượng đã biến đổi và lấy kích thước, tâm.

Bạn làm từ biểu tượng chỉ có raster? Chuẩn bị bản SVG ứng viên, kiểm tra đường bao tô và lỗ, rồi thử tệp đó trong cảnh nhỏ này trước khi xây trình xem đầy đủ.

Thêm từ blog

Bắt đầu với SVG gọn sạch hơn
và dễ chỉnh sửa hơn