Saturday, April 16, 2016

How to Create 3D Rubik Simulator in OpenGL - Get Started


Dengan membaca post ini, kalian akan menghemat waktu minimal 2 jam untuk googling apa fungsi yang diperlukan untuk membuat Rubik Simulator di OpenGL karena post ini udah diringkas secara spesifik khusus untuk tujuan membuat Rubik di OpenGL. So, ini pilihan kamu mau melanjutkan baca post ini secara intensif atau googling sendiri :p

Kita asumsikan dulu kita akan buat Rubik Simulator ini di platform mana. Saya menggunakan OpenGL default di Ubuntu 14.04. Asumsikan bahasa yang dipakai adalah C++.



Pertama-tama kita harus mengerti bagaimana cara kerja OpenGL.

Download kode ini terlebih dahulu untuk lebih mengerti bagaimana cara kerja OpenGL karena kita akan ngobrolin kode ini lebih lanjut. Jangan lupa untuk membaca komentar-komentarnya karena itu perlu :)

nehe.gamedev.net/data/lessons/linux/lesson04.tar.gz

Coba jalankan kode tersebut, dan amati hasilnya. Lebih baik lagi kalau kalian coba-coba modifikasi beberapa parameter fungsi dan 'bereksperimen'. Artinya ganti parameter kode tersebut, amati hasilnya, ganti lagi, amati hasilnya lagi, dst. Metode tersebut bakal lebih ampuh daripada baca tutorial ini :))

Kemudian perhatikan kode berikut yang terletak pada int main() di file tersebut.

1
2
3
4
5
6
7
8
  /* Register the function to do all our OpenGL drawing. */
  glutDisplayFunc(&DrawGLScene);  

  /* Register the function called when the keyboard is pressed. */
  glutKeyboardFunc(&keyPressed);
  
  /* Start Event Processing Engine */  
  glutMainLoop();  

Kode di atas mendaftarkan fungsi DrawGLScene untuk menjadi fungsi yang akan dipanggil setiap frame dicetak. Misalkan dalam satu detik OpenGL melakukan pencetakan sebesar 60 kali, maka 60 kali pula fungsi tersebut dipanggil dalam satu detik.

Sementara fungsi keyPressed didaftarkan untuk menjadi listener kalau ada penekanan tombol di keyboard. Penting diketahui bahwa fungsi keyPressed dijalankan secara 'interrupt'. Artinya fungsi DrawGLScene yang biasanya dipanggil akan 'diserobot' oleh fungsi keyPressed. OpenGL akan menyelesaikan perintah dalam keyPressed hingga habis terlebih dahulu, baru nanti kemudian memanggil DrawGLScene lagi.

Sekarang yang kita lakukan cukup memodifikasi fungsi DrawGLScene saja.

Berikut ini adalah template kode DrawGLScene yang kira-kira paling penting dan harus ada.

1
2
3
4
5
6
7
8
9
/* The main drawing function. */
void DrawGLScene()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // Clear The Screen And The Depth Buffer
  
  /* Write your code here */
  
  glutSwapBuffers();
}

Nah, yang kita lakukan cukup memasukkan kode untuk menggambarkan rubik ke block /* Write your code here */ di atas.

Emang apa sih yang bakal kita butuhkan untuk menggambar rubik?

1. Gimana cara mencetak polygon.
2. Gimana cara melakukan rotasi polygon.
3. Gimana cara melakukan rotasi polygon, sementara bagian lain nggak ikut berotasi.
4. Gimana cara melakukan rotasi polygon dengan titik pusat semau gue.
5. Gimana cara memindah 'view kamera' sehingga seakan-akan kita melihat dari posisi lain.
6. Gimana cara merespon tekanan keyboard.

Cukup tahu 6 'gimana' di atas, dan kita akan bisa membuat Rubik di OpenGL! =))


1. Mencetak Polygon

Easy. Tinggal tambah kode berikut

1
2
3
4
5
6
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon

Mau polygonnya berwarna? Tinggal kasih warna.

1
2
3
4
5
6
7
  glColor3f(0.5f,0.5f,1.0f);   // set color to a blue shade.
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon

Oiya, ini maksud parameter dari kode di atas.

1
void glVertex3f(float x, float y, float z);

Artinya menambah titik polygon di posisi x, y, z.

1
void glColor3f(float r, float g, float b);

Artinya memberi warna (r, g, b) pada seluruh glVertex3f setelah glColor3f dipanggil. (r, g, b) bernilai pecahan antara 0 hingga 1.

 
2. Melakukan rotasi polygon

Tinggal kasih kode berikut.

