Memulai WebGL

Setelah sebelumnya kita berkenalan sedikit dengan WebGL, mari kita mulai membuat aplikasi WebGL. Pastikan dulu browser yang Anda gunakan mendukung WebGL agar Anda dapat melihat hasil yang diharapkan.

Jika semua sudah siap, mari kita buka teks editor dan masukkan kode berikut ini.

<canvas id="canvas" width="640" height="480">WebGL tidak didukung</canvas>

Kode di atas akan membuat sebuah area gambar yang akan dijadikan tempat menggambar gambar 3 dimensi dengan WebGL.

Persiapan dari sisi HTML sudah cukup, sekarang mari beralih ke dunia Javascript untuk mengendalikan WebGL.

Pertama, dapatkan referensi ke area gambar <canvas> yang kita buat sebelumnya.

<script type="text/javascript">

var canvas = document.getElementById('canvas');

Setelah itu, ambil pengendali WebGL dari area gambar tadi.

var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

Mengapa mencoba mengambil dua hal berbeda (webgl dan experimental-webgl)? Sampai saat ini, WebGL masih bersifat eksperimental sehingga beberapa browser masih belum menjadikannya hal yang “udah pasti ada”. Maka dari itu, browser2 ini menggunakan nama experimental-webgl sebagai identifikasi pengendali WebGL.

Nilai yang dihasilkan dapat kita gunakan untuk mengecek apakah browser yang dipakai mendukung WebGL atau tidak.

if (!gl) {
  alert('WebGL tidak didukung');
}

Jika nilai keluaran tidak dievaluasi menjadi true, maka bisa diambil kesimpulan si browser tidak mendukung WebGL.

Setelah mendapatkan referensi ke pengendali WebGL, kita sudah bisa memanggil fungsi-fungsi WebGL. Sebagai contoh, fungsi untuk menghapus area gambar dengan warna tertentu.

