WebGPU / TSL
WebGPU adalah standar web baru yang menyediakan API tingkat rendah untuk merender grafik dan melakukan komputasi pada GPU. Ini dirancang untuk menjadi penerus WebGL, menawarkan performa yang lebih baik dan fitur yang lebih canggih.
Kabar baiknya, sekarang kita dapat menggunakannya dengan Three.js dengan sedikit perubahan pada basis kode.
Dalam pelajaran ini, kita akan menjelajahi cara menggunakan WebGPU dengan Three.js dan React Three Fiber, serta cara menulis shaders menggunakan Three Shading Language (TSL) yang baru.
Jika Anda baru dalam bidang shaders, saya sarankan untuk menyelesaikan bab Shaders terlebih dahulu sebelum melanjutkan yang ini.
Renderer WebGPU
Untuk menggunakan WebGPU API alih-alih yang WebGL, kita perlu menggunakan WebGPURenderer
(Belum ada bagian khusus pada dokumentasi Three.js) sebagai pengganti WebGLRenderer.
Dengan React Three Fiber, saat membuat komponen <Canvas>
, penyiapan renderer dilakukan secara otomatis. Namun, kita dapat menimpa renderer default dengan melewatkan fungsi ke prop gl
dari komponen <Canvas>
.
Dalam App.jsx
, kita memiliki komponen <Canvas>
yang menggunakan WebGLRenderer
default. Mari kita modifikasi untuk menggunakan WebGPURenderer
.
Pertama, kita perlu menghentikan frameloop
hingga WebGPURenderer
siap. Kita dapat melakukannya dengan mengatur prop frameloop
ke never
.
// ... import { useState } from "react"; function App() { const [frameloop, setFrameloop] = useState("never"); return ( <> {/* ... */} <Canvas // ... frameloop={frameloop} > {/* ... */} </Canvas> </> ); } export default App;
Selanjutnya, kita perlu mengimpor versi WebGPU dari Three.js:
import * as THREE from "three/webgpu";
Saat menggunakan WebGPU, kita harus menggunakan modul
three/webgpu
alih-alih modulthree
default. Ini karenaWebGPURenderer
tidak termasuk dalam build default Three.js.
Kemudian, kita dapat menggunakan prop gl
untuk membuat instance WebGPURenderer
baru:
// ... function App() { const [frameloop, setFrameloop] = useState("never"); return ( <> {/* ... */} <Canvas // ... gl={(canvas) => { const renderer = new THREE.WebGPURenderer({ canvas, powerPreference: "high-performance", antialias: true, alpha: false, stencil: false, shadowMap: true, }); renderer.init().then(() => { setFrameloop("always"); }); return renderer; }} > {/* ... */} </Canvas> </> ); } // ...
Kita membuat instance WebGPURenderer
baru dan melewatkan elemen canvas
ke dalamnya. Kita juga mengatur beberapa opsi untuk renderer, seperti powerPreference
, antialias
, alpha
, stencil
, dan shadowMap
. Opsi-opsi ini mirip dengan yang digunakan dalam WebGLRenderer
.
Terakhir, kita memanggil metode init()
dari renderer untuk menginisialisasinya. Setelah inisialisasi selesai, kita mengatur status frameloop
ke "always"
untuk memulai rendering.
Mari kita lihat hasilnya di browser:
Kubus kita sekarang dirender menggunakan WebGPURenderer
alih-alih WebGLRenderer
.
Semudah itu! Kita telah berhasil mengatur WebGPURenderer
dalam aplikasi React Three Fiber kita. Anda sekarang dapat menggunakan API Three.js yang sama untuk membuat dan memanipulasi objek 3D, seperti yang Anda lakukan dengan WebGLRenderer
.
Perubahan terbesar adalah ketika menulis shaders. API WebGPU menggunakan bahasa bayangan yang berbeda dari WebGL, yang berarti kita harus menulis shaders kita dengan cara yang berbeda. Dalam WGSL alih-alih GLSL.
Ini adalah tempat di mana Three Shading Language (TSL) masuk.
Three Shading Language
TSL adalah bahasa shading baru yang dirancang untuk digunakan dengan Three.js untuk menulis shaders dengan cara yang lebih ramah pengguna menggunakan pendekatan berbasis node.
Keuntungan besar dari TSL adalah tidak terikat pada renderer tertentu, yang berarti Anda dapat menggunakan shader yang sama dengan renderer yang berbeda, seperti WebGL dan WebGPU.
Ini membuatnya lebih mudah untuk menulis dan memelihara shader, karena Anda tidak perlu memikirkan perbedaan antara dua bahasa shading.
Ini juga tahan masa depan, karena jika ada renderer baru yang dirilis, kita bisa menggunakan shader yang sama tanpa perubahan selama TSL mendukungnya.
Three Shading Language masih dalam pengembangan, tetapi sudah tersedia dalam versi terbaru dari Three.js. Cara terbaik untuk mempelajarinya, dan untuk melacak perubahan, adalah dengan memeriksa halaman wiki Three Shading Language. Saya menggunakannya secara ekstensif untuk belajar bagaimana menggunakannya.
Node based materials
Untuk memahami cara membuat shader dengan TSL, kita perlu memahami apa yang dimaksud dengan berbasis node.
Dalam pendekatan berbasis node, kita membuat shader dengan menghubungkan node-node berbeda untuk membuat grafik. Setiap node mewakili operasi atau fungsi tertentu, dan koneksi antara node-node tersebut mewakili aliran data.
Pendekatan ini memiliki banyak keuntungan, seperti:
- Representasi visual: Lebih mudah untuk memahami dan memvisualisasikan aliran data dan operasi dalam shader.
- Reusability: Kita dapat membuat node yang dapat digunakan kembali dalam shader yang berbeda.
- Fleksibilitas: Kita dapat dengan mudah memodifikasi dan mengubah perilaku shader dengan menambah atau menghapus node.
- Ekstensibilitas: Menambah/Mengkustomisasi fitur dari material yang ada sekarang menjadi lebih mudah.
- Agnostic: TSL akan menghasilkan kode yang sesuai untuk renderer target, apakah itu WebGL (GLSL) atau WebGPU (WGSL).
Sebelum kita mulai membuat kode material berbasis node pertama kita, kita dapat menggunakan Three.js playground online untuk bereksperimen dengan node-system secara visual.
Buka Three.js playground dan di bagian atas, klik tombol Examples, dan pilih basic > fresnel
.
Anda seharusnya melihat editor material berbasis node dengan dua node color
dan sebuah node float
yang terhubung ke node fresnel
. (Color A
, Color B
, dan Fresnel Factor
)
Node fresnel
terhubung ke warna dari Basic Material
yang menghasilkan pewarnaan Teapot dengan efek fresnel.
Gunakan tombol Splitscreen
untuk melihat hasilnya di sebelah kanan.
Misalkan kita ingin mempengaruhi opasitas dari Basic Material
berdasarkan waktu. Kita dapat menambahkan node Timer
dan menghubungkannya ke node Fract
untuk mengatur ulang waktu ke 0 setelah mencapai 1. Kemudian kita menghubungkannya ke input opacity
dari Basic Material
.
Teapot kita sekarang memudar lalu menghilang dan muncul kembali.
Luangkan waktu untuk bermain dengan node-node yang berbeda dan lihat bagaimana mereka mempengaruhi material.
Sekarang kita memiliki pemahaman dasar tentang bagaimana material berbasis node bekerja, mari kita lihat bagaimana menggunakan material berbasis node baru dari Three.js dalam React Three Fiber.
Implementasi React Three Fiber
Sejauh ini, dengan WebGL, kita telah menggunakan MeshBasicMaterial, MeshStandardMaterial, atau bahkan ShaderMaterial kustom untuk membuat material kita.
Saat menggunakan WebGPU, kita perlu menggunakan material baru yang kompatibel dengan TSL. Nama-nama mereka sama dengan yang kita gunakan sebelumnya dengan Node
sebelum Material
:
MeshBasicMaterial
->MeshBasicNodeMaterial
MeshStandardMaterial
->MeshStandardNodeMaterial
MeshPhysicalMaterial
->MeshPhysicalNodeMaterial
- ...
Untuk menggunakannya secara deklaratif dengan React Three Fiber, kita perlu extend
mereka. Dalam App.jsx
:
// ... import { extend } from "@react-three/fiber"; extend({ MeshBasicNodeMaterial: THREE.MeshBasicNodeMaterial, MeshStandardNodeMaterial: THREE.MeshStandardNodeMaterial, }); // ...
Di versi masa depan dari React Three Fiber, ini mungkin dilakukan secara otomatis.
Sekarang kita bisa menggunakan MeshBasicNodeMaterial
dan MeshStandardNodeMaterial
yang baru di dalam komponen kita.
Mari kita ganti MeshStandardMaterial
dari kubus dalam komponen Experience
kita dengan MeshStandardNodeMaterial
:
<mesh> <boxGeometry args={[1, 1, 1]} /> <meshStandardNodeMaterial color="pink" /> </mesh>
Kita dapat menggunakan MeshStandardNodeMaterial
seperti kita menggunakan MeshStandardMaterial
.
Kubus kita sekarang bergantung pada MeshStandardNodeMaterial
alih-alih MeshStandardMaterial
. Kita sekarang dapat menggunakan nodes untuk menyesuaikan material tersebut.
Node Warna
Mari kita pelajari cara membuat node kustom untuk mempersonalisasi material kita dengan TSL.
Pertama, mari kita buat komponen baru bernama PracticeNodeMaterial.jsx
di dalam folder src/components
.
export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { return <meshStandardNodeMaterial color={colorA} />; };
Dan di dalam Experience.jsx
, ganti kubus kita dengan plane menggunakan PracticeNodeMaterial
:
// ... import { PracticeNodeMaterial } from "./PracticeNodeMaterial"; export const Experience = () => { return ( <> {/* ... */} <mesh rotation-x={-Math.PI / 2}> <planeGeometry args={[2, 2, 200, 200]} /> <PracticeNodeMaterial /> </mesh> </> ); };
Kita memiliki plane dengan PracticeNodeMaterial
.
Untuk mempersonalisasi material kita, kita sekarang dapat mengubah berbagai node yang tersedia menggunakan node yang berbeda. Daftar yang tersedia dapat ditemukan di halaman wiki.
Mari kita mulai dengan node colorNode
untuk mengubah warna material kita. Dalam PracticeNodeMaterial.jsx
:
import { color } from "three/tsl"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { return <meshStandardNodeMaterial colorNode={color(colorA)} />; };
Kita mengatur prop colorNode
menggunakan node color
dari modul three/tsl
. Node color
mengambil warna sebagai argumen dan mengembalikan node warna yang dapat digunakan dalam material.
Ini memberikan hasil yang sama seperti sebelumnya, tetapi sekarang kita dapat menambahkan lebih banyak node untuk mempersonalisasi material kita.
Mari kita impor node mix
dan uv
dari modul three/tsl
dan gunakan untuk mencampur dua warna berdasarkan koordinat UV dari plane.
import { color, mix, uv } from "three/tsl"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { return ( <meshStandardNodeMaterial colorNode={mix(color(colorA), color(colorB), uv())} /> ); };
Ini akan menjalankan node yang berbeda untuk mendapatkan warna akhir dari material. Node mix
mengambil dua warna dan faktor (dalam hal ini, koordinat UV) dan mengembalikan warna yang merupakan campuran dari kedua warna berdasarkan faktor tersebut.
Ini persis seperti menggunakan fungsi mix
di GLSL, tetapi sekarang kita dapat menggunakannya dengan pendekatan berbasis node. (Jauh lebih mudah dibaca!)
Kita sekarang dapat melihat dua warna bercampur berdasarkan koordinat UV dari plane.
Yang luar biasa, adalah kita tidak memulai dari awal. Kita menggunakan MeshStandardNodeMaterial
yang ada dan hanya menambahkan node kustom kita ke dalamnya. Yang berarti bayangan, cahaya, dan semua fitur lain dari MeshStandardNodeMaterial
masih tersedia.
Menyatakan node secara inline adalah baik untuk logika node yang sangat sederhana, tetapi untuk logika yang lebih kompleks, saya merekomendasikan untuk mendeklarasikan node (dan nanti uniform, dan lebih banyak lagi) dalam hook useMemo
:
// ... import { useMemo } from "react"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { const { nodes } = useMemo(() => { return { nodes: { colorNode: mix(color(colorA), color(colorB), uv()), }, }; }, []); return <meshStandardNodeMaterial {...nodes} />; };
Ini melakukan hal yang persis sama seperti sebelumnya, tetapi sekarang kita dapat menambahkan lebih banyak node ke objek nodes
dan meneruskannya ke meshStandardNodeMaterial
dengan cara yang lebih terorganisir/generic.
Dengan mengubah prop colorA
dan colorB
, itu tidak akan menyebabkan kompilasi ulang shader berkat hook useMemo
.
Mari kita tambahkan controls untuk mengubah warna material. Dalam Experience.jsx
:
// ... import { useControls } from "leva"; export const Experience = () => { const { colorA, colorB } = useControls({ colorA: { value: "skyblue" }, colorB: { value: "blueviolet" }, }); return ( <> {/* ... */} <mesh rotation-x={-Math.PI / 2}> <planeGeometry args={[2, 2, 200, 200]} /> <PracticeNodeMaterial colorA={colorA} colorB={colorB} /> </mesh> </> ); };
Warna default berfungsi dengan benar, tetapi memperbarui warna tidak memiliki efek.
Ini karena kita perlu meneruskan warna sebagai uniforms
ke meshStandardNodeMaterial
.
Uniforms
Untuk mendeklarasikan uniforms dalam TSL, kita dapat menggunakan node uniform
dari modul three/tsl
. Node uniform
mengambil nilai sebagai argumen (dapat berupa berbagai tipe seperti float
, vec3
, vec4
, dll.) dan mengembalikan uniform node yang dapat digunakan dalam berbagai node sambil diperbarui dari kode komponen kita.
Mari ubah warna yang di-hardcode menjadi uniforms dalam PracticeNodeMaterial.jsx
:
// ... import { uniform } from "three/tsl"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { const { nodes, uniforms } = useMemo(() => { const uniforms = { colorA: uniform(color(colorA)), colorB: uniform(color(colorB)), }; return { nodes: { colorNode: mix(uniforms.colorA, uniforms.colorB, uv()), }, uniforms, }; }, []); return <meshStandardNodeMaterial {...nodes} />; };
Kami mendeklarasikan objek uniforms
untuk organisasi kode yang lebih baik, dan kami menggunakan nilai uniform daripada nilai default yang kami dapatkan saat pembuatan node kami.
Dengan mengembalikannya dalam useMemo
, sekarang kita memiliki akses ke uniforms dalam komponen kita.
Dalam useFrame
kita dapat memperbarui uniforms:
// ... import { useFrame } from "@react-three/fiber"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { // ... useFrame(() => { uniforms.colorA.value.set(colorA); uniforms.colorB.value.set(colorB); }); return <meshStandardNodeMaterial {...nodes} />; };
Gunakan metode
value.set
ketika Anda memperbarui uniform objek. Misalnya,color
atauvec3
uniforms. Untukfloat
uniforms, Anda perlu mengatur nilai secara langsung:uniforms.opacity.value = opacity;
Warna sekarang diperbarui dengan benar secara real-time.
Sebelum melakukan lebih banyak hal pada warna, mari kita lihat bagaimana kita dapat mempengaruhi posisi vertices dari plane kita menggunakan positionNode
.
Mengatur Node Posisi
Node positionNode
memungkinkan kita untuk mempengaruhi posisi dari vertices geometri kita.
React Three Fiber: The Ultimate Guide to 3D Web Development
✨ You have reached the end of the preview ✨
Go to the next level with Three.js and React Three Fiber!
Get full access to this lesson and the complete course when you enroll:
- 🔓 Full lesson videos with no limits
- 💻 Access to the final source code
- 🎓 Course progress tracking & completion
- 💬 Invite to our private Discord community
One-time payment. Lifetime updates included.