2D Graphics with OpenGL by Examples

October 15, 2017 0 By Nam Vu

I.    CƠ SỞ LÝ THUYẾT
OpenGL là giao diện phần mềm hướng thủ tục theo chuẩn công nghiệp hỗ trợ đồ hoạ 3 chiều. Được phát triển đầu tiên bởi Silicon Graphic Inc,bao gồm khoảng 250 câu lệnh được hỗ trợ bởi nhiều ngôn ngữ như C, C++,Java. Cho phép người lập trình sử dụng tạo ra các đối tượng đồ họa. OpenGL được thiết kế không phụ thuộc vào nền tảng phần cứng cũng như hệ điều hành máy tính (independence of hardware platform and operating system). Với OpenGL ta sẽ tạo ra các mô hình từ các đối tượng hình học cơ bản đó là điểm (point), đường (line) và đa giác (polygon). Cú pháp lệnh của OpenGL: Các câu lệnh của OpenGL đều sử dụng tiền tố gl và các từ tiếp theo được bắt đầu bằng kí tự hoa, ví dụ glClearColor(). Các hằng được định nghĩa bằng tiền tố GL_ tiếptheo là các từ viết hoa được ngăn cách bằng kí tự gạch dưới, ví dụ GL_COLOR_BUFFER_BIT. Mặc dù OpenGL là một công cụ mạnh nhưng các đối tượng vẽ đều là các đối tượng hình học cơ bản. Để đơn giản hóa một số công việc,chúng ta được trang bị thêm một số thư viện cho phép sử dụng các thủ tục vẽ ở mức cao hơn: – OpenGL Utility Library (GLU). Bao gồm một số thủ tục thiết lập ma trận xác định hướng nhìn (viewing orientation), ma trận các phép chiếu (projection),và biểu diễn các mặt trong không gian 3 chiều (redering surfaces) – OpenGL Utility Toolkit (GLUT): Là một bộ công cụ được viết bởi Mark Kilgard bao gồm các thủ tục giúp cho đơn giản hóa việc xây dựng các đối tượng hình học. Các thủ tục của GLUT được bắt đầu bằng tiền tố glut.
A.    CÁC LỆNH VẼ TRONG OPENGL
Ta có một số ví dụ đơn giản:

#include <gl\glut.h>
/* hàm thực hiện các thao tác vẽ theo yêu cầu của chương trình */ 
void display(void) 
{ 
    /* xóa mọi pixel */ 
    glClear (GL_COLOR_BUFFER_BIT); 
    /* vẽ hình chữ nhật có điểm trái-trên và phải-dưới */
    /* (0.25, 0.25, 0.0) and (0.75, 0.75, 0.0) */ 
    glColor3f (1.0, 1.0, 1.0); /* thiết lập màu vẽ: màu trắng */ 
    glBegin(GL_POLYGON); /* bắt đầu vẽ đa giác */ 
    glVertex3f (0.25, 0.25, 0.0); /* xác định các đỉnh của đa giác */ 
    glVertex3f (0.75, 0.25, 0.0); 
    glVertex3f (0.75, 0.75, 0.0); 
    glVertex3f (0.25, 0.75, 0.0); 
    glEnd(); /* kết thúc vẽ đa giác */ 
    /* thực hiện quá trình đẩy ra buffer */ 
    glFlush (); 
}

Tất cả các hình khối trong openGL đều được nằm giữa hai dòng lệnh glBegin() và glEnd().
Có thể có nhiều cặp dòng lệnh như vậy, tức là ta có thể viết các hàm vẽ khác nhau và dùng cặp lệnh trên trong các hàm đó. Tham số của glBegin() là GL_LINE_LOOP có nghĩa là nó bảo window vẽ một đường khép kín, tức là điểm đầu trùng với điểm cuối.
Một số hàm vẽ như sau:
Hàm Ý nghĩa hàm

