WebGL Konsep — Creative Web

WebGL Konsep WebGL (Web Graphics Library) adalah API JavaScript untuk merender grafik 2D dan 3D langsung di browser menggunakan GPU (Graphics Processing…

WebGL adalah API JavaScript untuk menggambar di <canvas> memakai GPU, chip grafis di laptop dan HP. Bayangin cara Raka menyablon: sekali tarik rakel, tinta melewati screen dan menempel di ribuan titik kain sekaligus. Screen menentukan di mana tinta lewat, tintanya menentukan warna apa. GPU bekerja begitu: satu perintah gambar, lalu program kecil yang sama dijalankan untuk ribuan pixel bersamaan. Program kecil itu namanya shader, dan di WebGL kamu yang menulisnya.

Raka ingin pembeli bisa memutar mug sablon Kopi dalam 3D di tokonya. Sebelum ke three.js, kita lihat dulu apa yang terjadi di GPU dengan bentuk paling dasar: satu segitiga.

🧪 Tebakan cepat: sebuah segitiga punya tiga titik sudut: coklat #c47a3d di atas, krem #f5e6c8 di kiri bawah, hijau #7fb069 di kanan bawah. Pixel tepat di tengahnya (titik berat segitiga) berwarna apa? Tebak dulu, jawabannya ada di bagian "Attribute, uniform, varying".

WebGL 2 dan GPU

GPU punya ribuan inti kecil yang menjalankan program yang sama di banyak data sekaligus. WebGL 2 adalah OpenGL ES 3.0 untuk browser:

const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('Browser ini belum mendukung WebGL 2');

WebGL 2 ada sejak Chrome 56, Firefox 51 dan Safari 15, sekitar 96% pengguna (caniuse, Oktober 2026). getContext mengembalikan null kalau gak didukung atau GPU-nya diblokir, jadi selalu cek.

Canvas 2D WebGL 2
Cara menggambar panggil fillRect, arc, browser yang mengerjakan tulis shader sendiri, GPU menjalankannya
Warna tiap pixel ditentukan browser fragment shader-mu
3D gak ada ya, kamu hitung sendiri posisinya
Kode untuk satu bentuk 3 baris puluhan baris

Canvas 2D adalah API 2D tingkat tinggi. Browser bisa saja memakai GPU di belakangnya (Chrome dan Safari sering begitu), tapi kamu gak bisa menulis program untuk GPU. Di WebGL kamu bisa, dan itu yang membuat 3D dan efek berat mungkin.

Pipeline: dari titik ke pixel

Satu gl.drawArrays(gl.TRIANGLES, 0, 3) di canvas 300 × 300 melewati empat tahap:

Tahap Berjalan Tugasnya
1. Vertex buffer sekali diisi menyimpan data tiap titik (posisi, warna) di memori GPU
2. Vertex shader 3 kali, sekali per titik menentukan posisi akhir titik itu
3. Rasteriser sekali, otomatis mencari pixel mana yang ada di dalam segitiga: 14.850 pixel
4. Fragment shader 14.850 kali, sekali per pixel menentukan warna pixel itu

Kandidat pixel yang dihasilkan rasteriser disebut fragment. Angka 14.850 bukan tebakan: luas segitiganya 16,5% dari canvas, dan 16,5% × 90.000 = 14.850, sama dengan hitungan readPixels di Lab. Di canvas 600 × 600 (layar DPR 2) fragment shader jalan 59.400 kali, empat kali lipat. Rasteriser adalah satu-satunya tahap yang gak kamu tulis.

Clip space

Vertex shader gak bicara pixel. Ia mengisi gl_Position dalam clip space: x dan y dari -1 sampai 1, (0, 0) di tengah canvas, dan y ke atas, kebalikan dari Canvas 2D. Mengubahnya ke pixel:

pixel x = (x + 1) / 2 × lebar
pixel y = (1 - y) / 2 × tinggi

Titik atas segitiga (0; 0,6) di canvas 300 × 300 jatuh di pixel (150, 60). Titik di luar -1 sampai 1 gak error: bagian segitiga yang keluar dipotong (clipping), dan sisanya tetap digambar.

Segitiga pertama, kode lengkap

<canvas id="gpu" width="300" height="300"></canvas>
const canvas = document.getElementById('gpu');
const gl = canvas.getContext('webgl2', { antialias: false });
if (!gl) throw new Error('Browser ini belum mendukung WebGL 2');

// Vertex shader: jalan sekali per titik
const vsSource = `#version 300 es
in vec2 aPosisi;
in vec3 aWarna;
out vec3 vWarna;
void main() {
  vWarna = aWarna;
  gl_Position = vec4(aPosisi, 0.0, 1.0);
}`;

// Fragment shader: jalan sekali per pixel
const fsSource = `#version 300 es
precision highp float;
in vec3 vWarna;
out vec4 warnaAkhir;
void main() {
  warnaAkhir = vec4(vWarna, 1.0);
}`;

function buatShader(tipe, sumber) {
  const shader = gl.createShader(tipe);
  gl.shaderSource(shader, sumber);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader));
  }
  return shader;
}

const program = gl.createProgram();
gl.attachShader(program, buatShader(gl.VERTEX_SHADER, vsSource));
gl.attachShader(program, buatShader(gl.FRAGMENT_SHADER, fsSource));
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(program));
}

