WEBGPU WGSL SHADER GRAPHICS-PROGRAMMING GPU-COMPUTING WEBASSEMBLY HIGH-PERFORMANCE FRONTEND WEB-DEVELOPMENT BROWSER-API 3D-GRAPHICS COMPUTE-SHADER

WGSL (WebGPU Shading Language): Memprogram GPU Anda Langsung di Browser

⏱️ 10 menit baca
👨‍💻

WGSL (WebGPU Shading Language): Memprogram GPU Anda Langsung di Browser

1. Pendahuluan

Pernahkah Anda membayangkan bisa memprogram GPU (Graphics Processing Unit) langsung dari browser web Anda? Memanfaatkan kekuatan komputasi paralel yang luar biasa untuk grafis 3D yang memukau, pemrosesan data masif, atau bahkan model AI lokal? Dengan kedatangan WebGPU, mimpi itu menjadi kenyataan. Namun, untuk benar-benar mendalami potensi WebGPU, kita perlu memahami bahasa rahasia yang digunakannya: WGSL (WebGPU Shading Language).

WGSL adalah bahasa pemrograman khusus yang dirancang untuk menulis shader – program kecil yang berjalan di GPU. Jika WebGPU adalah “API baru untuk mengakses GPU dari web”, maka WGSL adalah “bahasa yang Anda gunakan untuk berbicara dengan GPU melalui API itu”. Ini adalah fondasi di balik semua visual canggih dan komputasi performa tinggi yang bisa Anda bangun dengan WebGPU.

Mengapa topik ini penting bagi developer web Indonesia?

Di artikel ini, kita akan menyelami WGSL: memahami apa itu, bagaimana sintaks dasarnya, dan bagaimana kita bisa mulai menulis shader sederhana untuk memprogram GPU langsung dari browser.

2. Memahami Peran Shader di WebGPU

Sebelum kita masuk ke sintaks WGSL, mari kita pahami dulu di mana posisi shader dalam pipeline WebGPU. Bayangkan WebGPU sebagai sebuah “pabrik” yang memproses data untuk menghasilkan gambar atau hasil komputasi. Shader adalah “instruksi” yang Anda berikan kepada pekerja di pabrik tersebut.

Ada dua jenis shader utama dalam konteks grafis 3D:

Selain itu, WebGPU juga mendukung:

WGSL digunakan untuk menulis ketiga jenis shader ini. Jadi, Anda akan menulis kode WGSL yang akan dikompilasi dan dijalankan langsung di GPU.

3. WGSL 101: Sintaks Dasar

WGSL memiliki sintaks yang mirip dengan C, Rust, atau GLSL (pendahulu shader di OpenGL/WebGL), tetapi dengan beberapa karakteristik modern dan spesifik untuk WebGPU.

Tipe Data Dasar

WGSL memiliki tipe data yang familiar:

Vektor dan Matriks

GPU sangat efisien dalam memproses data dalam bentuk vektor dan matriks. WGSL mendukung ini secara built-in:

Struktur (Structs)

Anda bisa mendefinisikan tipe data kustom menggunakan struct, mirip dengan bahasa C:

struct VertexInput {
    [[location(0)]] position: vec4<f32>,
    [[location(1)]] color: vec4<f32>,
};

struct Uniforms {
    model_view_projection_matrix: mat4x4<f32>,
    time: f32,
};

📌 Perhatikan atribut [[location(X)]] yang akan kita bahas nanti. Ini penting untuk menghubungkan input/output shader dengan data dari CPU.

Variabel dan Konstanta

Deklarasi variabel menggunakan var dan konstanta menggunakan const. Tipe data dapat disimpulkan atau dideklarasikan secara eksplisit.

const PI: f32 = 3.14159;
var my_color: vec4<f32> = vec4<f32>(1.0, 0.0, 0.0, 1.0); // Merah
var position = vec3<f32>(0.0, 0.0, 0.0); // Tipe disimpulkan

Fungsi (Functions)

Fungsi didefinisikan dengan fn. Setiap shader harus memiliki fungsi main atau fungsi yang ditandai dengan [[stage(vertex)]], [[stage(fragment)]], atau [[stage(compute)]].

