JavaScript dalam 3D: pengantar Three.js

Halo, Habr! Saya hadir untuk Anda terjemahan artikel "JavaScript dalam 3D: sebuah Pengantar Three.js" oleh Bret Cameron.

Pendahuluan


Three.js adalah alat yang ampuh. Ini membantu untuk menggunakan desain 3D di browser dengan kinerja yang dapat diterima. Memulai Three.js bisa jadi rumit, terutama jika Anda belum pernah terjun ke dunia pemrograman 3D sebelumnya.

Saya memiliki pengalaman dasar dengan mesin game Unity dan C #, tetapi tetap saja, banyak konsep yang baru bagi saya. Saya sampai pada kesimpulan bahwa sekarang ada sangat sedikit sumber daya untuk pengembang pemula, jadi saya memutuskan untuk menulis artikel ini. Di dalamnya, kita akan mempertimbangkan elemen dasar adegan Three.js mulai dari jerat poligon dan material hingga geometri, pemuat, dan banyak lagi.

Pada akhir artikel ini, Anda akan memiliki pemahaman yang kuat tentang aspek-aspek dasar yang diperlukan untuk menambahkan dimensi ekstra ke proyek web masa depan Anda.

Three.js contoh dari Ben Houston , Thomas Diewald dan StrykerDoesAnimation .

Vektor dan wadah - blok bangunan dasar


Seringkali ada dua kelas utama di Three.js - Vector3 dan Box3 . Jika Anda baru mengenal 3D, ini mungkin terdengar agak abstrak, tetapi Anda akan bertemu mereka berkali-kali.

Vector3


Kelas 3D paling dasar yang berisi tiga angka: x, y, dan z . Bilangan adalah koordinat suatu titik dalam ruang 3D atau arah dan panjangnya. Sebagai contoh:

const vect = new THREE.Vector3(1, 1, 1); 

Sebagian besar konstruktor di Three.js menerima objek bertipe Vector3 sebagai argumen input, misalnya Box3

Kotak3


Kelas ini mewakili berbentuk kubus (wadah 3D). Tugas utamanya adalah membuat wadah di sekitar objek lain - dan itu saja, berbentuk kubus terkecil di mana objek 3D akan cocok. Setiap Box3 disejajarkan tentang sumbu x, y, dan z. Contoh cara membuat wadah menggunakan Vector3 :

 const vect = new THREE.Vector3(1, 1, 1); const box = new THREE.Box3(vect); 

Contoh cara membuat wadah di sekitar objek 3D yang ada:

 const box = new THREE.Box3(); box.setFromObject(object); 

Anda dapat membuat kisi-kisi tanpa pengetahuan mendalam ini, tetapi begitu Anda mulai membuat atau mengubah model, kelas-kelas ini pasti akan berguna. Sekarang kita akan beralih dari abstraksi ke hal-hal yang lebih terlihat.

Mesh poligon


Di Three.js, elemen visual utama di panggung adalah Mesh . Ini adalah objek 3D yang terdiri dari segitiga segi empat (poligonal mesh). Itu dibangun menggunakan dua objek:
Geometri - menentukan bentuknya, Material - menentukan penampilan.

Definisi mereka mungkin tampak sedikit membingungkan (misalnya, kelas Geometri mungkin berisi informasi tentang warna), tetapi perbedaan utamanya adalah persis seperti itu.

Geometri


Berdasarkan tugas yang ingin Anda capai, Anda mungkin ingin mendefinisikan geometri di dalam Three.js atau mengimpor yang lain dari file.

Dengan menggunakan fungsi-fungsi seperti TIGA . TorsiKnotGeometry , kita dapat membuat objek kompleks dengan satu baris kode. Kami akan segera melakukan ini, tetapi pertama-tama pertimbangkan bentuk yang lebih sederhana.
Sosok 3D paling sederhana, berbentuk kubus atau wadah, dapat ditentukan oleh parameter lebar , tinggi dan kedalaman .

 const geometry = new THREE.BoxGeometry( 20, 20, 20 ); 


Untuk bola, nilai parameter radius , widthSegment, dan heightSegments minimal . Dua variabel terakhir menunjukkan berapa banyak segitiga model yang harus digunakan untuk mewakili bola: semakin besar angkanya, semakin halus akan terlihat.

 const geometry = new THREE.SphereGeometry( 20, 64, 64 ); 