GL_POINT    Vẽ điểm
GL_LINE    Vẽ đường thẳng
GL_LINE_STRIP    Tập hợp của những đoạn thẳng được nối với nhau
GL_LINE_LOOP    Đường gấp khúp khép kín
GL_TRIANGLES    Vẽ hình tam giác
GL_QUADS    Vẽ hình tứ giác
GL_TRIANGLES_STRIP    Vẽ tập hợp các tam giác đều liền nhau,chung cạnh
GL_QUAD_STRIP    Vẽ tập hợp các tứ giác liền nhau,chung một cạnh
GL_TRIANGLE_FAN    Vẽ hình quạt


Ảnh minh họa cho các câu lệnh vẽ được sử dụng trong thư viện openGL
Hàm glVertex2d xác định điểm hai chiều, chúng ta nên biết một số tiền tố các hàm của openGL, các hàm dùng thư viện nào sẽ bắt đầu bằng tên của thư viện đó.
Ví dụ dùng các hàm cơ bản của openGL thì thường bắt đầu với gl, các hàm dùng thư viện glut thì bắt đầu với glu, các hàm dùng thư viện aux thì bắt đầu với aux… Các hàm cũng có hậu tố, ví dụ glVertex2d() là vẽ điểm 2 chiều, glVertex3d() là vẽ điểm 3 chiều.
B.    TÔ MÀU TRONG OPENGL
Hàm thiết lập màu cho hình vẽ: glColor3f(x,x,x)

Một số màu cơ bản:

glColor3f(0.0,0.0,0.0); //màu đen
glColor3f(1.0,1.0,1.0); //màu trắng
glColor3f(1.0,0.0,0.0); //màu đỏ
glColor3f(1.0,0.0,1.0); //màu tím
glColor3f(1.0,1.0,0.0); //màu vàng
glColor3f(0.0,1.0,0.0); //màu lục
glColor3f(0.0,1.0,1.0); //màu tím xanh
glColor3f(0.0,0.0,1.0); //màu xanh lơ

C.    CÁC CÂU LỆNH ĐIỀU KHIỂN
Câu lệnh điều khiển chuột:

void mouse(int button, int state, int x, int y) 
{
   switch (button) 
   {
      case GLUT_LEFT_BUTTON:  /* khi nhấn chuột trái */
        if (state == GLUT_DOWN)
          glutIdleFunc(spinDisplay); 
        /* khi chương trình đang trong trạng thái idle (không phải xử lý gì cả) thì sẽ thực hiện hàm spinDisplay */
     break;
     case GLUT_MIDDLE_BUTTON:  /* khi nhấn nút giữa */
        if (state == GLUT_DOWN)
          glutIdleFunc(NULL);
     break;
     default:
     break;
   }
}

Hàm xử lý từ bàn phím với các kí tự nhận biết được dưới mã ASCII:
void glutKeyboardFunc(void (*func) (unsigned char key, int x, int y))
Tham số (*func)(unsigned char key, int x, int y) – con trỏ tới hàm xử lý

x – tọa độ x của chuột (hoành độ)
y – tọa độ y của chuột (tung độ)

    Hàm xử lý các kí tự trên bàn phím nhận biết được dưới mã ASCII :
void glutSpecialFunc (void (*func) (int key, int x, int y))
Tham số tương tự glutKeyboardFunc().
Những key được định nghĩa sẵn là một trong số mã sau:

GLUT_KEY_F1 F1
GLUT_KEY_F2 F2

GLUT_KEY_F12 F12
GLUT_KEY_LEFT //phím chức năng trái
GLUT_KEY_RIGHT //phím chức năng phải
GLUT_KEY_UP //phím chức năng trên
GLUT_KEY_DOWN //phím chức năng dưới
GLUT_KEY_PAGE_UP //phím chức năng Page Up
GLUT_KEY_PAGE_DOWN //phím chức năng Page Down
GLUT_KEY_HOME //phím chức năng Home
GLUT_KEY_END //phím chức năng End
GLUT_KEY_INSERT //phím chức năng Insert

II.    SƠ ĐỒ CHÍNH VÀ MÔ TẢ CHƯƠNG TRÌNH
A.    SƠ ĐỒ CHÍNH CHƯƠNG TRÌNH

 

 B.    MÔ TẢ CHƯƠNG TRÌNH1

Bước 1: Khởi tạo