// Tiap titik: x, y (clip space), lalu r, g, b (0 sampai 1)
const data = new Float32Array([
   0.0,  0.6,   0.769, 0.478, 0.239, // atas: COKLAT #c47a3d
  -0.6, -0.5,   0.961, 0.902, 0.784, // kiri bawah: KREM #f5e6c8
   0.6, -0.5,   0.498, 0.690, 0.412, // kanan bawah: HIJAU #7fb069
]);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const lokPosisi = gl.getAttribLocation(program, 'aPosisi');
gl.enableVertexAttribArray(lokPosisi);
gl.vertexAttribPointer(lokPosisi, 2, gl.FLOAT, false, 20, 0);
const lokWarna = gl.getAttribLocation(program, 'aWarna');
gl.enableVertexAttribArray(lokWarna);
gl.vertexAttribPointer(lokWarna, 3, gl.FLOAT, false, 20, 8);

gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.11, 0.098, 0.09, 1); // hitam kaos #1c1917
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);

Attribute, uniform, varying

Ada tiga cara data masuk ke shader:

Jenis Di GLSL ES 3.00 Nilainya Contoh
attribute in di vertex shader beda di tiap titik, dari buffer aPosisi, aWarna
uniform uniform sama untuk semua titik dan pixel di satu gambar warna tunggal, waktu, matriks kamera
varying out di vertex shader, in di fragment shader dicampur (interpolasi) GPU untuk tiap pixel vWarna

WebGL 1 menulisnya attribute dan varying; GLSL ES 3.00 memakai in dan out. Rasteriser mencampur varying menurut jarak pixel ke tiap titik. Di titik berat, ketiga titik berbobot sama, 1/3: merah (196 + 245 + 127) / 3 = 189, hijau (122 + 230 + 176) / 3 = 176, biru (61 + 200 + 105) / 3 = 122. Pixel tengahnya #bdb07a, krem kecoklatan. Itu jawaban tebakannya; Chrome, Firefox dan Safari sama.

Mau satu warna untuk seluruh segitiga? Itu uniform:

// fragment shader: uniform vec3 uWarna;  ...  warnaAkhir = vec4(uWarna, 1.0);
gl.useProgram(program);
gl.uniform3f(gl.getUniformLocation(program, 'uWarna'), 0.769, 0.478, 0.239);

Kenapa three.js ada

Kode di atas 55 baris (tanpa baris kosong dan komentar) untuk satu segitiga datar. Versi three.js:

import * as THREE from 'three';

const canvas = document.getElementById('gpu');
const renderer = new THREE.WebGLRenderer({ canvas });
const scene = new THREE.Scene();
scene.background = new THREE.Color('#1c1917');
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, -1, 1);

const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0.6, 0, -0.6, -0.5, 0, 0.6, -0.5, 0], 3));
const warna = ['#c47a3d', '#f5e6c8', '#7fb069'].flatMap((hex) => new THREE.Color(hex).toArray());
geometry.setAttribute('color', new THREE.Float32BufferAttribute(warna, 3));
scene.add(new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ vertexColors: true })));

renderer.render(scene, camera);

12 baris, dan shader, buffer, VAO dan program dibuatkan three.js (versi 0.186). Segitiganya menutup 14.850 pixel yang sama; tengahnya sedikit lebih terang (#c5b78a) karena three.js mencampur warna di ruang warna linear, yang dibahas di lesson Geometri & Material. Selisihnya melebar untuk mug 3D: kamera perspektif, matriks putar, normal permukaan, rumus cahaya dan tekstur. Di WebGL langsung itu ratusan baris; di three.js beberapa baris per hal.

WebGPU hari ini

WebGPU adalah penerus WebGL: API yang lebih dekat ke GPU modern, dengan shader dalam WGSL (WebGPU Shading Language), bukan GLSL. Statusnya per Oktober 2026 (wiki implementasi gpuweb):

Browser Aktif tanpa setelan
Chrome dan Edge Windows, macOS, ChromeOS sejak 113; Android 12+ sejak 121. Linux masih di balik flag
Firefox Windows sejak 141; macOS Apple Silicon sejak 145. Linux, Mac Intel dan Android masih dikembangkan
Safari macOS, iOS, iPadOS dan visionOS 26

Sekitar 86% pengguna (caniuse), lebih sedikit dari WebGL 2. three.js punya WebGPURenderer yang otomatis turun ke WebGL 2 kalau WebGPU gak ada. Konsepnya sama: buffer, vertex shader, rasteriser, fragment shader.

⚠️ Jebakan yang sering ketemu

TL;DR: WebGL 2 (OpenGL ES 3.0, getContext('webgl2'), cek null) menggambar dengan GPU lewat shader yang kamu tulis dalam GLSL ES 3.00. Pipeline: vertex buffer → vertex shader (sekali per titik, mengisi gl_Position di clip space -1 sampai 1, y ke atas) → rasteriser (mencari pixel di dalam bentuk) → fragment shader (sekali per pixel, menentukan warna). Segitiga di canvas 300 × 300: vertex shader 3 kali, fragment shader 14.850 kali. Attribute beda per titik, uniform sama untuk semua, varying dicampur per pixel (tengah segitiga coklat, krem, hijau = #bdb07a). Satu segitiga butuh 55 baris WebGL atau 12 baris three.js. WebGPU (WGSL) aktif di Chrome, Edge, Safari 26 dan Firefox di Windows dan Mac Apple Silicon, belum di semua Linux.

Yang akan kamu pelajari