Jika kita ingin membuat bentuk yang tajam atau segitiga, maka kita dapat menggunakan kerucut. Argumennya adalah kombinasi dari argumen dua tokoh sebelumnya. Di bawah ini, kami meresepkan radius , widthSegment dan radialSegments .

  const geometry = new THREE.ConeBufferGeometry( 5, 20, 32 ); 


Ini hanya bagian dari figur paling umum. Three.js memiliki banyak bentuk di dalam kotak, yang dapat ditemukan dalam dokumentasi. Pada artikel ini, kita akan melihat bentuk yang lebih menarik yang dibangun berdasarkan metode TorusKnotGeometry .

Mengapa bentuk-bentuk ini terlihat persis seperti yang terlihat? Pertanyaan ini berada di luar cakupan artikel ini, tetapi saya mendorong Anda untuk bereksperimen dengan nilai parameter, karena Anda bisa mendapatkan bentuk yang sangat menarik dengan satu baris kode!

 const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20); 

https://codepen.io/BretCameron/pen/gOYqORg

Material


Geometri menentukan bentuk objek 3D kita, tetapi bukan penampilannya. Untuk memperbaikinya, kita membutuhkan material.

Three.js menawarkan 10 bahan di luar kotak, masing-masing memiliki kelebihan dan parameter yang dapat disesuaikan. Kami akan menganggap hanya bagian yang paling berguna.



MeshNormalMaterial


Berguna untuk memulai dan memulai dengan cepat.

Kita akan mulai dengan MeshNormalMaterial , material multicolor yang kita gunakan dalam contoh di atas. Ini sesuai dengan vektor normal pada panel RGB, dengan kata lain, warna digunakan untuk menentukan posisi vektor dalam ruang 3D.

 const material = new THREE.MeshNormalMaterial(); 

Perhatikan bahwa jika Anda ingin mengubah warna materi, Anda dapat menggunakan filter CSS dan mengubah saturasi:
  filter: hue-rotate(90deg) . 


Dalam pengalaman saya, materi ini lebih berguna untuk strata cepat dan peluncuran. Untuk lebih mengontrol objek Anda, lebih baik menggunakan sesuatu yang lain.

Meshbasicmaterial


Berguna saat hanya menampilkan kerangka.

Jika Anda ingin memberikan angka satu warna, maka Anda dapat menggunakan MeshBasicMaterial hanya jika pencahayaan tidak diterapkan. Saya merasa berguna untuk menggunakan bahan itu dalam merender kerangka model. Untuk hanya menggambar kerangka, Anda harus memasukkan {wireframe: true} sebagai parameter.

 const material = new THREE.MeshBasicMaterial({ wireframe: true, color: 0xdaa520 }); 

Kerugian utama dari material ini adalah bahwa informasi tentang kedalaman material benar-benar hilang. Setiap bahan memiliki opsi untuk hanya menampilkan kerangka, tetapi hanya satu bahan yang memecahkan masalah kurangnya kedalaman - MeshDepthMaterial


Meshhambertahan


Berguna untuk kinerja tinggi tetapi akurasi rendah.

Ini adalah bahan pertama yang memperhitungkan cahaya, jadi Anda perlu menambahkan beberapa cahaya ke adegan kami. Dalam kode di bawah ini, kami menambahkan lampu sorot dengan warna kuning untuk menciptakan efek yang lebih hangat.

 const scene = new THREE.Scene(); const frontSpot = new THREE.SpotLight(0xeeeece); frontSpot.position.set(1000, 1000, 1000); scene.add(frontSpot); const frontSpot2 = new THREE.SpotLight(0xddddce); frontSpot2.position.set(-500, -500, -500); scene.add(frontSpot2); 

Sekarang tambahkan bahan untuk gambar kita. Karena sosok kami seperti hiasan, saya sarankan menambahkan warna yang lebih keemasan. Parameter lain, emissive , adalah warna objek yang berasal dari objek itu sendiri (tanpa sumber cahaya). Seringkali ini berfungsi lebih baik sebagai warna gelap - misalnya, sebagai nuansa abu-abu gelap, seperti pada contoh di bawah ini

 const material = new THREE.MeshLambertMaterial({ color: 0xdaa520, emissive: 0x111111, }); 


Seperti yang Anda lihat pada contoh di bawah ini, warnanya kurang lebih benar, tetapi caranya berinteraksi dengan cahaya tidak menambah realisme. Untuk memperbaiki ini, kita perlu menggunakan MeshPhongMaterial atau MeshStandardMaterial.

MeshPhongMaterial


Berguna untuk kinerja sedang dan akurasi sedang.