if (gl) {
  // hapus layar
  gl.clearColor(1.0, 0.5, 0.0, 1.0); // warna oranye
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

</script>

Jika sukses, area gambar yang dibuat seharusnya akan berwarna oranye seperti berikut.

Memulai WebGL

Selamat, aplikasi WebGL super sederhana sudah jadi! 😀

Kode lengkap dari tutorial kali ini dapat dilihat di http://fajran.github.io/webgl-tutorial/01-memulai-webgl/index.html. Seluruh kode sumber tutorial WebGL ini akan tersedia di https://github.com/fajran/webgl-tutorial.

Pengenalan WebGL

Mari kita mulai ngoprek2 WebGL 😀 Mudah2an ini beneran menjadi satu set tutorial xD

Apa itu WebGL? Singkatnya WebGL adalah OpenGL yang hidup di dalam browser. OpenGL sendiri adalah satu set API yang dapat dipakai untuk membuat gambar 3 dimensi (termasuk juga 2 dimensi). Proses penggambaran yang menggunakan OpenGL biasanya akan “dibantu” dengan perangkat keras grafis (GPU) sehingga prosesnya akan jauh lebih cepat.

Kalau OpenGL aslinya adalah satu set API untuk bahasa C, maka WebGL, berhubung dia hidup di dalam browser, adalah API untuk Javascript. WebGL ini merupakan kelanjutan dari Canvas API yang hanya menyediakan fasilitas menggambar gambar 2 dimensi.

Sampai saat ini, belum semua browser mendukung WebGL. Dukungan yang disediakan juga sering bergantung dengan GPU dan driver yang dipakai. Hal ini tidak selalu berarti pengguna GPU tipe lama tidak bisa menikmati WebGL karena implementasi prosedur gambar 3 dimensi bisa saja dilakukan tanpa bantuan perangkat keras, walau efeknya proses akan berjalan lebih lambat. Pendekatan ini diambil oleh Chromium/Google Chrome yang juga menyediakan implementasi WebGL yang hanya menggunakan peranti lunak.

Untuk mencari tahu apakah browser yang Anda pakai saat ini dapat menampilkan WebGL atau tidak, kunjungi saja halaman berikut ini.

http://get.webgl.org/

Jika WebGL didukung, Anda akan menjumpai tampilan seperti berikut. Perhatikan juga ada sebuah animasi kubus berputar di halaman tersebut.

Screen Shot 2013-05-28 at 8.32.57 PM

Apakah browser yang Anda gunakan mendukung WebGL?

Backup Blog

Selain menggabung sekian blog jadi satu, kemarin saya juga memindahkan tempat si blog berada. Sebelumnya, 3 dari 4 blog yang saya gabung ada di Dreamhost namun blog ini ada di sebuah VPS mungil murah meriah 😀 Satu hal yang jujur saja saya khawatirkan adalah masalah realibilitas dari si VPS sehingga kali ini saya memikirkan mengenai backup. Sebelumnya saya cukup percaya sama om Google dan Dreamhost tuk menjaga data saya.

Setelah memikirkan beberapa strategi backup, akhirnya saya memilih tuk membackup data saya ke layanan lain yang mudah2an bisa saya percayai: BitBucket! 😀

Ada dua hal dari blog ini yang saya rasa perlu saya backup: database yang dipakai dan juga instalasi WordPress (termasuk berkas-berkas yang saya upload via WordPress). Strategi backup saya adalah dengan memasukkan semuanya ke dalam repositori Git dan mengirimnya ke BitBucket. Untung saja BitBucket menyediakan repositori privat yang gratis dan dapat memiliki kapasitas tak terbatas. Klop!

Strategi sudah dipilih, sekarang saatnya membuat langkah konkrit. Untuk database, saya dapat menjalankan mysqldump agar isi database disimpan ke berkas. Saya juga menggunakan opsi --skip-extended-insert agar satu buah record ditulis menjadi satu baris. Andai opsi ini tidak dimasukkan, maka satu baris dalam berkas SQL yang dihasilkan dapat mengandung banyak data sekaligus. Dengan membuat satu entry satu baris, ukuran berkas SQL yang dihasilkan memang bertambah besar namun Git akan dapat melacak perubahan dengan lebih efisien. Untuk berkas instalasi, tidak ada perlakuan khusus agar Git dapat memasukkan semuanya dengan baik.

Setelah coba-coba, inilah kira2 skrip yang saya pakai. Saya jalankan skrip berikut di direktori induk dari direktori instalasi WordPress, secara berkala melalui crontab.

#!/bin/sh

set -x

cd `dirname $0`

mysqldump -h HOST -u USER --password=PASSWORD \
          --skip-extended-insert DATABASE > data.sql

git add data.sql
git commit -m "Database backup"

git add www
git commit -m "WordPress backup"

git push origin master

Tentunya sebelum ini saya sudah persiapkan direktori kerja lokal Git dan juga repositori Git di BitBucket. Kunci SSH tanpa password juga saya gunakan agar operasi push dapat bekerja di belakang layar.

Improvisasi

Salah satu fitur yang ada di OS X adalah penyuaraan tulisan alias “Text to Speech”. OS X menyediakan sebuah aplikasi command line yang memungkinkan kita untuk meyuarakan tulisan apa saja yang kita masukkan

$ say "How are you?"

Dari sekian penyuara yang tersedia, salah satunya adalah Damayanti yang bisa menyuarakan Bahasa Indonesia

$ say -v Damayanti "Apa kabar?"

Ternyata si Damayanti ini bisa berimprovisasi menambahkan kata yang tidak kita tuliskan di sana. Coba saja jalankan perintah berikut.

$ say -v Damayanti "Sudah 5 hari aku berdiri selama 5 menit dan 5 detik setiap 5 jam"

Memulai OpenGL ES dengan SDL

Saya ingin membuat tulisan berseri mengenai OpenGL dan inilah tulisan pertamanya 😀 mudah2an bener2 lanjut terus xD

Untuk masuk ke “dunia” OpenGL, hal pertama yang harus dibuat adalah sebuah tempat dimana kode OpenGL kita dapat bekerja. Salah satu caranya adalah dengan menggunakan SDL yang sudah sangat membantu untuk urusan membuat window, menangkap event (mouse, keyboard), dan juga membuat dunia OpenGL kosong yang siap diisi.

Berhubung sekarang dunia mobile sedang sangat meriah, saya juga akan menggunakan standar OpenGL ES yang untungnya juga dapat dijalankan di atas komputer desktop. Untuk bahasa pemrograman, saya akan menggunakan C++ dengan harapan kode akan dapat dengan mudah dipindahkan ke platform lain seperti Android maupun iOS.

Pastikan pustaka pengembangan SDL dan OpenGL ES sudah terpasang. Bagi pengguna keluarga Debian, pasang paket libsdl1.2-dev dan libgles2-mesa-dev.

$ sudo apt-get install libsdl1.2-dev libgles2-mesa-dev

Mari kita mulai..

Pertama-tama, inilah kode pembuka kita 😀

#include <SDL/SDL.h>

int main(int argc, char** argv) {
  const int width = 1024;
  const int height = 768;
  const int depth = 32;

  // Prepare SDL
  SDL_Init(SDL_INIT_EVERYTHING);
  SDL_SetVideoMode(width, height, depth,
                   SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL);

  SDL_Event event;

  bool running = true;
  while (running) {
    // Check for incoming event
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_KEYUP &&
          event.key.keysym.sym == SDLK_ESCAPE) {
        running = false;
      }
    }

    // Swap buffer
    SDL_GL_SwapBuffers();
  }

  SDL_Quit();

  return 0;
}