1
2
3
4
5
6
7
8
  glRotatef(angle,1.0f,0.0f,0.0f);  // Rotate The Quad On The X axis 
  glColor3f(0.5f,0.5f,1.0f);   // set color to a blue shade.
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon


1
  void glRotatef(float angle, float sumbuX, float sumbuY, float sumbuZ);

Fungsi di atas melakukan rotasi sebanyak angle terhadap sumbu yang kita deklarasikan.
Kalau kita pengen dia berotasi terhadap sumbuX, maka kasih parameter 1, parameter sumbu lain 0. Kalau kita pengen dia berotasi terhadap sumbuX dan sumbuZ bersamaan, maka kasih mereka berdua parameter 1, sumbuY-nya 0. dst.

Angle dalam satuan derajat, sumbuX/sumbuY/sumbuZ bernilai pecahan antara range 0 sampai 1.

Seluruh fungsi glVertex3f yang berada di bawah fungsi glRotatef akan dirotasikan.

Tapi ingat! Rotasi ini bersifat statis. Artinya jika kita tidak mengubah nilai angle sama sekali, maka dia hanya merotasi sekali saja, nggak bakal timbul efek berputar. Bagaimana jika kita pengen ada efek berputar? Tambah nilai angle setiap saat setelah fungsi tersebut selesai.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  glRotatef(angle,1.0f,0.0f,0.0f);  // Rotate The Quad On The X axis 
  // draw a square (quadrilateral)
  glColor3f(0.5f,0.5f,1.0f);   // set color to a blue shade.
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon
  angle += 15;

Setiap saat setelah gambar di-render, nilai angle akan bertambah sebesar 15. Artinya, setiap render, kita akan mengubah sudut angle sebesar 15 derajat. Otomatis efek 'seperti berputar' nanti akan muncul.


3. Melakukan rotasi polygon, sementara bagian lain nggak ikut berotasi.

Tambahkan glLoadIdentity() untuk membuat dua blok kode supaya independen (tidak mempengaruhi satu sama lain. Artinya, apabila kita melakukan rotasi pada salah satu blok kode, blok kode lain tidak akan terpengaruh rotasi lagi.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  glLoadIdentity();    // Reset The View
  glTranslatef(-1.5f,0.0f,-6.0f);  // Move Left 1.5 Units And Into The Screen 6.0
  
  // draw a triangle (in smooth coloring mode)
  glBegin(GL_POLYGON);    // start drawing a polygon
  glColor3f(1.0f,0.0f,0.0f);   // Set The Color To Red
  glVertex3f( 0.0f, 1.0f, 0.0f);  // Top
  glColor3f(0.0f,1.0f,0.0f);   // Set The Color To Green
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glColor3f(0.0f,0.0f,1.0f);   // Set The Color To Blue
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // we're done with the polygon (smooth color interpolation)

  glLoadIdentity();    // make sure we're no longer rotated.
  glTranslatef(1.5f,0.0f,-6.0f);  // Move Right 3 Units, and back into the screen 6.0
 
  glRotatef(angle,1.0f,0.0f,0.0f);  // Rotate The Quad On The X axis 
  // draw a square (quadrilateral)
  glColor3f(0.5f,0.5f,1.0f);   // set color to a blue shade.
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon
  angle += 15;


4. Melakukan rotasi polygon dengan titik pusat semau gue.

Manfaatkan fungsi glTranslate(float x, float, y, float z) untuk modifikasi titik pusat polygon secara custom. Perhatikan kode pendek berikut for better understanding.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  glLoadIdentity();    
  glTranslatef(1.5f,0.0f,-6.0f);  // Move Right 3 Units, and back into the screen 6.0
 
  glTranslate(0, 1, 0);             // kita pindah pointer saat ini ke atas
  glRotatef(angle,1.0f,0.0f,0.0f);  // numpang naruh 'titik pusat rotasi'
  glTranslate(0, -1, 0);            // kita balik lagi ke tempat semula, untuk menggambar polygon
  
  glColor3f(0.5f,0.5f,1.0f);
  glBegin(GL_QUADS);
  glVertex3f(-1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f,-1.0f, 0.0f);
  glVertex3f(-1.0f,-1.0f, 0.0f); 
  glEnd();
  angle += 15;

Kalo kalian coba run kode tersebut, maka polygon akan berputar dengan pusat rotasi di atas persegi.

Misalkan kalian ingin polygon berputar dengan pusat di bawah persegi. Coba lihat kode di bawah, dan bandingkan dengan kode di atas barusan.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  glLoadIdentity();    
  glTranslatef(1.5f,0.0f,-6.0f);  // Move Right 3 Units, and back into the screen 6.0
 
  glTranslate(0, -1, 0);            // kita pindah pointer saat ini ke bawah
  glRotatef(angle,1.0f,0.0f,0.0f);  // numpang naruh 'titik pusat rotasi'
  glTranslate(0, 1, 0);             // kita balik lagi ke tempat semula, untuk menggambar polygon
  
  glColor3f(0.5f,0.5f,1.0f);
  glBegin(GL_QUADS);
  glVertex3f(-1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f,-1.0f, 0.0f);
  glVertex3f(-1.0f,-1.0f, 0.0f); 
  glEnd();
  angle += 15;

Dengan ini, maka pusat sumbu rotasi bisa diubah-ubah semau kita. Intinya, manfaatkan fungsi glTranslate untuk numpang naruh titik pusat rotasi.


5. Memindah 'view kamera' sehingga seakan-akan kita melihat dari posisi lain.

Gunakan fungsi glLookAt dengan parameter ini.

1
2
3
void glLookAt(float fromX, float fromY, float fromZ,
              float toX, float toY, float toZ,
              0, 1, 0);

'from' adalah titik (x, y, z) di mana kamera berada. 'to' adalah titik (x, y, z) ke mana kamera akan melihat. Letakkan fungsi glLookAt sebelum menggambar polygon menggunakan glVertex3f.

Contoh kode berikut :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  glLoadIdentity();    
  glTranslatef(1.5f,0.0f,-6.0f);  // Move Right 3 Units, and back into the screen 6.0

  glLookAt(5, 5, 5,
           1, 1, 0,
           0, 1, 0);    // from = (5, 5, 5); to = (1, 1, 0)
  
  glColor3f(0.5f,0.5f,1.0f);
  glBegin(GL_QUADS);
  glVertex3f(-1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f, 1.0f, 0.0f);
  glVertex3f( 1.0f,-1.0f, 0.0f);
  glVertex3f(-1.0f,-1.0f, 0.0f); 
  glEnd();
  angle += 15;

Untuk lebih mengerti tentang glLookAt, akan sangat baik sekali kalo kalian mencoba-coba ganti parameter glLookAt dan jalankan kodenya supaya paham betul perilaku glLookAt seperti apa :)

 
6. Merespon tekanan keyboard.