Materi ini menawarkan tradeoff antara kinerja dan akurasi render, sehingga materi ini merupakan pilihan yang baik untuk aplikasi yang harus produktif bersama dengan rendering yang lebih akurat daripada dengan MeshLambertMaterial.

Sekarang kita dapat mengubah properti specular yang mempengaruhi kecerahan dan warna pantulan permukaan. Jika properti emissive biasanya gelap, maka specular paling cocok untuk warna-warna terang. Di bawah ini kami menggunakan abu-abu terang.

 const material = new THREE.MeshPhongMaterial({ color: 0xdaa520, emissive: 0x000000, specular: 0xbcbcbc, }); 


Secara visual, gambar dari atas memantulkan cahaya lebih meyakinkan, tetapi masih belum sempurna. Cahaya putih terlalu terang dan material terlihat lebih berusuk daripada logam (dan kami berusaha keras untuk ini). Kita bisa mendapatkan hasil yang lebih baik menggunakan MeshStandardMaterial.

MeshStandartMaterial


Berguna untuk akurasi tinggi tetapi produktivitas rendah.

Ini adalah bahan yang paling akurat dari semua, meskipun penggunaannya akan memerlukan biaya menggunakan lebih banyak daya. MeshStandartMaterial digunakan dengan parameter metalness dan roughness tambahan, yang masing-masing mengambil nilai antara 0 dan 1.

Parameter sifat logam mempengaruhi bagaimana objek memantulkan, menjadi lebih dekat dengan sifat logam. Ini karena bahan konduktif seperti logam memiliki sifat reflektif yang berbeda, tidak seperti dielektrik seperti keramik.

Kekasaran menambahkan lapisan ekstra untuk penyesuaian. Anda dapat membayangkannya sebagai kebalikan dari gloss: 0 - sangat glossy, 1 - sangat matte.

 const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, }); 


Ini adalah materi yang paling realistis dari semua yang disajikan dalam Three.js, tetapi juga yang paling memakan sumber daya

Materi yang kami diskusikan di bawah adalah yang paling sering saya temui dan Anda bisa melihat semua opsi di dok.

Loader


Seperti yang kita bahas di atas, Anda dapat menentukan geometri dan jerat poligon secara manual. Dalam praktiknya, orang sering memuat geometri dari file. Untungnya, Three.js memiliki beberapa pengunduh yang didukung yang mendukung banyak format 3D.

ObjectLoader utama memuat file JSON menggunakan format JSON Object / Scene . Sebagian besar bootloader perlu diimpor secara manual. Anda dapat menemukan daftar lengkap bootloader yang didukung di sini dan mengimpornya. Di bawah ini adalah daftar kecil dari apa yang dapat Anda impor.

 // GLTF import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; // OBJ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; // STL import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; // FBX import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'; // 3MF import { 3MFLoader } from 'three/examples/jsm/loaders/3MFLoader.js'; 

Format yang disarankan untuk ditonton online adalah GLTF, karena formatnya “ditujukan untuk memberikan aset dalam runtime, ringkas untuk transfer, dan cepat untuk diunduh”.