•    Tạo khung nhìn Form gồm : Chế độ nhập, Chế độ nhìn, Chế độ màu, Form size, Vị trí Form , Tiêu đề Form.
•    Khởi tạo Chế độ nhập Init().
Bước 2: Chức năng xử lý
•    Dùng hàm Display() để xử lý vẽ hình, tô màu, quay hình.

  • Khởi tạo màu nền và chế độ buffers.
  • Xử lý xoay hình theo bàn phím key = { “L”, “R”, “U”, “D”}

Gọi hàm SpinDisplay() thực hiện quay. Tăng góc quay sau mỗi lần thực hiện cho đến khi góc quay Spin lớn hơn 360 độ thì trả lại giá trị ban đầu cho Spin.

  • Xử lý góc nhìn và độ sau góc nhìn.
  • Xử lý vẽ Đa giác theo từng cạnh: Tô màu và vẽ theo trục không gian 3D.
  • Thực hiện hoán đổi Bufers.

•    Dùng hàm Reshape() để xử lý thay đổi kích thước khung Form.
•    Dùng hàm Keyboard() để xử lý bàn phím máy tính.

  • Trong trường hợp nhấn phím “L” thực hiện hàm quay trái.
  • Trong trường hợp nhấn phím “R” thực hiện hàm quay phải.
  • Trong trường hợp nhấn phím “U” thực hiện hàm quay lên.
  •  Trong trường hợp nhấn phím “D” thực hiện hàm quay xuống.

•    Dùng hàm Mouse() để xử lý chuột máy tính.

  • Trong trường hợp nhấn chuột trái thực hiện hàm quay trái.
  • Trong trường hợp nhấn chuột phải thực hiện hàm quay phải.
  • Trong trường hợp nhấn giữa không thực hiện.

•    Dùng hàm MainLoop() để dừng màn hình hiển thị.
III.    KẾT QUẢ
A.    VẼ HÌNH THANG CÂN


B.    VẼ MÚI KHẾ

C.    VẼ KHỐI LẬP PHƯƠNG

Khối lập phương xoay theo trục Ox

Khối lập phương xoay theo trục Oy

IV.    CODE CHƯƠNG TRÌNH
A.    VẼ HÌNH THANG CÂN

#include <gl\glut.h>  
void display(void) 
{
    /* Xóa window */   
    glClear(GL_COLOR_BUFFER_BIT);
    /* Vẽ đa giác */
    glBegin(GL_POLYGON); 
    glColor3f(1.9, 0.6, 0.6); // tô màu hình thang (màu hồng)
    glVertex2f(-40, -25); // tọa độ đỉnh
    glVertex2f(-25, 25); 
    glVertex2f(25, 25); 
    glVertex2f(40, -25); 
    glEnd();
    glFlush(); 
} 

void init() 
{ 
    glClearColor (0.0, 0.0, 0.0, 0.0); // tô nền màu đen
    glMatrixMode (GL_PROJECTION); 
    glLoadIdentity (); 
} 

int main(int argc, char** argv) 
{ 
    glutInit(&argc,argv); // gọi glutInit trước các lệnh khác
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500,500); // kích thước cửa sổ giao diện
    glutInitWindowPosition(0,0); // vị trí hiện cửa sổ giao diện
    glutCreateWindow("Nhom 8"); // tên cửa sổ giao diện
    glutDisplayFunc(display); // gọi hàm display
    init(); // khởi tạo
    glutMainLoop(); // lặp cho tới khi đóng ứng dụng
    return 0;
}

B.    VẼ HÌNH MÚI KHẾ

#include <gl\glut.h>
#include <gl\glu.h>
#include <gl\gl.h>
static GLfloat spin = 0.0; // góc quay hiện tại của hình 
void init(void) 
{
    glClearColor (0, 0, 0, 0);
    glShadeModel (GL_ SMOOTH); // trộn màu
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glPushMatrix();
    glRotatef(spin, 0.0, 1.0, 0.0);  // xoay một góc “spin” quanh trục y
    glEnable(GL_DEPTH_TEST);
    /* Vẽ các mặt của hình */
    … // code như vẽ hình thang cân
    glPopMatrix();
    glutSwapBuffers();  // thực hiện việc hoán đổi 2 buffer 
}