fn add_vectors(a: vec3<f32>, b: vec3<f32>) -> vec3<f32> {
    return a + b;
}

4. Input/Output Shader: Atribut, Varying, dan Binding

Bagaimana data bisa masuk ke shader dan keluar dari shader? WGSL menggunakan beberapa mekanisme penting:

Atribut Input (Vertex Shader)

Data yang masuk ke vertex shader untuk setiap vertex (misalnya posisi, warna, normal) disebut atribut. Anda mendeklarasikannya di struct input vertex shader dengan atribut [[location(X)]]. X adalah indeks lokasi yang harus cocok dengan konfigurasi buffer di WebGPU API.

// Input untuk Vertex Shader
struct VertexInput {
    [[location(0)]] position: vec4<f32>, // Posisi vertex
    [[location(1)]] color: vec4<f32>,    // Warna vertex
};

Varying (Komunikasi Vertex ke Fragment Shader)

Data yang dihitung di vertex shader dan perlu diteruskan ke fragment shader disebut “varying” (karena nilainya bervariasi antar fragment). Data ini akan di-interpolasi secara otomatis oleh GPU di antara vertex-vertex. Anda mendeklarasikannya di struct output vertex shader dan struct input fragment shader, juga dengan [[location(X)]].

// Output dari Vertex Shader, Input untuk Fragment Shader
struct VertexOutput {
    [[builtin(position)]] clip_position: vec4<f32>, // Posisi akhir di layar (built-in)
    [[location(0)]] interpolated_color: vec4<f32>, // Warna yang diinterpolasi
};

📌 [[builtin(position)]] adalah atribut khusus untuk output posisi vertex. WebGPU membutuhkan ini agar tahu di mana harus menggambar. Ada banyak [[builtin]] lainnya seperti vertex_index, instance_index, frag_depth, dll.

Binding (Uniforms, Textures, Buffers)

Untuk data yang tidak berubah per vertex atau per fragment, seperti matriks transformasi (model-view-projection), tekstur, atau buffer data umum, kita menggunakan [[group(X), binding(Y)]].

// Uniform Buffer (data yang sama untuk semua vertex/fragment dalam satu draw call)
@group(0) @binding(0)
var<uniform> uniforms: Uniforms; // `Uniforms` adalah struct yang kita definisikan sebelumnya

// Texture
@group(0) @binding(1)
var my_texture: texture_2d<f32>;

// Sampler (cara membaca tekstur)
@group(0) @binding(2)
var my_sampler: sampler;

// Storage Buffer (untuk compute shader, bisa dibaca/ditulis)
@group(1) @binding(0)
var<storage, read_write> data_buffer: array<f32>;

⚠️ var<uniform> berarti data hanya-baca. var<storage, read_write> berarti data bisa dibaca dan ditulis (umumnya untuk compute shader).

5. Fungsi dan Kontrol Aliran

WGSL mendukung struktur kontrol aliran yang Anda kenal:

fn calculate_light(normal: vec3<f32>, light_dir: vec3<f32>) -> f32 {
    let dot_product = dot(normal, light_dir);
    if dot_product > 0.0 {
        return dot_product;
    } else {
        return 0.0;
    }
}

WGSL juga memiliki banyak fungsi built-in untuk matematika vektor/matriks (seperti dot, cross, normalize, length), fungsi trigonometri (sin, cos, tan), dan fungsi umum lainnya yang sangat berguna dalam grafis.

6. Contoh Praktis: Shader Sederhana (Segitiga Berwarna)

Mari kita buat shader WGSL yang paling dasar untuk menggambar segitiga berwarna. Ini akan melibatkan satu vertex shader dan satu fragment shader.

Vertex Data (JavaScript/TypeScript)

Pertama, kita siapkan data vertex di sisi JavaScript. Anggap kita punya segitiga dengan 3 vertex, masing-masing punya posisi dan warna.