Simpan ke dalam berkas, katakanlah dengan nama 01-begin.cc, lalu kompilasi dengan perintah berikut:

$ g++ -o 01-begin 01-begin.cc `sdl-config --libs --cflags`

Jika aplikasi ini dijalankan, akan muncul sebuah window dengan isi “acak”. Kita belum memerintahkan penggambaran apa2 sehingga isi window dapat berisi apa saja :D. Berikut ini yang saya dapatkan sekarang.

$ ./01-begin

Untuk mengakhiri, tekan tombol escape.

Kode di atas dapat didapat juga di https://github.com/fajran/opengles-tutorial/tree/master/01-begin

Setelah kerangka dasar siap, mari kita mulai menyentuh OpenGL ES. Agar fungsi-fungsi OpenGL ES dapat kita gunakan, maka pastikan kita sudah mengimpor header dari OpenGL ES

#include <GLES2/gl2.h>

Setelah itu kita siapkan 3 buah fungsi “utama” yang digunakan untuk menyiapkan OpenGL ES, untuk memperbarui “kondisi” OpenGL ES, dan terakhir untuk menggambar ke layar.

void init_opengl() {
  glClearColor(0xCD / 255.0, 0xD7 / 255.0, 0xB6 / 255.0, 1.0);
}

void update_opengl() {

}

void draw_opengl() {
  glClear(GL_COLOR_BUFFER_BIT);
}

Lalu kita selipkan pemanggilan 3 fungsi di atas dalam kode yang pertama kita buat. Fungsi pertama dipanggil setelah SDL siap dipakai. Lalu fungsi kedua dan ketiga dipanggil di dalam “main loop” yang akan menggambar setiap frame yang akan kita buat. Kode akhir dapat dilihat di GitHub.

Mari kita bahas sedikit apa yang baru saja ditambahkan.

Pertama, pemanggilan fungsi glClearColor digunakan untuk mengatur warna yang akan digunakan saat layar kita “hapus”. Pada contoh di atas, saya menggunakan warna hijau muda. Berhubung saya cuma tahu kode heksadesimalnya (rentang 0-255) dan fungsi tersebut meminta kode warna dalam tipe float dengan rentang 0.0 sampai 1.0, maka saya konversi dulu nilai warnanya.

Lalu untuk setiap frame yang digambar, saya perintahkan OpenGL untuk menghapus layar dengan memanggil fungsi glClear dengan memberikan paramter GL_COLOR_BUFFER_BIT agar buffer warna yang menampung gambar dihapus dan diisi dengan warna yang saya set sebelumnya.

Mari kita lihat hasilnya sekarang!

$ g++ -o 02-background 02-background.cc `sdl-config --libs --cflags` -lGLESv2
$ ./02-background

Sekian dulu tulisan pembuka ini. Untuk selanjutnya kita akan mencoba sesuatu yang bernama shader!

Semua kode yang akan saya buat dalam seri tulisan ini tersedia juga di https://github.com/fajran/opengles-tutorial dan silakan dipakai sebebas-bebasnya karena semua kode yang terlibat saya rilis dalam domain publik alias public domain.

