Latar Belakang
Pengembangan aplikasi web saat ini telah memungkinkan untuk membuat aplikasi 3D (tiga dimensi) interaktif. Beberapa teknologi yang dapat dijadikan pilihan untuk mengembangkan aplikasi 3D berbasis web antara lain Microsoft Silverlight 5, Adobe Flash Player 11 dan Khronos WebGL 1.0. Diantara 3 teknologi tersebut, WebGL telah menjadi pilihan banyak developer untuk membuat aplikasi 3D. WebGL yang dikelola oleh Khronos telah diimplementasikan oleh browser-browser mayor seperti Mozilla Firefox, Google Chrome, Opera dan Aplle Safari secara native. Telah banyak demo dan presentasi atau tutorial WebGL, tetapi untuk versi Indonesia masih jarang ditemukan (opini pribadi). Oleh karena itu tutorial tentang WebGL ini dibuat untuk anda yang ingin mempelajari WebGL.
Tujuan
Tujuan dari tutorial ini adalah sebagai berikut:
- Pengenalan GLSL (OpenGL Shading Language)
- Menggambar titik pada canvas
Batasan Masalah
Penulis mengasumsikan pembaca telah mengetahui tentang HTML, CSS dan Javascript. Tutorial ini untuk pemula, akan tetapi terdapat perihal WebGL yang tidak akan dijelaskan oleh penulis, seperti:
- apa itu WebGL,
- sejarah WebGL,
- browser (dan versinya) yang support WebGL
- dll.
Persiapan
Tutorial ini menggunakan tiga file, masing-masing adalah index.html, style.css dan script.js. Konten dalam index.html adalah sebagai berikut.
<!DOCTYPE html> <html> <head> <meta charset="utf8"/> <title>WebGL</title> <link rel="stylesheet" type="text/css" href="style.css"/> </head> <body> <canvas id="canvas-webgl"> Browser tidak support HTML5 Canvas Element. </canvas> <script type="text/javascript" src="script.js"></script> </body> </html>
konten file style.css adalah sebagi berikut
#canvas-webgl { display: block; margin: auto; border: thin solid red;
background-color: black; }
(function() { // nothing to do here yet })();
untuk kode javascript akan diletakan didalam self-execution function yang terdapat dalam file script.js. Border disekitar canvas saya tambahkan hanya untuk mempermudah melihat keberadaan canvas tersebut.
Programmable Pipeline
WebGL bersifat hardware accelerated dan spesifikasinya berdasar pada OpenGL ES 2.0. Bagian CPU diproses menggunakan javascript, sedangkan GPU menggunakan GLSL (OpenGL Shading Language). Programmable pipeline WebGL (sama dengan OpenGL ES 2.0) adalah sebagai berikut.
Secara garis besar, masing-masing tahap pada pipeline diatas adalah sebagi berikut (beberapa saya sendiri kurang yakin).
- API (CPU) merupakan API WebGL
- Vertex Buffer Object (CPU) adalah data vertex yang akan dikirim ke GPU
- Promitive Processing (CPU) adalah segala proses yang dituliskan melalui javascript
- Vertex Shader (GPU) merupakan shader yang memproses setiap vertex
- Primitive Assembly (GPU) itu seperti pengelompokan data vertex-vertex untuk dibentuk menjadi titik, garis atau poligon (hanya segitiga)
- Rasterizer (GPU) adalah pembentukan pixel
- Fragment Shader (GPU) adalah tempat pewarnaan pixel.
- Depth Stencil (GPU) merupakan proses depth test dan stencil test.
- Colour Buffer Blend (GPU) adalah proses blending warna dari pixel-pixel yang tersisa.
- Dither (kurang tau ini apaan)
- Frame Buffer (GPU) adalah tempat gambar 2D hasil. Jika Framebuffer tidak ada, maka hasil akan digambar pada canvas
Vertex dan fragment shader adalah bagian yang harus anda definisikan menggunakan GLSL.
WebGL = Javascript + GLSL
Secara garis besar, Pemograman WebGL dilakukan dengan mengatur data dari CPU ke GPU. Konstruksi data dilakukan di CPU menggunakan bahasa Javascript. Data kemudian dikirim ke GPU dan diproses menggunakan bahasa GLSL pada bagian vertex shader dan fragment shader. Output dari vertex shader adalah posisi titik-titik tiga dimensi dalam clip space dan ukuran pixel untuk setiap titik, sedangkan fragment shader adalah warna untuk setiap pixel.
Inisiasi
var canvas = document.getElementById('canvas-webgl'); try { var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); } catch(err) { // silahkan kalo mau lakukan debuging disini... } if(!gl) { // ada masalah, entah tidak support WebGL atau masalah lainnya... alert('Inisiasi WebGL gagal'); return; }
<script id="shader-vertex" type="x-shader/x-vertex"> attribute vec3 a_position; void main(void) { gl_Position = vec4(a_position, 1.0); gl_PointSize = 3.0; } </script> <script id="shader-fragment" type="x-shader/x-fragment"> void main(void) { gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); } </script>
Tujuan utama script diatas hanyalah mendapatkan string kode GLSL untuk kedua shader. Anda bisa menempatkan kedua shader tersebut dalam file yang kemudian kontennya diambil melalui AJAX.
Membuat Objek WebGLShader
Objek WebGLShader digunakan untuk memberi tau WebGL tentang kode GLSL yang akan digunakan. Input untuk membuat WebGLShader adalah jenis shader dan string source kode GLSL. Langkah-langkah pembuatan objek WebGLShader pada umumnya adalah sebagai berikut.
function createShader = function(shaderSource, shaderType) { // membuat objek WebGLShader dengan input type shader var glShader = gl.createShader(shaderType); // menentukan kode GLSL untuk shader gl.shaderSource(glShader, shaderSource); // kompilasi shader gl.compileShader(glShader); // periksa status kompilasi shader // jika kompilasi berhasil, lanjut ke langkah 8 if (!gl.getShaderParameter(glShader, gl.COMPILE_STATUS)) { // kompilasi gagal. Dapatkan log info shader var infoLog = gl.getShaderInfoLog(glShader); // hapus shader gl.deleteShader(glShader); glShader = null; // lempar error throw Error('Error compile shader\r\n' + infoLog); } // kembalikan objek WebGLShader return glShader; }
Jenis shader hanya ada dua, yaitu vertex shader dan fragment shader. Nilai untuk jenis shader dapat diakses dari properti objek WebGL, yaitu VERTEX_SHADER dan FRAGMENT_SHADER, sehingga pembuatan shader dapat dilakukan dengan cara dibawah ini.
// string kode source untuk vertex shader var vshSrc = document.getElementById('shader-vertex').textContent; // string kode source untuk fragment shader var fshSrc = document.getElementById('shader-fragment').textContent; // WebGLShader untuk vertex shader var glVertexShader = createShader(vshSrc, gl.VERTEX_SHADER); // WebGLShader untuk fragment shader var glFragmentShader = createShader(fshSrc, gl.FRAGMENT_SHADER);
Kedua shader telah selesai dibuat, untuk menggunakan kedua shader tersebut, perlu dibuat objek baru yaitu WebGLProgram.
Membuat WebGLProgram
Objek WebGLProgram digunakan untuk menjembatani CPU dan GPU. WebGLProgram menentukan shader mana yang digunakan untuk vertex shader dan fragment shader. Pembuatan WebGLProgram dapat dilakukan dengan cara berikut.
// membuat objek WebGLProgram var glProgram = gl.createProgram(); // menentukan vertex dan fragment shader yang akan digunakan gl.attachShader(glProgram, glVertexShader); gl.attachShader(glProgram, glFragmentShader);
Sebelum WebGLProgram digunakan, perlu dilakukan link terhadap WebGLProgram yang ter-attach vertex shader dan fragment shader. Ling WebGLProgram dapat gagal dengan beberapa sebab, salah satunya adalah vertex shader dan fragment shader tidak dapat dihubungkan (walaupun berbeda, vertex sahder dan fragment shader saling berhubungan dalam menjalankan proses di GPU). Link WebGLProgram dapat dilakukan seperti dibawah ini.
// link program gl.linkProgram(glProgram); // check status link if(!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) { // link gagal, dapatkan info log program var infoLog = gl.getProgramInfoLog(glProgram); // hapus program gl.deleteProgram(glProgram); glProgram = null; // lempar error throw Error('Could not initialize program\r\n' + infoLog); }
Vertex dan vertex shader
Vertex didefinisikan sebagai titik (point) dalam ruang 3D yang memiliki koordinat x, y, dan z. Akan tetapi dalam WebGL, pengertian vertex lebih dari sekedar titik. Vertex dapat didefinisikan sebagai sebuah objek, berupa titik, yang memiiki attibut/properti. Salah satu attribut adalah posisi, dan dapat memiliki atribut lain seperti warna dan surface normal.
Atribut vertex dalam vertex shader didefinisikan menggunakan keyword attribute. Dalam tutorial ini, vertex hanya memiliki satu atribut yaitu "a_position" yang merupakan posisi vertex dalam ruang 3D.
Data Vertex
Data vertex dibuat di CPU dan dikirim ke GPU. Untuk mengirim data ke GPU, data perlu "dipaket" dalam vertex buffer. Setting data dalam vertex buffer dilakukan dengan menggunakan ArrayBuffer atau ArrayBufferView. Terdapat 8 jenis ArrayBufferView yaitu Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array. Tutorial kali ini menggunakan 4 vertex dimana koordinat posisi (x, y, z) masing-masing vertex adalah sebagai berikut.
- ( 0.5, 0.5, 0)
- ( 0.5, -0.5, 0)
- (-0.5, -0.5, 0)
- (-0.5, 0.5, 0)
ArrayBufferView yang digunakan sebagai data posisi vertex adalah Float32Array, karena data koordinat x, y dan z berupa tipe data float. Data harus diletakkan secara "rata" dalam elemen-elemen ArrayBufferView. Data vertex untuk keempat vertex tersebut dapat ditulis seperti kode berikut.
var dataVertex = new Float32Array([ //x , y , z 0.5, 0.5, 0, // kanan atas 0.5, -0.5, 0, // kanan bawah -0.5, -0.5, 0, // kiri bawah -0.5, 0.5, 0 // kiri atas ]); var verticesCount = 4;
Variabel verticesCount akan digunakan dalam proses menggambar untuk memberi tau berapa jumlah vertex dalam data.
Data vertex kemudian di-bind kedalam vertex buffer mengguanakn WebGLBuffer. Proses setting vertex buffer berserta datanya adalah sebbagai berikut.
// membuat objeck WebGLBuffer var vertexBuffer = gl.createBuffer(); // bind objeck WebGLBuffer gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // setting data WebGLBuffer menggunakan ArrayBufferView gl.bufferData(gl.ARRAY_BUFFER, dataVertex, gl.STATIC_DRAW);
Render
Dalam tutorial kali ini, proses render diawali dengan menggunakan WebGLProgram, seperti pada kode berikut.
// menggunakan WebGLProgram gl.useProgram(glProgram);
Tutorial kali ini hanya menggunakan satu WebGLProgram, akan tetapi, dalam apikasi WebGL yang kompleks, terkadang harus membuat lebih dari satu vertex shader atau fragment shader, sehingga perlu juga membuat lebih dari satu WebGLProgram untuk masing-masing shader.
Setelah menentukan WebGLProgram yang digunakan, langkah berikutnya adalah setting pointer vertex attribut dengan data vertex yang berada dalam vertex buffer.
// index lokasi atribut "a_position" dalam WebGLProgram var attribPositionIndx = gl.getAttribLocation(glProgram, 'a_position'); // ukuran atribut, 3 element untuk data posisi var posSize = 3; // type data posisi var posType = gl.FLOAT; // normalize posisi. selalu false untuk float var posNormalized = false; // setting pointer gl.vertexAttribPointer(attribPositionIndx, posSize, posType, posNormalized, 0, 0);
Dalam kode diatas, pointer disetting terhadap atribut "a_position" dari glProgram. Settingtersebut menggunakan index attribPositionIndx. variabel posSize digunakan untuk memberitahukan WebGL bahwa data untuk "a_position" diambi dari 3 element dalam vertex buffer. Dua argument terakhir dengan nilai 0 adalah stride dan offset. Untuk tutorial kali ini, vertex buffer hanya memuat data untuk satu vertex atribut, sehingga kedua parameter tersebut disetting 0.
Setelah setting pointer dilakukan, maka proses menggambar (draw) dapat dilakukan. Proses menggambar dapat dilakukan dengan cara berikut.
// enable index lokasi atribut "a_position" gl.enableVertexAttribArray(attribPositionIndx); gl.drawArrays(gl.POINT, 0, verticesCount);
Proses menggambar tersebut didahului dengan meng-enable atribut vertex menggunakan index lokasi atribut dalam WebGLProgram. Setelah itu proses menggambar dilakukan dengan mode point, offset data dimuai dari 0 (nol) dan jumlah vertex yang digambar adalah 4 (variabel verticesCount). Hasil render adalah seperti dibawah ini.
Nah.. ternyata hasilnya hanya 4 buah titik warna hijau yang tidak menarik.. hahahah
well.. untuk sekarang itu saja dulu, artikel ini mungkin masih akan di update, karena masih banyak hal kurang dijelaskan dalam ruang lingkup tutorial ini.
Sekian dulu ya.. Thanks for reading...