Kita perlu membuat animasi rotasi rubik ketika ada tekanan dari keyboard. Yang kita bisa pahami adalah user menekan tombol keyboard, lalu rubik berputar 90 derajat. Ini bisa diakali dengan meng-handle variabel angle dari fungsi keyPressed.

Bagaimana caranya? Perhatikan kode berikut. Kita akan lakukan modifikasi di fungsi keyPressed untuk mengatur variabel angle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
float angle = 0;

void DrawGLScene() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // Clear The Screen And The Depth Buffer
  glLoadIdentity();    // Reset The View
  glTranslatef(-1.5f,0.0f,-6.0f);  // Move Left 1.5 Units And Into The Screen 6.0
  
  glRotatef(angle,1.0f,0.0f,0.0f);  // Rotate The Quad On The X axis 
  glColor3f(0.5f,0.5f,1.0f);   // set color to a blue shade.
  glBegin(GL_QUADS);    // start drawing a polygon (4 sided)
  glVertex3f(-1.0f, 1.0f, 0.0f);  // Top Left
  glVertex3f( 1.0f, 1.0f, 0.0f);  // Top Right
  glVertex3f( 1.0f,-1.0f, 0.0f);  // Bottom Right
  glVertex3f(-1.0f,-1.0f, 0.0f);  // Bottom Left 
  glEnd();     // done with the polygon
}

/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) 
{
  /* sleep to avoid thrashing this procedure */
  usleep(100);

  if(key == 'p') {
    int goal = 90;
    int degree_per_frame = 5;
    int frame = goal / degree_per_frame;
    for(int i = 0; i < frame; i++) {
      angle += degree_per_frame;
      DrawGLScene();
    }
  }
}



Pada kode di atas, kita berhasil memutar persegi sebesar 90 derajat ketika tombol 'p' ditekan. Persegi akan terlihat seperti berputar.

Variabel angle akan bertambah 5 derajat setiap frame dicetak. Dan perhatikan bahwa fungsi keyPressed akan dipanggil langsung secara interrupt ketika ada tekanan keyboard. Fungsi keyPressed akan dieksekusi hingga selesai terlebih dahulu baru OpenGL memanggil fungsi DrawGLScene() seperti biasa.


Tahu 6 benda di atas ternyata cukup lho untuk bikin Rubik Simulator. Good luck, selamat mencoba dan mengeksplorasi lebih lanjut!

Cukup sekian. Semoga bermanfaat! Sorry for the long post. Here is a lovely potato for you.


Related Articles

14 comments: