Hướng dẫn lập trình OpenGL căn bản
1.1. Xóa màn hình
Trong OpenGL có 2 loại buffer phổ biến nhất
color buffer: buffer chứa màu của các pixel cần được thể hiện
depth buffer (hay còn gọi là z-buffer): buffer chứa chiều sâu của pixel, được đo bằng
khoảng cách đến mắt. Mục đích chính của buffer này là loại bỏ phần đối tượng nằm sau
đối tượng khác.
Mỗi lần vẽ, chúng ta nên xóa buffer
glClearColor(0.0, 0.0, 0.0, 0.0); /* xác định màu để xóa color buffer
(màu đen) */
glClearDepth(1.0); /* xác định giá trị để xóa depth buffer */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* xóa color
buffer và depth buffer */
1.2. Xác định màu
Khi vẽ một đối tượng, OpenGL sẽ tự động sử dụng màu đã được xác định trước đó. Do đó, để vẽ
đối tượng với màu sắc theo ý mình, cần phải thiết lập lại màu vẽ. Thiết lập màu vẽ mới dùng
hàm glColor3f(), ví dụ
glColor3f(0.0, 0.0, 0.0); // black
glColor3f(1.0, 0.0, 0.0); // red
glColor3f(0.0, 1.0, 0.0); // green
glColor3f(1.0, 1.0, 0.0); // yellow
glColor3f(0.0, 0.0, 1.0); // blue
glColor3f(1.0, 0.0, 1.0); // magenta
glColor3f(0.0, 1.0, 1.0); // cyan
glColor3f(1.0, 1.0, 1.0); // white
g đoạn lớn chúng ta cần phải làm 1) Thực hiện các thao tác vẽ trong chế độ render (đây là điều mà 4 chương trước đã bàn tới) 2) Thực hiện các thao tác vẽ trong chế độ selection (giống hoàn toàn như trong công đoạn 1), kết hợp với một số thao tác đặc trưng trong chế độ selection. Công đoạn 1 là các thao tác để biến đổi các đối tượng trong không gian về các pixel và sau đó hiển thị lên màn hình. Công đoạn 2, gần như ngược lại, chương trình xác định xem pixel mà người dùng tương tác (ví dụ như nhấn chuột trái) thuộc đối tượng nào. Để chuyển đổi qua lại giữa các công đoạn (hay chế độ), chúng ta dùng hàm GLint glRenderMode(GLenum mode); trong đó mode là GL_RENDER hoặc GL_SELECT (mode còn có thể là GL_FEEDBACK nhưng ở đây chúng ta sẽ không xét tới). 2. Các thao tác trên chế độ selection Trước tiên chúng ta cần kích hoạt chế độ selection glRenderMode(GL_SELECT) 2.1. Xác định vùng chọn Ví dụ về chọn đối tượng bằng click chuột được cho như hình dưới đây Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 27 Việc xác định vùng chọn tương tự như là việc xác định khối nhìn, tức là chúng ta sẽ thao tác trên phép chiếu (projection – chương 3, mục 3). Thao tác tổng quát như sau glMatrixMode (GL_PROJECTION); glPushMatrix (); glLoadIdentity (); gluPickMatrix (...); gluPerspective, glOrtho, gluOrtho2D, or glFrustum /* ... */ glPopMatrix(); Trong đó, void gluPickMatrix(GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport[4]); là hàm xác định vùng quan tâm trong viewport (ví dụ như xung quanh vùng click chuột) với: (x, y, width, height) là tham số xác định quan tâm trên viewport viewport[4] là mảng 4 phần tử chứa 4 tham số của viewport, có thể dùng hàm glGetIntegerv(GL_VIEWPORT, GLint *viewport) để lấy ra. 2.2. Thiết lập đối tượng và danh tính cho đối tượng Để phân biệt được các đối tượng với nhau, OpenGL cần phải đặt tên cho các đối tượng cần quan tâm. Việc đặt tên này có 3 điều đáng lưu ý 1) tên là một số nguyên, 2) các đối tượng có thể mang cùng tên: đây là các đối tượng được gom vào cùng một nhóm được quan tâm, ví dụ như nhóm các hình cầu, nhóm các hình khối hộp, 3) tên có thể mang tính phân cấp, thể hiện ở đối tượng được cấu thành từ nhiều thành phần khác nhau. Ví dụ như khi click vào một cái bánh của cái một cái xe hơi, chúng ta cần biết là cái bánh số mấy của cái xe hơi số mấy. Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 28 OpenGL có một stack giúp thao tác trên tên các đối tượng, với các hàm void glInitNames(void) khởi tạo stack (stack lúc này rỗng) void glPushName(GLuint name) đặt tên của đối tượng cần xét vào trong stack void glPopName(void) lấy tên nằm ở đỉnh stack ra khỏi stack void glLoadName(GLuint name) thay nội dung của đỉnh stack Việc sử dụng stack này giúp cho mục đích thứ 3 – xây dựng tên mang tính phân cấp. Mỗi lần thực thi glPushName(name) hoặc glLoadName(name) thì chương trình sẽ hiểu là các đối tượng được vẽ ở các dòng lệnh sau sẽ có tên là name và chúng là thành phần bộ phận của đối tượng có tên đặt ở ngay dưới đỉnh stack. Ví dụ: xét đoạn mã giả sau glInitNames(); glPushName(1); /* vẽ đối tượng thứ nhất */ ... glPushName(2); /* vẽ đối tượng thứ 2 */ ... glPushName(4); /* vẽ đối tượng thứ 3 */ ... stack sẽ có nội dung sau thì nghĩa là đối tượng (4) là thành phần của đối tượng (2), và đối tượng (2) là thành phần của đối tượng (1). 2.3. Truy vấn đối tượng trong vùng chọn Để có thể truy vấn xem đối tượng nào được chọn, OpenGL xử lý như sau trước tiên sẽ đánh dấu mọi đối tượng nào có vùng giao với vùng chọn, sau đó, với mỗi đối tượng có vùng giao, tên của nó và giá trị z nhỏ nhất, z lớn nhất của vùng giao sẽ được lưu trong hit records mọi truy vấn về đối tượng được chọn sẽ được thực hiện trên hit records. Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 29 Như vậy, dựa trên hit records chúng ta biết được các thông tin sau 1) số recods = số lượng đối tượng cần quan tâm nằm trong vùng chọn 2) với mỗi record, chúng ta biết được các thông tin sau a. tên của đối tượng (bao gồm tên của tất cả các đối tượng mà nó là thành phần) b. z_min và z_max của vùng giao giữa đối tượng với vùng chọn (2 con số này nàm trong [0,1] và cần phải nhân với 231-1 (0x7fffffff) ). Để khởi tạo hit records, chúng ta cần phải gọi hàm void glSelectBuffer(GLsizei size, GLuint *buffer) trong đó buffer chính là mảng chứa hit records. Chú ý: thủ thục này phải được gọi trước khi chuyển sang chế độ GL_SELECT. 4. Ví dụ Trong ví dụ này, tương tự như ví của của chương 3 mục 4, chúng ta sẽ vẽ mô hình trái đất quay xung quanh mặt trời. Hơn nữa, chúng ta sẽ cho phép thực hiện các thao tác sau nếu người dùng click vào mặt trời thì mặt trời sẽ được vẽ bằng solid sphere thay vì là wire sphere nếu người dùng click vào trái đất thì trái đất sẽ được vẽ bằng solid sphere thay vì là wire sphere nếu người dùng click vào vùng không thuộc đối tượng nào thì cả trái đất và mặt trời sẽ được vẽ bằng hình mặc định ban đầu là wire sphere #include "glut.h" #define NON -1 #define SUN 1 #define PLANET 2 static int year = 0, day = 0; static int ichosen = NON; // ghi lại xem đối tượng nào đang được chọn, NON nghĩa là không có đối tượng nào hết void init(void) { glClearColor (0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); } Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 30 // hàm vẽ tổng quát cho cả chế độ render và chế độ selection void draw(GLint mode) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glColor3f (1.0, 0, 0); if (mode == GL_SELECT) // nếu đang là chế độ selection thì đặt tên cho mặt trời glLoadName(SUN); if (ichosen == SUN) // nếu đang chọn SUN thì sẽ vẽ mặt trời khác đi glutSolidSphere(1.0, 50, 50); else glutWireSphere(1.0, 20, 16); // ngược lại thì vẽ như bình thường /* di chuyển đến tọa độ mới để vẽ trái đất */ glRotatef ((GLfloat) year, 0.0, 1.0, 0.0); glTranslatef (2.0, 0.0, 0.0); glRotatef ((GLfloat) day, 0.0, 1.0, 0.0); glColor3f (0, 0, 1.0); if (mode == GL_SELECT) // nếu đang là chế độ selection thì đặt tên cho mặt trời glLoadName(PLANET); if (ichosen == PLANET) // nếu trái đất đang được chọn thì sẽ vẽ khác đi glutSolidSphere(0.2, 30, 30); else glutWireSphere(0.2, 10, 8); glPopMatrix(); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(GL_RENDER); glutSwapBuffers(); } // hàm xử lý hit records void processHits (GLint hits, GLuint buffer[]) { unsigned int i, j; GLuint *ptr; float min_z_min; ptr = (GLuint *) buffer; ichosen = NON; /* lặp với mỗi hit, trong trường hợp có nhiều hit thì sẽ chọn đối tượng ở gần mắt nhất */ for (i = 0; i < hits; i++) { GLuint names = *ptr; ptr++; float z_min = (float) *ptr/0x7fffffff; ptr++; // giá trị z_min của vùng giao đối tượng với vùng chọn float z_max = (float) *ptr/0x7fffffff; ptr++; // giá trị z_max GLuint name = *ptr; ptr++; Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 31 if ( i == 0 || min_z_min > z_min ) // chọn đối tượng ở gần mắt hơn { min_z_min = z_min; ichosen = name; } } ptr = (GLuint*) buffer; } #define BUFSIZE 512 // hàm xử lý thông điệp về mouse void pick(int button, int state, int x, int y) { GLuint selectBuf[BUFSIZE]; GLint hits; GLint viewport[4]; /* chỉ xử lý khi người dùng click chuột trái */ if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN) return; glGetIntegerv (GL_VIEWPORT, viewport); glSelectBuffer (BUFSIZE, selectBuf); // khởi tạo hit records (void) glRenderMode (GL_SELECT); // chọn chế độ selection glInitNames(); // khởi tạo stack tên glPushName(0); // đặt tên cho đối tượng rỗng là 0 /* thiết lập vùng chọn */ glMatrixMode (GL_PROJECTION); glPushMatrix (); glLoadIdentity (); gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3]- y), 5.0, 5.0, viewport); // vùng quan tâm vùng quanh mouse 5x5 pixel gluPerspective(60.0, viewport[2]/viewport[3], 1.0, 20.0); draw(GL_SELECT); // vẽ trong chế độ selection glMatrixMode (GL_PROJECTION); glPopMatrix (); glFlush (); hits = glRenderMode (GL_RENDER); // hits là số hit trong vùng chọn processHits (hits, selectBuf); // xử lý hit records glutPostRedisplay(); // bắt vẽ lại } void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 32 glLoadIdentity(); gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); } void keyboard (unsigned char key, int x, int y) { switch (key) { case 'd': day = (day + 10) % 360; glutPostRedisplay(); break; case 'D': day = (day - 10) % 360; glutPostRedisplay(); break; case 'y': year = (year + 5) % 360; glutPostRedisplay(); break; case 'Y': year = (year - 5) % 360; glutPostRedisplay(); break; default: break; } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMouseFunc(pick); // thiết lập hàm pick xử lý thông điệp mouse glutMainLoop(); return 0; } Kết quả khi click vào mặt trời Hướng dẫn lập trình OpenGL căn bản Đặng Nguyễn Đức Tiến – Vũ Quốc Hoàng - Lê Phong Page 33
File đính kèm:
- huong_dan_lap_trinh_opengl_can_ban.pdf