Video transparan dengan shader

Main-main dengan OpenGL shader memang mengasikkan 😀 Katakanlah saya punya sebuah video yang ingin saya tampilkan dalam OpenGL scene. Saya bisa memakai ffmpeg tuk mengekstrak setiap frame yang ada di dalamnya. Frame ini lalu diubah menjadi OpenGL texture sehingga dapat ditampilkan dalam OpenGL scene. GPU dan drivernya zaman sekarang sudah mendukung NPOT Texture alias tekstur yang ukurannya tidak perlu angka dari dua pangkat sekian sehingga setiap frame dari video bisa langsung begitu saja digunakan tanpa harus diubah ukurannya terlebih dahulu.

Kalau cuma menampilkan yang seperti ini, vertex dan fragment shader berikut sudah cukup tuk digunakan. Katakanlah mpv berisi model-view-projection matrix, coord adalah koordinat kotak video (range 0.0-1.0), textureId adalah (tentunya) texture id dari video frame.

attribute vec3 coord;
uniform mat4 mpv;
varying vec2 texcoord;

void main() {
   gl_Position = mpv * vec4(coord, 1.0);
   texcoord = coord;
}
varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    gl_FragColor = texture2D(textureId, texcoord);
}

Beberapa hari belakangan saya mencari2 cara tuk menampilkan video yang transparan. Satu teknik yang terpikirkan langsung oleh saya adalah dengan menggunakan Chroma Key sehingga saya cukup mencari warna tertentu dan menghapusnya agar tidak ditampilkan. Namun kalau pakai cara ini, bagaimana menampilkan gambar yang tampil/hilang secara bertahap (err.. fade-in/out)?

Setelah merenung lebih lanjut, saya jadi terpikir kalau saya punya akses ke alpha channel saya bisa menggunakan nilai si alpha untuk membuat warna RGB menjadi transparan. Namun apa artinya si video harus menampung gambar dengan channel RGBA? apa format kompresi yang banyak dipakai (spesifiknya h264) itu mendukung RGBA? Untung saja saya menemukan sebuah pertanyaan di StackOverflow yang menjawab pertanyaan saya ini 😀

Intinya, video diubah menjadi seperti berikut ini.

Bagian atas video berisi channel RGB dan bagian bawah video adalah alpha masknya. Kalau sudah begini, si video sendiri bisa dikompresi dengan format apapun karena pada dasarnya si video hanya berisi RGB stream biasa. Namun kita perlu melakukan sedikit usaha tambahan pada saat menampilkannya: ambil setengah gambar atas dan setengah gambar bawah, ambil nilai RGB dari gambar atas, ambil nilai (katakanlah) R dari gambar bawah, gabungkan kedua nilai ini dg menjadikan nilai R dari gambar bawah sebagai nilai Alpha dari warna akhir.

Kalau kalimat terakhir di atas diubah menjadi fragment shader, hasilnya kira2 akan seperti berikut.

varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    vec2 tc_rgb = texcoord * vec2(1.0, 0.5);
    vec2 tc_alpha = tc_rgb + vec2(0.0, 0.5);
    vec4 frame = texture2D(textureId, tc_rgb)
    vec4 alpha = texture2D(textureId, tc_alpha);
    gl_FragColor = vec4(frame.rgb, alpha.r);
}

Voila! eiya, jangan lupa nyalakan GL_BLEND dan set blend function-nya agar alpha value-nya beneran terpakai.

Bongkar pasang berkas video

Contekan tuk bongkar pasang berkas video pakai ffmpeg

Ekstrak audio stream dan ubah formatnya menjadi AAC

$ ffmpeg -i input.avi -vn -ac 2 -ar 44100 -ab 196k -acodec libvo_aacenc audio.aac

Ekstrak frame dari video stream menjadi PNG sequence:

$ ffmpeg -i input.avi -r 30 -f image2 frame-%010d.png

Gabung sederet gambar PNG menjadi berkas video:

$ ffmpeg -f image2 -i frame-%010d.png -vcodec libx264 -b 128k -r 30 video.mp4

Gabung aliran video dan suara menjadi satu:

$ ffmpeg -i audio.aac -i video.mp4 -acodec copy -vcodec copy result.mp4