/* Hàm xoay */
void spinDisplay(void)
{
    spin = spin + 0.05;  // xoay thêm 0.05 độ cho mỗi lần lặp 
    if (spin > 360.0)
    spin = spin - 360.0;
    glutPostRedisplay();// thông báo cho chương trình cần phải thực hiện việc vẽ lại 
}

/* Các thao tác xử lý chuột */
void mouse(int button, int state, int x, int y) 
{
    switch (button) 
    {
        case GLUT_LEFT_BUTTON:  // khi nhấn chuột trái 
        if (state == GLUT_DOWN)
        glutIdleFunc(spinDisplay); // chương trình đang ngừng thì thực hiện hàm spinDisplay 
        break;
        case GLUT_MIDDLE_BUTTON:  // khi nhấn nút giữa 
        if (state == GLUT_DOWN)
        glutIdleFunc(0); // chương trình ngừng xoay 
        break;
        default:
        break;
    }
}

int main(int argc, char** argv)
{
    // giống 7 dòng đầu trong hàm main của mục A. Vẽ hình thang cân
    glutMouseFunc(mouse); // gọi hàm mouse về xử lý chuột 
    glutMainLoop();
    return 0;
}

C.    VẼ KHỐI LẬP PHƯƠNG

#include <gl\glut.h>
#include <gl\glu.h>
#include <gl\gl.h>
static GLfloat spin = 0.0; 
char k;    
float a;
void init(void) 
{
    glClearColor (0, 0, 0, 0);
    glShadeModel (GL_SMOOTH);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glPushMatrix();
    if(k=='l' ||k=='r')
        glRotatef(spin, 0, 1, 0);  // nếu nhấn phím r hoặc l thì xoay quanh trục Oy
    else if(k=='u' ||k=='d')
        glRotatef(spin, 1, 0, 0); // nếu nhấn phím u hoặc d thì xoay quanh trục Ox
    else
        glRotatef(spin, 1.0, 1.0, 1.0); // còn lại xoay 2 trục Ox và Oy
    gluLookAt (10, 10, 10, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0); // góc nhìn
    glTranslatef (2.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    /* Vẽ đa giác */
    … // vẽ các mặt cho hình lập phương tương tự vẽ hình thang cân
    glPopMatrix();
    glutSwapBuffers();  // thực hiện việc hoán đổi 2 buffer 
}

void spinDisplay(void)
{
    … // xem code tương tự mục B. Vẽ múi khế 
}

void spinDisplay_P(void) // hàm xoay hình theo phím
{
    spin = spin + a;  // xoay thêm a độ cho mỗi lần lặp 
    if (spin > 360.0)
    spin = spin - 360.0;
    glutPostRedisplay(); 
}

/* Hàm dùng bàn phím */
void keyboard (unsigned char key, int x, int y) 
{ 
    switch (key) 
    { 
        case 'l': 
            glutIdleFunc(spinDisplay_P);
            a=-0.05; // nhấn phím l góc xoay giảm 0.05 độ 
            k='l'; // cho biến k = l để xoay theo trục Oy
            glutPostRedisplay(); 
        break; 
        case 'r':
            glutIdleFunc(spinDisplay_P);
            a=0.05; // nhấn phím l góc xoay tăng 0.05 độ
            k='r';
            glutPostRedisplay(); 
        break; 
        case 'u': 
            glutIdleFunc(spinDisplay_P);
            a=-0.05;
            k='u'; // cho biến k = u để xoay theo trục Ox
            glutPostRedisplay(); 
        break; 
        case 'd': 
            glutIdleFunc(spinDisplay_P);
            a=0.05;
            k='d';
            glutPostRedisplay(); 
            break; 
        default: 
        break; 
   } 
} 

void mouse(int button, int state, int x, int y) 
{
    … xem code tương tự mục B. Vẽ múi khế 
}

int main(int argc, char** argv)
{
    … // giống 7 dòng đầu trong hàm main của mục A. Vẽ hình thang cân
    glutKeyboardFunc(keyboard);
    glutMouseFunc(mouse); 
    glutMainLoop();
    return 0;
}