10.Animation(Hoạt cảnh)Phần này sẽ giới thiệu với các bạn về cách tạo hoạt cảnh trong opengl.Hoạt cảnh luôn luôn có sức thu hút người lập trình, nó là một phần quan trọng trong lập trình đồ hoạ.Trước hết chúng ta sẽ xem xét hai hàm auxIdleFunc() và auxMainLoop().Hàm auxIdleFun() có nhiệm vụ gọi các hàm trong khi không nhận một sự kiện (event) của người dùng, trong chương trình dưới đây, cụ thể là nó sẽ vẽ lại window khi không có event nào.Còn hàm auxMainLoop() chỉ vẽ lại window khi có một sự kiện cụ thể như người dùng di chuyển cửa sổ, nút được bấm, bị cửa sổ khác đè lên.... Để quan sát được rõ ràng chúng ta cũng phải dùng đến hàm glMatrixMode().Khi thay đổi modeling và viewing thì phải thay đổi ma trận của nó, bằng cách dùng hai thông số GL_MODELVIEW và GL_PROJECTION, vì nếu chỉ thay đổi trong lúc khởi tạo window thì ta sẽ không thu được tác dụng của các hàm này khi cửa sổ bị thay đổi, chính vì thế mà chúng ta để nó trong hàm resize(), vì ma trận trên được lặp đi lặp lại nên chúng ta để hàm glMatrixMode(GL_MODELVIEW) sau cùng.Dưới đây là mã nguồn:/°filename animation1.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifGLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int edge[][2]={ {0,1}, {1,2}, {2,3}, {0,3}, {4,5}, {5,6}, {6,7}, {7,4}, {0,4}, {1,5}, {2,6}, {3,7}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT);glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glColor3d(1.0,1.0,1.0); glBegin(GL_LINES); for(i=0;i<12;i++){glVertex3dv(vertex[edge[i][0]]);glVertex3dv(vertex[edge[i][1]]); } glEnd(); glFlush(); if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA); auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); auxMainLoop(none); return 0;}Trong chương trình trên, mỗi lần hàm draw()được gọi thì giá trị r được tăng lên một đơn vị nếu vượt quá 360 độ thì nó sẽ trở về 0.Chúng ta phải thành lập hàm none() mặc dù nó không thực hiện một chức năng gì, nhưng hàm auxMainLoop() cần một hàm để gọi đến nó nên ta đã tạo hàm none.Tuy vậy bạn cũng chỉ nhìn thấy nhấp nháy của hình lập phương, để có thể quan sát được hãy biên dịch mã nguồn của chương trình sau:/°filename animation2.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifGLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int edge[][2]={ {0,1}, {1,2}, {2,3}, {0,3}, {4,5}, {5,6}, {6,7}, {7,4}, {0,4}, {1,5}, {2,6}, {3,7}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glColor3d(1.0,1.0,1.0); glBegin(GL_LINES); for(i=0;i<12;i++){glVertex3dv(vertex[edge[i][0]]);glVertex3dv(vertex[edge[i][1]]); } glEnd(); auxSwapBuffers(); /°hàm mới°/ if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA|AUX_DOUBLE);/°thông số mới°/ auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); auxMainLoop(none); return 0;}Trong hàm draw() tôi đã bỏ đi hàm glFlush() và thay bằng hàm auxSwapBuffers(), sử dụng double buffer là một kỹ thuật để tránh hiện tượng nhấp nháy màn hình mà các bạn mới lập trình đồ hoạ thường mắc phải.Kỹ thuật này được mô tả như sau: dùng một offbuffer, rồi vẽ lên đó sau đó mới đưa lên màn hình, tưởng tượng nếu bạn cắt từng chữ rồi dán lên để người xem thấy thì họ sẽ nhìn thấy bạn dán từng chữ một, nhưng nếu bạn dán lên đằng sau tờ giấy rồi lật ngược lại thì họ không biết là nó được dán từng chữ một(tượng trưng thôi, chứ người ta biết thừa)Để dùng được double buffers bạn phải thêm thông số AUX_DOUBLE trong hàm auxInitDisplayMode().Bây giờ bạn đã có một hình lập phương chuyển động mịn màng quanh trục OY.Thật ra hình lập phương mà chúng ta đã vẽ chỉ là một khung của hình lập phương thôi, còn các mặt thì chúng ta chưa vẽ, vì thế mà các nét khuất chúng ta vẫn nhìn thấy, bây giờ chúng ta sẽ dùng tham số GL_QUADS để vẽ hình lập phương với các mặt đầy đủ và phần bị khuất sẽ không nhìn thấy được.Mã nguồn:/°filename: animation3.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifGLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int face[][4]={ {0,1,2,3}, {1,5,6,2}, {5,4,7,6}, {4,0,3,7}, {4,5,1,0}, {3,2,6,7}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i,j; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glColor3d(1.0,1.0,1.0); glBegin(GL_QUADS); for(i=0;i<6;i++) for(j=0;j<4;j++){glVertex3dv(vertex[face[i][j]]); } glEnd(); auxSwapBuffers(); if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA|AUX_DOUBLE); auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); auxMainLoop(none); return 0;}Tiếp theo chúng ta sẽ thêm màu vào để hình của chúng ta được sinh động, bạn chú ý cách sắp xếp dữ liệu của tôi, rất nhiều nhà lập trình thích cách sắp xếp này.Mã nguồn:/°filename animation4.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifGLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int face[][4]={ {0,1,2,3}, {1,5,6,2}, {5,4,7,6}, {4,0,3,7}, {4,5,1,0}, {3,2,6,7}};GLdouble color[][3]={ {1.0,0.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,1.0,0.0}, {1.0,0.0,1.0}, {0.0,1.0,1.0}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i,j; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glBegin(GL_QUADS); for(i=0;i<6;i++){glColor3dv(color[i]);for(j=0;j<4;j++){glVertex3dv(vertex[face[i][j]]);} } glEnd(); auxSwapBuffers(); if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA|AUX_DOUBLE); auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); auxMainLoop(none); return 0;}Tạm thời dừng phần animation ở đây chúng ta chuyển qua phần khác.10-Sử dụng Depth buffer test:Mới nghe tên thôi các bạn cũng có thể hình dung được công việc chúng ta sắp làm đó là tạo chiều sâu và độ xa từ khi quan sát vật từ điểm quan sát.Trong phần này chúng ta sử dụng thêm thư viện nữa vì thế hãy thêm vào tiêu đề file GL/glu.h(cái này cũng có sẵn trong VC bạn không phải download ở đâu cả).Dưới đây là mã nguồn /°filename depth1.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif#include<GL/glu.h>GLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int face[][4]={ {0,1,2,3}, {1,5,6,2}, {5,4,7,6}, {4,0,3,7}, {4,5,1,0}, {3,2,6,7}};GLdouble color[][3]={ {1.0,0.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,1.0,0.0}, {1.0,0.0,1.0}, {0.0,1.0,1.0}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i,j; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);/°thông sô mới°/ glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glEnable(GL_DEPTH_TEST); /°hàm mới°/ glBegin(GL_QUADS); for(i=0;i<6;i++){glColor3dv(color[i]);for(j=0;j<4;j++){glVertex3dv(vertex[face[i][j]]);} } glEnd(); glDisable(GL_DEPTH_TEST); /°hàm mới°/ auxSwapBuffers(); if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA|AUX_DOUBLE|AUX_DEPTH);/°thông số mới°/ auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); auxMainLoop(none); return 0;}Trong chương trình trên chúng ta đã sử dụng tham số GL_DEPTH_BUFFER_BIT, muốn xoá buffer z thì chúng ta phải dùng tham số này(mục đích là cài đặt z buffer), hai hàm glEnable() và glDisable() có tác dụng khởi động buffer z(chiều sâu) và tắt nó đi, được dùng với thông số GL_DEPTH_TEST.Trong hàm auxInitDisplayMode() chúng ta cũng phải thêm thông số AUX_DEPTH để có thể quan sát được chiều sâu của đồ vật.Tuy vậy chương trình trên có một số yếu điểm, đó là việc liên quan đến các mặt khuất.Thử biên dịch mã nguồn dưới đây và quan sát sự khác nhau:/°filename depth2.cpp°/ #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif#include<GL/glu.h>GLdouble vertex[][3]={ {0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}};int face[][4]={ {0,1,2,3}, {1,5,6,2}, {5,4,7,6}, {4,0,3,7}, {4,5,1,0}, {3,2,6,7}};GLdouble color[][3]={ {1.0,0.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,1.0,0.0}, {1.0,0.0,1.0}, {0.0,1.0,1.0}};GLvoid CALLBACK none(void){}GLvoid CALLBACK draw(void){ int i,j; static int r=0; glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0); glRotated((double)r,0.0,1.0,0.0); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glBegin(GL_QUADS); for(i=0;i<6;i++){glColor3dv(color[i]);for(j=0;j<4;j++){glVertex3dv(vertex[face[i][j]]);} } glEnd(); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); auxSwapBuffers(); if(++r>=360) r=0;}GLvoid CALLBACK resize(GLsizei w,GLsizei h){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0,0,w,h); gluPerspective(30.0,1.0,1.0,10.0); glMatrixMode(GL_MODELVIEW);}int main(int argc, char °argv[]){ auxInitPosition(200,100,512,512); auxInitDisplayMode(AUX_RGBA|AUX_DOUBLE|AUX_DEPTH); auxInitWindow(argv[0]); auxReshapeFunc(resize); auxIdleFunc(draw); glCullFace(GL_BACK); auxMainLoop(none); return 0;}các bạn thấy rõ sự khác nhau rồi chứ?Chương trình trước, các cạnh dường như song song, trông không giống một vật 3 chiều chút nào, vì các mặt được chọn để thể hiện chưa được làm chính xác.Muốn hiểu rõ hơn xin xem tại trang opengl.org.