Tentu saja, ada banyak alasan untuk memilih jenis file tertentu (misalnya, jika kualitas adalah prioritas atau akurasi diperlukan untuk pencetakan 3D). Performa online terbaik adalah ketika mengimpor GLTF.

 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import model from '../models/sample.gltf'; let loader = new GLTFLoader(); loader.load(model, function (geometry) { // if the model is loaded successfully, add it to your scene here }, undefined, function (err) { console.error(err); }); 

Menyatukan semuanya


Salah satu alasan mengapa Three.js mungkin tampak menakutkan adalah bahwa Anda dapat membuat sesuatu dari awal hanya dengan beberapa baris kode. Dalam setiap contoh di atas, kami perlu membuat adegan dan kamera. Untuk menyederhanakan, saya menyimpan kode ini di luar lingkup ulasan, tetapi untuk sekarang kita akan melihat bagaimana itu akan terlihat bersama.

Cara Anda mengatur kode terserah Anda. Dalam contoh sederhana, seperti dalam artikel ini, masuk akal untuk menulis semua kode di satu tempat. Namun dalam praktiknya, akan berguna untuk memisahkan elemen individu untuk kemungkinan memperluas basis kode dan manajemennya.

Untuk mempermudah, kami akan mempertimbangkan elemen yang digambar sebagai objek tunggal, jadi kami akan meletakkan semua kode dalam satu file.

 // Import dependencies import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; //   const scene = new THREE.Scene(); scene.background = new THREE.Color(0x282c34); //  ,       const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000); camera.position.z = 5; //  ""      const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); //   DOM   renderer.domElement   document.getElementById('threejs').appendChild(renderer.domElement); //  ,      DOM  let controls = new OrbitControls(camera, document.getElementById('threejs')); controls.target.set(0, 0, 0); controls.rotateSpeed = 0.5; controls.update(); //  ( )   const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20); //    const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, }); //   ,       const mesh = new THREE.Mesh(geometry, material); mesh.scale.x = 0.1; mesh.scale.y = 0.1; mesh.scale.z = 0.1; scene.add(mesh); //  ,       const frontSpot = new THREE.SpotLight(0xeeeece); const frontSpot2 = new THREE.SpotLight(0xddddce); frontSpot.position.set(1000, 1000, 1000); frontSpot2.position.set(-500, -500, -500); scene.add(frontSpot); scene.add(frontSpot2); //   ,           const animate = function () { requestAnimationFrame(animate); mesh.rotation.x += 0.005; mesh.rotation.y += 0.005; mesh.rotation.z += 0.005; renderer.render(scene, camera); }; //    animate(); 

Apakah saya perlu menggunakan framework?


Akhirnya, saatnya untuk membahas apakah akan menggunakan Three.js dengan kerangka kerja favorit Anda? Saat ini, ada paket reaksi-tiga-serat yang baik untuk React. Untuk pengguna Bereaksi, ada keuntungan yang jelas untuk menggunakan paket seperti ini - Anda mempertahankan struktur untuk bekerja dengan komponen yang memungkinkan Anda untuk menggunakan kembali kode.

Untuk pemula, saya menyarankan Anda untuk mulai dengan Vanila JS yang biasa, karena sebagian besar materi online yang ditulis tentang Three.js berkaitan dengan Three.js di Vanila JS. Berdasarkan pengalaman belajar saya, ini bisa membingungkan dan sulit dipelajari melalui paket - misalnya, Anda harus menerjemahkan objek dan metode Three.js ke komponen dan alat peraga. (segera setelah Anda belajar Three.js Anda dapat menggunakan paket apa pun).

Bagaimana cara menambahkan Three.js ke framework


Three.js menyediakan objek HTML (paling sering disebut renderer.domElement ) yang dapat ditambahkan ke objek HTML apa pun di aplikasi Anda. Misalnya, jika Anda memiliki div dengan id = "threejs" Anda cukup memasukkan kode berikut dalam kode Three.js Anda:

 document.getElementById('threejs').appendChild(renderer.domElement); 

Beberapa kerangka kerja memiliki jalur yang disukai untuk mengakses node DOM dari pohon. Misalnya, ref dalam React, $ ref dalam Vue atau ngRef di Angular dan itu terlihat seperti nilai tambah besar dengan latar belakang akses langsung ke elemen DOM. Sebagai contoh, mari kita lihat implementasi cepat untuk Bereaksi.

Strategi untuk Bereaksi


Jika Anda menggunakan Bereaksi, maka ada satu cara untuk menanamkan file Three.js ke salah satu komponen Anda. Dalam file ThreeEntryPoint.js kita akan menulis kode berikut:

 export default function ThreeEntryPoint(sceneRef) { let renderer = new THREE.WebGLRenderer(); // ... sceneRef.appendChild(renderer.domElement); } 

Kami mengekspor ini sebagai fungsi yang mengambil satu argumen: referensi ke elemen dalam komponen kami. Sekarang kita dapat membuat komponen kita

 import React, { Component } from 'react'; import ThreeEntryPoint from './threejs/ThreeEntryPoint'; export default class ThreeContainer extends Component { componentDidMount() { ThreeEntryPoint(this.scene); } render() { return ( <> <div ref={element => this.scene = element} /> </> ); } } 

Fungsi ThreeEntryPoint yang diimpor harus dipanggil dalam metode componentDidMount dan meneruskan div baru sebagai argumen menggunakan referensi
Sebagai contoh dari pendekatan ini dalam tindakan, Anda dapat mengkloning repositori dan mencobanya sendiri: https://github.com/BretCameron/three-js-sample .

Kesimpulan


Masih banyak yang bisa saya bicarakan tentang Three.js, tapi saya harap artikel ini memberi Anda cukup informasi untuk mulai menggunakan teknologi canggih ini. Ketika saya mulai belajar Three.js, saya tidak dapat menemukan sumber tunggal seperti artikel ini, jadi saya harap saya membantu membuat teknologi ini lebih mudah diakses oleh pemula.

Source: https://habr.com/ru/post/id477956/


All Articles