// Ini contoh data yang akan kita kirim ke GPU
const vertices = new Float32Array([
    // Posisi (x, y, z, w) | Warna (r, g, b, a)
    0.0,  0.5, 0.0, 1.0,   1.0, 0.0, 0.0, 1.0, // Top vertex: Merah
    -0.5, -0.5, 0.0, 1.0,   0.0, 1.0, 0.0, 1.0, // Left vertex: Hijau
    0.5, -0.5, 0.0, 1.0,   0.0, 0.0, 1.0, 1.0, // Right vertex: Biru
]);
// Nanti di WebGPU API, kita akan mengkonfigurasi buffer ini
// agar posisi masuk ke location(0) dan warna ke location(1)

WGSL Vertex Shader (shader.wgsl)

// Mendefinisikan struktur input untuk vertex shader
// [[location(0)]] akan menerima posisi vertex dari buffer
// [[location(1)]] akan menerima warna vertex dari buffer
struct VertexInput {
    [[location(0)]] position: vec4<f32>,
    [[location(1)]] color: vec4<f32>,
};

// Mendefinisikan struktur output dari vertex shader
// Ini juga akan menjadi input untuk fragment shader (varying)
struct VertexOutput {
    // [[builtin(position)]] adalah output wajib yang menentukan posisi akhir di layar
    [[builtin(position)]] clip_position: vec4<f32>,
    // [[location(0)]] adalah output warna yang akan diinterpolasi
    [[location(0)]] interpolated_color: vec4<f32>,
};

// Fungsi utama vertex shader
// [[stage(vertex)]] menandakan ini adalah vertex shader
fn main(input: VertexInput) -> VertexOutput {
    var output: VertexOutput;
    output.clip_position = input.position; // Cukup terusan posisi mentah untuk contoh 2D sederhana
    output.interpolated_color = input.color; // Meneruskan warna ke fragment shader
    return output;
}

💡 Di sini, kita hanya meneruskan posisi dan warna. Dalam aplikasi 3D nyata, input.position akan dikalikan dengan matriks model-view-projection di sini.

WGSL Fragment Shader (shader.wgsl)

// Mendefinisikan struktur input untuk fragment shader
// Ini sama dengan struktur output dari vertex shader
struct FragmentInput {
    [[builtin(position)]] frag_coord: vec4<f32>, // Koordinat piksel di layar (built-in)
    [[location(0)]] interpolated_color: vec4<f32>, // Warna yang diinterpolasi dari vertex shader
};

// Fungsi utama fragment shader
// [[stage(fragment)]] menandakan ini adalah fragment shader
// -> vec4<f32> adalah warna akhir piksel (RGBA)
fn main(input: FragmentInput) -> [[location(0)]] vec4<f32> {
    // Cukup gunakan warna yang sudah diinterpolasi dari vertex shader
    return input.interpolated_color;
}

Tips: Untuk memulai, selalu coba dengan warna solid (return vec4<f32>(1.0, 0.0, 0.0, 1.0);) di fragment shader untuk memastikan pipeline rendering Anda berfungsi sebelum menambahkan kompleksitas.

Dengan shader-shader ini, dan kode WebGPU API yang sesuai untuk mengatur pipeline rendering, Anda akan melihat segitiga dengan gradasi warna dari merah ke hijau ke biru di layar!

Kesimpulan

WGSL adalah pintu gerbang menuju kekuatan penuh GPU di web. Sebagai bahasa shader yang modern dan efisien, WGSL memungkinkan developer web untuk menciptakan pengalaman visual yang kaya, melakukan komputasi paralel yang intensif, dan mendorong batas-batas performa aplikasi web.

Memulai dengan WGSL mungkin terasa seperti belajar bahasa baru yang asing, tetapi dengan sintaks yang terstruktur dan konsep yang jelas mengenai bagaimana data mengalir melalui GPU, Anda akan segera mampu menulis shader yang mengubah cara aplikasi web Anda berinteraksi dengan hardware. Ini bukan hanya tentang grafis 3D; ini tentang membuka dimensi baru performa dan kemampuan di browser. Jadi, mulailah bereksperimen, tulis shader pertama Anda, dan saksikan GPU Anda bekerja!

🔗 Baca Juga