76
HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIỄN THÔNG BÀI TẬP LỚN TÌM HIỂU CƠ BẢN VỀ OPENGL Giáo viên hướng dẫn: Trịnh Thị Vân Anh Lớp: H09CN7 Nhóm Sinh viên thực hiện: Hoàng Thị Hương Sầm Thị Châm Trần Thị Phương Thơm Lê Mậu Thành Nguyễn Thị Thuý Lan 1

Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Embed Size (px)

Citation preview

Page 1: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

HỌC VIỆN CÔNG NGHỆ

BƯU CHÍNH VIỄN THÔNG

BÀI TẬP LỚN

TÌM HIỂU CƠ BẢN VỀ OPENGL

Giáo viên hướng dẫn: Trịnh Thị Vân Anh Lớp: H09CN7Nhóm Sinh viên thực hiện: Hoàng Thị Hương

Sầm Thị Châm Trần Thị Phương Thơm Lê Mậu Thành

Nguyễn Thị Thuý Lan Hoàng Công Phúc Nguyễn Thu Ngọc Phạm Ngọc Thắng Trần Minh Ngọc Diệp

Hà Nội, tháng 12 năm 2010

1

Page 2: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Mục lục

Phần A. Tìm hiểu về Opengl…………………………………..............3CHƯƠNG 1. GIỚI THIỆU VỀ OPENGL……………………………….................3CHƯƠNG 2. CÀI ĐẶT OPENGL TRONG VISUAL C++………………..............4CHƯƠNG 3. CÁC THÀNH PHẦN CƠ BẢN CỦA OPENGL…………………....4

3.1. Chương trình đầu tiên trong opengl………………………………….....43.2. Xoá màn hình trong opengl…………………………………………......53.3. Vẽ hình trong opengl……………………………………………………63.4. Sử dụng màu vẽ…………………………………………………………83.5. Giao diện của cửa sổ và quản lý cửa sổ……………………………......103.6. Quan sát – khung nhìn………………………………………………….113.7. Chuột…………………………………………………………………...143.8. Thể hiện toạ độ 3 chiều………………………………………………...213.9. Sử dụng ánh sáng trong opengl………………………………………...253.10. Vẽ nhiều vật – Dùng ma trận………………………………………….31

CHƯƠNG 4. VẼ CÁC ĐỐI TƯỢNG HÌNH HỌC CƠ BẢN TRONG OPENGL…………………………………………………………………………….41

4.1 Vẽ điểm, đường, đa giác (Point, Line, Polygon)………………………...424.1.1. OpenGl tạo ra điểm, đường, đa giác từ các đỉnh( Vertex)…………….434.1.2. Một số lệnh khác…………………………………………………........434.2. Tập lồi…………………………………………………………………...444.2.2. Phéo đặc tam giác hợp lệ……………………………………………...444.3. Phép biến đổi điểm nhìn và biến đổi mô hình (Verwing and Modeling Transformations)…………………………………………………………….454.3.1. Phéo biển đổi điểm nhìn…………………………………………........454.3.2. Phéo biển đổi mô hình………………………………………………...464.3.3. Các chuyển đổi từ phép biến đổi điểm nhìn sang biến đổi mô hình…..464.3.4. Phép biến đổi cổng nhìn………………………………………….........474.3.5. Điểu khiển các ngăn xếp ma trận……………………………………...49

Phần B. Ví dụ mô phỏng……………………………………....................51I. Môi trường làm việc trong ViSualC++…………………………51II- Sử dụng VC++ kết hợp với OpenGl để mô phỏng động học…………….56

2

Page 3: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Phần A. Tìm hiểu về Opengl

CHƯƠNG 1. GIỚI THIỆU VỀ OPENGL

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 Các thư viện liên quan của OpenGL: 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.

CHƯƠNG 2. CÀI ĐẶT OPENGL TRONG VISUAL C++

Lập trình opengl trong Windows bằng Visual C, ta sử dụng ba thư viện sau glaux.lib glu32.lib và opengl32.lib.Trong Visual C muốn link tới các thư viện này ta làm như sau: trên menu(trình đơn) chọn Project sau đó chọn setting rồi cuối cùng trong tab link ta đánh tên 3 thư viện trên vào (giữa các tên của thư viện có dấu cách) khi tạo một project mới ta phải chọn Window32 console application và giờ ta có thể viết mã lệnh . Ta Down Load : glut-3.7.6-bin.rar1. Copy và Unzip tệp glut-3.7.6-bin.rar2. Copy tệp glut32.dll vào Window\System(Win xp, xp sp3)3. Copy tệp glu.32.lip vào Program File\Microsoft Visual Studio\VC98\ Lip

3

Page 4: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

4. Copy tệp glut.h vào Program File\Microsoft Visual Studio\VC98\ Include\Gl

CHƯƠNG 3. CÁC THÀNH PHẦN CƠ BẢN CỦA OPENGL

3.1. Chương trình đầu tiên trong Opengl

/*filename: hello.c*//*Chương trình đầu tiên tạo một cửa sổ trong opengl*/

#ifdef unix /*Phần này dùng để xác định môi trường làm việc của bạn*/#include <GL/gl.h> /*Nó sẽ xác định bạn biên dịch chương trình này trên unix*/#include “aux.h“ /*hay Windows, với lập trình viên trên windows bạn có */#define CALLBACK /*thể bỏ phần bên trên đi và chỉ lấy phần in đậm*/#else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifint main(int argc, char *argv[]){

auxInitWindow(argv[0]);return 0;

}

Lệnh auxInitWindow(string); có tác dụng tạo một cửa sổ mới, string là tiêu đề của cửa sổ đó, bạn có thể viết tiều đề như thế nào là tuỳ chúng ta.Chương trình này sau khi biên dịch thì nó mới chỉ hiện ra một cửa sổ rồi đóng ngay, nếu windows của ta chạy nhanh quá thì ta sẽ không nhìn thấy gì hết Sau đây chúng ta sẽ bắt Window dùng lại chừng 1 giây để chúng ta quan sát.Cũng với mã lệnh trên bạn chỉ cần thêm một dòng lệnh: sleep(số_giây_muốn_xem x 1000);(tức là lệnh này bắt window tạm dừng trong vòng 1 phần nghìn giây)

3.2. Xoá màn hình trong opengl

/*filename: clear.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>

4

Page 5: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

#include<GL/gl.h>#include<GL/glaux.h>#endifint main(int argc, char *argv[]){

auxInitWindow(argv[0]);/*Những dòng lệnh mới*/

glClearColor(1.0,1.0,1.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glFlush();

/*Những dòng lệnh mới*/Sleep(1000);return 0;

}

Các lệnh glClearColor(), glClear(),glFush() là những lệnh cơ bản của Opengl.glClearColor() có nhiệm vụ chọn màu để xoá window, bạn dễ dàng nhận ra là nó có 4 tham số, 4 tham số đó là RGBA( red green blue alpha).Không giống với hàm RGB() trong Win32 API , 4 tham số này có giá trị trong khoảng 0.0f đến 1.0f(kiểu float).Ba tham số đầu là màu đỏ xanh lá cây và xanh da trời, còn tham số thứ 4 là độ sáng tối của window.

3.3. Vẽ hình trong opengl - vẽ một số hình đơn giản:/*filename line.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifint main(int argc, char *argv[]){

auxInitWindow(argv[0]);glClearColor(1.0,1.0,1.0,0.0);glClear(GL_COLOR_BUFFER_BIT);

/*những dòng lệnh mới*/glBegin(GL_LINE_LOOP);glVertex2d(0.1,0.1);glVertex2d(0.9,0.1);glVertex2d(0.9,0.9);glVertex2d(0.1,0.9);

/*những dòng lệnh mới*/glEnd();glFlush();Sleep(1000);return 0;

}

Tất cả các hình khối được vẽ trong opengl đều được nằm giữa hai dòng lệnh glBegin() và glEnd().

5

Page 6: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

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 câu 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 điểm đầu trùng với điểm cuối.Dưới đây là một số hằng số cơ bản:

Hằng số ý nghĩaGL_POINT Vẽ điểmGL_LINÉ Vẽ đường thẳng nối hai điểmGL_LINE_STRIP Tập hợp của những đoạn đựơc nối với nhauGL_LINE_LOOP Đường gấp khúc khép kínGL_TRIANGLES Vẽ hình tam giácGL_QUADS Vẽ tứ giácGL_TRIANGLES_STRIP Vẽ một tập hợp các tam giác liền nhau, chung một cạnhGL_QUAD_STRIP Vẽ một tập hợp các tứ giác liền nhau, chung một cạnhGL_TRIANGLE_FAN Vẽ hình quạtDưới đây là bức tranh toàn cảnh về các thông số này.

6

Page 7: Bai Tap Lon Tim Hieu Co Ban Ve 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 là 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,....

3.4. Sử dụng màu vẽ - mã nguồn:/*filename: color1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifint main(int argc, char *argv[]){

auxInitDisplayMode(AUX_RGBA); /*hàm mới*/auxInitWindow(argv[0]);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glColor3d(1.0,0.0,0.0); /*hàm mới*/glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS); /*tham số mới*/glVertex2d(0.1,0.1);glVertex2d(0.9,0.1);glVertex2d(0.9,0.9);glVertex2d(0.1,0.9);glEnd();glFlush();Sleep(1000);return 0;

}

Hàm auxInitDisplayMode() báo với window rằng chúng ta chọn cách hiển thị những gì mà chúng ta sắp vẽ tới đây, tham số của nó là AUX_RGBA chính là mode RGBA mà nhóm chúng tôi đề cập ở trên. Hàm glColor3d() cho phép chúng ta chọn màu vẽ, tham số của nó là red green và blue nhưng các giá trị này là kiểu double nếu ta muốn dùng kiểu float thì có hàm glColor3f(), cả hai kiểu trên giá trị của màu vẫn nằm trong khoảng 0 đến 1. Chú ý là chương trình trên chúng ta đã đổi tham số mới cho hàm glBegin(), bây giờ nó sẽ vẽ một tứ giác, và trong chương trình này thì là một hình vuông. Trong phần này nhóm tôi muốn trình bày một kỹ thuật nữa, chương trình trên chỉ cho chúng ta nhìn thấy một màu đỏ do chúng ta đặt một màu duy nhất trước khi vẽ. Để có thể tạo nhiều màu ấn tượng bạn có thể cài đặt đi cài đặt lại hàm glColor3d() mỗi khi chúng ta vẽ mới.

7

Page 8: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Dưới đây là mã nguồn:/*filename: color2.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endifint main(int argc, char *argv[]){

auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0); /*hàm này đã được chuyển xuống đây*/glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0); /*tham số mới cho hàm*/glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0); /*tham số mới cho hàm*/glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0); /*tham số mới cho hàm*/glVertex2d(0.1,0.9);glEnd();glFlush();Sleep(1000);return 0;

}

Biên dịch và chạy thử bạn có một hình vuông trông khá đẹp mắt Ngoài ra , cách sử dụng hàm, với các hậu tố: ví dụ với hàm glVertex*() và glColor*(), hay các hàm khác có dấu hoa thị * thì nó có thể có rất nhiều hậu tố.Và nó có cấu tạo như sau: lấy ví dụ hàm glVertex*() Có hàm glVertex4dv(Gldouble x,Gldouble y,Gldouble z,Gldouble w) số 4 thể hiện rằng hàm có 4 tham số, chữ d thể hiện rằng tham số có giá trị double(ngoài ra nó còn có thể là float,int,short, unsigned int, unsigned short, unsigned char,char) chữ v thể hiện rằng nó dùng pointer.

3.5. Giao diện của cửa sổ và quản lý cửa sổ: Với những chương trình chỉ cần vẽ đơn giản thì ta có thể dùng các chương trình trên, nhưng với các chương trình phức tạp sau này chúng ta không thể viết như thể được nữa. Dưới đây nhóm tôi sẽ trình bày cấu trúc của chương trình trong opengl.Trước hết là từ khoá CALLBACK, nếu đã lập trinh WIN API thì có thể hiểu rõ được lệnh này, nhưng có thể nói đơn giản là khi sử dụng thư viện AUX thì ta phải dùng từ khoá này để chỉ định nó.Các chương trình bên trên chúng ta viết đều dùng

8

Page 9: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

lệnh Sleep(1000) để bắt window dừng lại cho chúng ta theo dõi, sắp tới đây chúng ta sẽ làm một cách chuyên nghiệp hơn là dùng hàm auxMailLoop() trong thân của hàm main() – hàm chính của chương trình. Tham số của hàm này là con trỏ trỏ đến hàm mà chúng ta vẽ , hiện thị những gì chúng ta muốn( trong chương trình này tham số chính là hàm draw()). Điều gì sẽ xảy ra nếu người dùng thay đổi kích cỡ của cửa sổ. Để thực hiên điều này chúng ta cũng dùng một hàm tương tự như hàm auxMainLoop(), đó là hàm auxReshapeFunc(), tham số của nó cũng là con trỏ chỉ đến hàm mà chúng ta có thể thay đôi thông số của cửa sổ, tham số của nó trong chương trình này là hàm resize(). Nếu bạn đã học qua về đồ hoạ máy tính thì sẽ dễ dàng hiểu về toạ độ trong đồ hoạ, hàm glLoadIdentity() có nhiệm vụ thiết định ma trận của toạ độ là ma trận đơn vị.

Mã nguồn dưới đây sẽ cho chúng ta rõ hơn:

/*filename: interface.cpp*/

#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

GLvoid CALLBACK draw(void){ /*chú ý bạn có thể không cần chữ void trong */

glClearColor(0.0,0.0,0.0,0.0); /*khi lập trình với VC, Glvoid là kiểu */

glClear(GL_COLOR_BUFFER_BIT);/*hàm trong opengl, nó tương tụ */glClearColor(0.0,0.0,0.0,0.0); /*như void trong C hay C++*/glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0);glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0);glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0);glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0);glVertex2d(0.1,0.9);glEnd();glFlush();

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();}

int main(int argc, char *argv[])

9

Page 10: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

{auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMainLoop(draw);return 0;

}

3.6. Quan sát – Khung nhìn: Chương trình trên, khi ta thay đôi kích cỡ có lúc ta không nhìn thấy hình vuông mà chúng ta đã vẽ nữa, tại sao lại như vậy?Câu trả lời nằm trong chương trình dưới đây:

/*filename: view.cpp*/

#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

GLvoid CALLBACK draw(void){glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0);glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0);glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0);glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0);glVertex2d(0.1,0.9);glEnd();glFlush();

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h); /*hàm mới*/glOrtho(-1.0,1.0,-1.0,1.0,0.0,1.0); /*hàm mới*/

}

int main(int argc, char *argv[]){

10

Page 11: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMainLoop(draw);return 0;

}

Nhóm tôi sẽ giới thiệu thế nào là Viewport. Viewport xác định cổng nhìn cho chúng ta, tức là phần không gian trên cửa sổ window ma người quan sát được phép quan sát. Nó chính là một hình chữ nhật. Hai tham số đầu tiên của hàm này xác định toạ độ của đỉnh trên cùng phía tay trái của hình chữ nhật, hai toạ dộ sau xác định chiều rộng và chiều cao của hình chữ nhật ấy. Với các tham số trên ta có thể thấy , chương trình trên cho phép ta quan sát toàn bộ màn hình. Tiếp theo là kiểu nhìn glOrtho(). Quan sát hình vẽ dưới đây:

Như đã thấy trên hình, hàm glOrtho(), xác lập một ma trận cho phép chúng ta nhìn theo kiểu như hình vẽ, đây là hàm tổng quát:

void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);

Tương ứng với chương trình trên của chúng ta left là –1.0, right là 1.0, bottom là –1.0, top là 1.0, near là 0.0 và far là 1.0. Trong phần này nhóm tôi muốn trình bày thêm một hàm số nữa.Các chương trình trên đều tạo cửa sổ với chiều dài và rộng xác đinh, muốn tạo một cửa sổ có kích cỡ theo ý muốn bạn dùng hàm sau: auxInitPosition(), nó có 4 thông số là toạ độ x, y của đỉnh trên bên tay trái của cửa sổ, chiều rộng và chiều dài của cửa sổ.Dưới đây là mã nguồn:

/*filename : size.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK

11

Page 12: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

#else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

GLvoid CALLBACK draw(void){glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0);glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0);glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0);glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0);glVertex2d(0.1,0.9);glEnd();glFlush();

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w/2,h/2);glOrtho(-1.0,1.0,-1.0,1.0,0.0,1.0);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480); /*hàm mới*/auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMainLoop(draw);return 0;

}

3.7. Chuột: Trong các trò chơi ta đều thấy sự quan trọng của việc sử dụng chuột, trong phần này chúng ta sẽ xem xét làm thế nào để chương trình chúng ta nhận ra chúng ta đang bấm trái chuột, chúng ta đang di chuyển chuột. Để làm được điều này chúng ta sử dụng hàm auxMouseFunc(). Dưới đây là mã nguồn của chương trình mouse.cpp

/*filename mouse.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else

12

Page 13: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#include"stdio.h" /*nếu bạn không có dòng này thì hàm printf() không thực hiện*/#endif

GLvoid CALLBACK draw(void){glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0);glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0);glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0);glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0);glVertex2d(0.1,0.9);glEnd();glFlush();

}

GLvoid CALLBACK left(AUX_EVENTREC *event){

printf("%d,%d\n",event->data[AUX_MOUSEX],event->data[AUX_MOUSEY]);}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w/2,h/2);glOrtho(-1.0,1.0,-1.0,1.0,0.0,1.0);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480);auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);

/*hàm mới*/auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,left);

/*hàm mới*/auxMainLoop(draw);return 0;

}

Trong chương trình trên , chúng ta thấy xuất hiện hàm left() và hàm auxMouseFunc(). Hàm auxMouseFunc() có gọi đến hàm left(), nó có ý nghĩa rằng, khi chuột được bấm thì sẽ thực hiện hàm left(). Trong tham số của hàm auxMouseFunc() có các tham sô sau: tham số đầu tiên nói đến phần nào của chuột được tác động, tham số thứ 2 nói đến nó được tác động như thế nào, và tham số cuối cùng muốn nói tác động rồi thì làm gì. Trong hàm left() tham số có dạng con trỏ và

13

Page 14: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

có kiểu là AUX_EVENTREC, nó lấy dữ liệu về toạ độ x và y của chuột. Trong một chương trình không phải là chỉ có một hàm auxMouseFunc() mà bạn có thể dùng bao nhiêu tuỳ thích, miễn là đừng va chạm nhau là được, trong phần mã nguồn chúng tôi có cho thêm một chương trình ví dụ về cách dùng 2 lần hàm auxMouseFunc()(trong file mouse2.cpp)

Dưới đây tôi sẽ trình bày một chương trình khá thú vị , mã nguồn của nó như sau:/*filename connectlines.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#include"stdio.h"#endif

GLvoid CALLBACK draw(void){}

GLvoid CALLBACK left(AUX_EVENTREC *event){

static int flag=0;static GLint x,y;

if(flag){glColor3d(0.0,0.0,0.0);glBegin(GL_LINE_STRIP);glVertex2i(x,y);glVertex2i(event->data[AUX_MOUSEX],event->data[AUX_MOUSEY]);glEnd();glFlush();

}x=event->data[AUX_MOUSEX];y=event->data[AUX_MOUSEY];flag=1;

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h);glOrtho(0.0,(GLdouble)w,(GLdouble)h,0.0,0.0,1.0);/* đổi thông số*/glClearColor(1.0,1.0,1.0,0.0); /*chuyển vị trí 2 hàm

này*/glClear(GL_COLOR_BUFFER_BIT);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480);auxInitDisplayMode(AUX_RGBA);

14

Page 15: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,left);auxMainLoop(draw);return 0;

}

Thực ra chương trình này rất dễ hiểu, có lẽ không phải trình bày gì nhiều. Nó lưu cá điểm lại và nối thành một đường gấp khúc.Nhược điểm của chương trình trên hẳn các bạn đã rõ khi biên dịch nó.Nó không vẽ lại cửa sổ của bạn khi cửa sổ của bạn bị che bởi một cửa sổ khác, hay bị minimize, tức là hình mà ta muốn vẽ không được gửi tới hàm draw(). Vì vậy bạn phải lưu những điểm đã chọn và vẽ lại chúng trong hàm draw().Dưới đây là mã nguồn:/*filename connectlines1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

#define MAXPOINTS 100 /*số điểm tối đa có thể được chọn*/GLint point[MAXPOINTS][2]; /*mảng lưu trữ các điểm đó*/int num=0; /*số điểm đã chọn đến thời điểm hiện tại*/

GLvoid CALLBACK draw(void){

int i;

if(num>=2){glClearColor(1.0,1.0,1.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glColor3d(0.0,0.0,0.0);glBegin(GL_LINE_STRIP); /*bạn hãy nhớ cấu trúc này*/for(i=0;i<num;i++){

glVertex2iv(point[i]);}glEnd();glFlush();

}}

GLvoid CALLBACK left(AUX_EVENTREC *event){

if(num>=MAXPOINTS) return; /*giới hạn số điểm bạn vẽ */

point[num][0]=event->data[AUX_MOUSEX]; /*lưu trữ toạ độ x của chuột*/

point[num][1]=event->data[AUX_MOUSEY]; /*lưu trữ toạ độ y của chuột*/

15

Page 16: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

num++; /*tăng số điểm sau mỗi lần bấm*/}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h);glOrtho(0.0,(GLdouble)w,(GLdouble)h,0.0,0.0,1.0);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480);auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,left);auxMainLoop(draw);return 0;

}

Bây giờ bạn không phải lo đến việc cửa sổ không chịu vẽ lại khi nó bị che mất. Một điều cũng đáng chú ý trong chương trình trên là chúng ta đã sử dụng hàm glVertex2iv() hàm này có tham số là thành viên của mảng và thành viên của mảng có các giá trị x,y là số nguyên, chữ i trong phần hậu tố của hàm trên biểu hiện cho giá trị nguyên còn chữ v biểu hiện cho kiểu pointer. Dưới đây cung cấp một chương trình có thể vẽ được cả những đường gấp khúc và các đa giác. Mã nguồn không có gì phức tạp và đáng bàn ở đây cả, nó chỉ là cách sắp xếp dữ liệu và có thêm một hàm right() mà đã đề cập ở các phần trên./*filename connectlines2.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

#define MAXPOINTS 100GLint point[MAXPOINTS][2];int num=0;int flag=0;

GLvoid CALLBACK draw(void){

int i;

if(num>=2){if(flag){

flag=0;i=num-2;glColor3d(0.0,0.0,0.0);

16

Page 17: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glBegin(GL_LINE_STRIP);}else{

i=0;glClearColor(1.0,1.0,1.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glColor3d(0.0,0.0,0.0);glBegin(GL_POLYGON);}for(;i<num;i++){

glVertex2iv(point[i]);}glEnd();glFlush();

}}

GLvoid CALLBACK left(AUX_EVENTREC *event){

if(num>=MAXPOINTS) return;point[num][0]=event->data[AUX_MOUSEX];point[num][1]=event->data[AUX_MOUSEY];num++;flag=1;

}

GLvoid CALLBACK right(AUX_EVENTREC *event){

draw();}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h);glOrtho(0.0,(GLdouble)w,(GLdouble)h,0.0,0.0,1.0);glClearColor(1.0,1.0,1.0,0.0);glClear(GL_COLOR_BUFFER_BIT);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480);auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,left);auxMouseFunc(AUX_RIGHTBUTTON,AUX_MOUSEDOWN,right);auxMainLoop(draw);return 0;

}

17

Page 18: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

3.8. Thể hiển toạ độ 3 chiều: Đến giờ ta mới biết đến toạ độ 2 chiều trong opengl, nếu chỉ có vậy thì chẳng khác gì trong lập trình Window cả.Vì vậy trong phần này chúng ta sẽ cùng xem opengl vẽ các hình 3 chiều như thế nào.

/*filename : rotated45.cpp*/

#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

GLvoid CALLBACK draw(void){

glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);glRotated(45,0.0,1.0,0.0); /*quay quanh trục OY 45 độ*/glBegin(GL_QUADS);glColor3d(1.0,0.0,0.0);glVertex2d(0.1,0.1);glColor3d(0.0,1.0,0.0);glVertex2d(0.9,0.1);glColor3d(0.0,0.0,1.0);glVertex2d(0.9,0.9);glColor3d(1.0,0.0,1.0);glVertex2d(0.1,0.9);glEnd();glFlush();

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h);glOrtho(-1.0,1.0,-1.0,1.0,0.0,1.0);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,640,480);auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMainLoop(draw);return 0;

}

Chương trình này không có gì đặc biệt ngoài hàm glRotated() , hàm này cho phép chúng ta quay hình tứ giác của chúng ta quanh trục OY với góc quay 45 độ.Tham số đầu tiên của nó là góc sẽ được quay, 3 tham số sau là tham số của vector mà hình

18

Page 19: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

của chúng ta sẽ quay với góc quay trên.Ta nhận thấy rằng các giá trị của vector chúng ta là : toạ độ x bằng 0, toạ độ y bằng 1, toạ độ z bằng 0. Tức là véctơ của chúng ta thẳng đứng theo trục OY, ta có thể thay đổi các thông số của vector này để kiểm nghiệm hàm này xem . Các giá trị của các thông số này là kiểu double.(Chú ý nếu không thử các thông số khác thì bạn sẽ rất khó để quan sát hàm này hoạt động như thế nào )

Tiếp theo nhóm tôi xin trình bày cách vẽ một hình lập phương thật sự bằng opengl. /*filename cube1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #else#include<windows.h>#include<GL/gl.h>#include<GL/glaux.h>#endif

GLdouble vertex[][3]={ /*Khai báo dữ liệu cho tám đỉnh của hình lập phương*/

{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]={ /*Khai báo các cạnh, mà chúng ta sẽ sư dụng dữ liệu*/{0,1}, /*của các đỉnh bên trên*/{1,2},{2,3},{0,3},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{2,6},{3,7}

};

GLvoid CALLBACK draw(void){

int i;

glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);

glColor3d(1.0,1.0,1.0);

19

Page 20: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glBegin(GL_LINES);for(i=0;i<12;i++){

glVertex3dv(vertex[edge[i][0]]); /*hàm mới*/glVertex3dv(vertex[edge[i][1]]); /*hàm mới*/

}glEnd();glFlush();

}

GLvoid CALLBACK resize(GLsizei w,GLsizei h){

glLoadIdentity();glViewport(0,0,w,h);glOrtho(-2.0,2.0,-2.0,2.0,0.0,2.0);//gluPerspective(30.0,1.0,1.0,10.0);//gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

}

int main(int argc, char *argv[]){

auxInitPosition(200,100,512,512);auxInitDisplayMode(AUX_RGBA);auxInitWindow(argv[0]);auxReshapeFunc(resize);auxMainLoop(draw);return 0;

}

Phần khai báo dữ liệu, điều đáng nói thứ nhất là chúng ta dùng hàm glVertex3dv() thay cho các hàm vẽ đỉnh 2 chiều trước đây, hàm này nhận tham số là thành viên của mảng, giá trị của các thành viên phải là double, và nó có toạ độ 3 chiều. Trước hết biên dịch chương trình trên, ta chưa thấy gì nếu không cho hai hàm mà nhóm tôi đã đánh dấu đỏ bên trên vào, hãy nhớ lại cách quan sát bằng glOrtho() trước đây và dễ dàng nhận thấy chúng ta không thể nhìn thấy toàn bộ hình lập phương được mà chỉ là một hình vuông, vì cách nhìn bằng glOrtho() chỉ cho ta nhìn song song thôi. Chính vì vậy mà chúng ta phải chuyển qua cách quan sát bằng gluPerspective(), bốn tham số của nó được trình bày như sau:

20

Page 21: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble near, GLdouble far);

Tham số đầu tiên là góc quan sát trong mặt phẳng XOZ, trong ví dụ trên góc đó là 30độ, tham số thứ hai là tỉ lệ giữa w và h, nó bằng w/h, tham số thứ 3 và thứ 4 thì hẳn các bạn đã quen khi quan sát bằng glOrtho().Nếu chỉ có hàm này không thì chúng ta vẫn chưa quan sát được hình lập phương mà chúng ta vẽ. Để quan sát được chúng ta phải dùng thêm hàmgluLookAt() Hàm này có tới 9 tham số, nhưng thực ra nó nằm trong 3 tham số chính.Tham số đầu tiên là vị trí của mắt, cũng có thể coi đó là vị trí của camera(chú ý là trong toạ độ 3 chiều, nên vị trí của mắt chứa 3 toạ độ), tham số thứ 2 là điểm nhìn, và tham số thứ 3 gọi là upvector, từ này không biết dịch ra tiếng việt ra sao. Upvector , hãy tưởng tượng bạn đang theo dõi một vật, upvector chính là vector từ tim bạn lên đỉnh đầu, nếu thay đổi số liệu cũng tương tự như bạn nghiêng đầu sang phải sang trái. Vậy là 9 tham số đã rõ, bây giờ hãy bỏ lệnh glOrtho() đi và cho 2 lệnh đánh dấu đỏ vào, chúng ta sẽ quan sát được hình lập phương đó, mã nguồn nằm trong file cube2.cpp

3.9. Sử dụng ánh sáng trong opengl. Để xác định mặt nào được chiếu sáng và với cường độ sáng bao nhiêu, người ta dùng véc tơ pháp tuyến (normal vector). Trong chương trình dưới đây sẽ giới thiệu cách dùng véc tơ này. Mã nguồn:

/*filename: light1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #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},

21

Page 22: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

{1,5,6,2},{5,4,7,6},{4,0,3,7},{4,5,1,0},{3,2,6,7}

};

GLdouble normal[][3]={{0.0,0.0,-1.0},{1.0,0.0,0.0},{0.0,0.0,1.0},{-1.0,0.0,0.0},{0.0,-1.0,0.0},{0.0,1.0,0.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_LIGHTING); /*tham số mới*/glEnable(GL_LIGHT0); /*tham số mới*/

glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]); /*hàm mới*/for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

glDisable(GL_LIGHT0); /*tham số mới*/glDisable(GL_LIGHTING); /*tham số mới*/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);

22

Page 23: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

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;

}

Thông số mới trong hàm glEnable() và glDisable(), các thông số này cho phép chúng ta sử dụng ánh sáng trong khi tạo đồ vật. Opengl cung cấp cho chúng ta 8 nguồn sáng mà chương trình trên mới chỉ sử dụng một nguồn sáng LIGHT0, hàm glNormal3dv() thiết lập véc tơ pháp tuyến cho mỗi mặt chúng ta vẽ, vì thế khi vật thể quay quanh trục thì ta sẽ thấy độ sáng tối thay đổi.

Trong chương trình light0.cpp trên chúng ta không sử dụng màu nên màu sẽ được xác định là màu mặc định như các bạn đã thấy. Tiếp theo đây tôi xin giới thiệu cách sử dụng vật liệu để tạo vật với các màu sắc ấn tượng khác nhau.Chúng ta khai báo dữ liệu sau:

GLfloat ambient[]={ 0.2,0.2,0.2,1.0};GLfloat diffuse[]={ 0.8,0.0,0.0,1.0};GLfloat specular[]={0.5,0.5,0.5,1.0};GLfloat shininess=40.0;

Trong đó các thông số trên đều được đặt theo hệ RGBA(như đã đề cập ở các phần trên), chính vì thế mà chúng có 4 giá trị.Thông số ambient biểu hiện cho độ sáng của môi trường bên ngoài, diffuse là độ khuếch tán, specular là độ phản xạ và shininess là độ bóng(độ bóng sáng). Các thông số trên được hàm glMaterial*() sử dụng để tạo vật thể của chúng ta.Hãy quan sát cách khai báo hàm trên:

glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,shininess);

Chú ý là lệnh cuối cùng không phải là “fv” như bình thường, vì shininess chỉ là một số.Đây là mã nguồn của chương trình.

/*filename light1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK

23

Page 24: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

#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 normal[][3]={{0.0,0.0,-1.0},{1.0,0.0,0.0},{0.0,0.0,1.0},{-1.0,0.0,0.0},{0.0,-1.0,0.0},{0.0,1.0,0.0}

};

GLfloat ambient[]={ 0.2,0.2,0.2,1.0};GLfloat diffuse[]={ 0.8,0.0,0.0,1.0};GLfloat specular[]={0.5,0.5,0.5,1.0};GLfloat shininess=40.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);

24

Page 25: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glEnable(GL_DEPTH_TEST);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);

glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,shininess);

glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]);for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

glDisable(GL_LIGHT0);glDisable(GL_LIGHTING);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;

}

3.10. Vẽ nhiều vật - Dùng Ma trân. - Mã Nguồn :void cube(){

int i,j;

25

Page 26: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]);for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

}

Vì điểm nhìn không thay đôi nên khi cỡ của window thay đổi ta cũng phải thay đổi theo. Dưới đây là mã nguồn để thay đổi khung nhìn của chúng ta:

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);glLoadIdentity();gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

}

Chú ý là hai hàm cuối trong hàm trên đã được chuyển từ hàm draw() sang. Nếu thiết đặt ma trân như trên thì khi hàm glRotated() làm thay đổi vị trí của vật sẽ làm cho window của chúng ta trở nên không bình thường. Vì vậy trước khi dùng hàm glRotated() thì chúng ta phải lưu ma trận vào đã rồi khì thực hiện xong hàm này ta lại trả lại ma trận thì sẽ bình thương. Để làm việc này chúng ta sử dụng 2 hàm glPushMatrix() và glPopMatrix(). Mã nguồn được trình bày dưới đây.

/*filename matrix1.cpp*/#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #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}

};

26

Page 27: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

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 normal[][3]={{0.0,0.0,-1.0},{1.0,0.0,0.0},{0.0,0.0,1.0},{-1.0,0.0,0.0},{0.0,-1.0,0.0},{0.0,1.0,0.0}

};

void cube(){

int i,j;glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]);for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

}GLvoid CALLBACK none(void){}

GLvoid CALLBACK draw(void){

static int r=0;

glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);

glPushMatrix();glRotated((double)r,0.0,1.0,0.0);cube();glPopMatrix();

glDisable(GL_LIGHT0);glDisable(GL_LIGHTING);glDisable(GL_DEPTH_TEST);

auxSwapBuffers();if(++r>=360) r=0;

27

Page 28: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

}

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);glLoadIdentity();gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

}

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;

}

Bây giờ ta sẽ tìm cách để vẽ hai hình lập phương mà không mất công vẽ lại từng mặt của hình lập phương mới nữa. Để làm được điều này bạn dùng hàm glTranslated(), hàm này có 3 thông số, nó có nhiệm vụ chuyển ta đến vị trí mới để vẽ(qua ba thông số của nó).Thực ra nó nhân ma trận hiện tại với một ma trận khác để chuyển toạ độ cho chúng ta, vì thế mà chúng ta sẽ phải dùng 2 lần các hàm glPushMatrix() và glPopMatrix().Hãy nhớ là bao nhiêu lần gọi hàm glPushMatrix() thì cũng phải từng đấy lần gọi hàm glPopmatrix().Dưới đây là mã nguồn:

/*filename matrix2.cpp*/

#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #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}

28

Page 29: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

};

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 normal[][3]={{0.0,0.0,-1.0},{1.0,0.0,0.0},{0.0,0.0,1.0},{-1.0,0.0,0.0},{0.0,-1.0,0.0},{0.0,1.0,0.0}

};

void cube(){

int i,j;glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]);for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

}GLvoid CALLBACK none(void){}

GLvoid CALLBACK draw(void){

static int r=0;

glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);

glPushMatrix();glRotated((double)r,0.0,1.0,0.0);cube();

glPushMatrix();glTranslated(1.0,1.0,1.0);cube();glPopMatrix();

glPopMatrix();

29

Page 30: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glDisable(GL_LIGHT0);glDisable(GL_LIGHTING);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);glLoadIdentity();gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

}

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;

}

Tiếp theo chúng ta sẽ cho hình lập phương thứ hai quay với vận tốc khác, vận tốc mới của nó là 2*r, để làm được điều này bạn chỉ cần thêm hàm glRotated((double)(2*r),0.0,1.0,0.0); vào sau hàm glTranslated(1.0,1.0,1.0); v à trước hàm cube() thứ hai. Mã nguồn nằm trong file matrix3.cpp.Bây giờ hãy cùng tô màu cho chúng.

/*filename matrix4.cpp*/

#ifdef unix #include <GL/gl.h> #include "aux.h" #define CALLBACK #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},

30

Page 31: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

{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 normal[][3]={{0.0,0.0,-1.0},{1.0,0.0,0.0},{0.0,0.0,1.0},{-1.0,0.0,0.0},{0.0,-1.0,0.0},{0.0,1.0,0.0}

};

void cube(){

int i,j;glBegin(GL_QUADS);for(i=0;i<6;i++){

glNormal3dv(normal[i]);for(j=0;j<4;j++){

glVertex3dv(vertex[face[i][j]]);}

}glEnd();

}GLvoid CALLBACK none(void){}

GLvoid CALLBACK draw(void){

static int r=0;static GLfloat red[]={ 1.0,0.0,0.0,1.0};static GLfloat blue[]={ 0.0,0.0,1.0,1.0};

glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);

glPushMatrix();

31

Page 32: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

glRotated((double)r,0.0,1.0,0.0);glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,red);cube();

glPushMatrix();glTranslated(1.0,1.0,1.0);glRotated((double)(2*r),0.0,1.0,0.0);glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,blue);cube();glPopMatrix();

glPopMatrix();

glDisable(GL_LIGHT0);glDisable(GL_LIGHTING);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);glLoadIdentity();gluLookAt(3.0,4.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

}

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;

}

Đến đây thì không cần giải thích gì ta cũng đã hiểu chương trình này.

32

Page 33: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

CHƯƠNG 4. VẼ CÁC ĐỔI TƯỢNG HÌNH HỌC CƠ BẢN TRONG OPENGL

4.1. Vẽ điểm, đường, đa giác (Point, Line, Polygon)

4.1.1 OpenGL tạo ra điểm, đường, đa giác từ các đỉnh (Vertex).

Các đỉnh được liệt kê giữa hai hàm :

glBegin(tham số) ….glEnd();

Danh sách các tham số được liệt kê trong bảng 2.1

GL_POÍNT Các điểmGL_LINES đoạn thẳngGL_POLYGON Đa giác lồiGL_TRIANGLES Tam giácGL_QUADS Tứ giácGL_LINE_SRIP Đường gấp khúc không khép kínGL_LINE_LOOP Đường gấp khúc khép kínGL_TRIANGLE_STRIP Một giải các tam giác liên kết với nhauGL_TRIANGLE_FAN Các tam giác liên kết với nhau theo hình quạtGL_QUAD_STRIP Một giải các tứ giác liên kết với nhau

Một số kiểu dữ liệu:

Ký hiệu Kiểu dữ liệu Tên kiểu của Opengls 16-bít interger GLshorti 32- bit interger Glint, Glsizeif 32-bit floating-point Glfoat, GL.elamfd 64-bit floating-point Glfoat.double, GL.elampd

Ví dụ 2.1:

33

Page 34: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Sau đây là một số ví dụ khi ta chỉ định 1 điểm glVertex2s(2, 3); //Tọa độ thuộc kiểu GLshort trong không gian 2 chiều glVertex3d(0.0,0.0,3.1415926535898); //Tọa độ thuộc kiểu GLdouble trong không gian 3 chiều glVertex4f(2.3, 1.0, -2.2, 2.0); //Tọa độ đồng nhất kiểu GLfloat GLdouble dvect[3] = {5.0, 9.0,1992.0}; glVertex3dv(dvect); //Tọa độ được lấy từ mảng dvect

Ví dụ 2.2: Lệnh Kết quả hiển thị glBegin(GL_POLYGON);

glVertex2f(0.0, 0.0); glVertex2f(0.0, 3.0); glVertex2f(3.0, 3.0); glVertex2f(4.0, 1.5); glVertex2f(3.0, 0.0);

glEnd();

glBegin(GL_POINTS); glVertex2f(0.0, 0.0); glVertex2f(0.0, 3.0); glVertex2f(3.0, 3.0); glVertex2f(4.0, 1.5); glVertex2f(3.0, 0.0);

glEnd();

4.1.2. Một số lệnh khác Thiết lập màu nền cho của sổ hiển thị

glClearColor(red, green ,blue ,anpha); (0≤red, green, blue, anpha≤1)

Thiết lập màu cho đối tượng vẽ

glColor3f(red, green, blue); (0≤red, green, blue≤1)

Vẽ hình chữ nhật

glRect{sifd}(x1, y1, x2, y2);

Một số thủ tục trong GLUT cho phép vẽ hình trong không gian 3 chiều(solid là hình đặc, wire là hình khung)

Vẽ hình lập phương glutWireCube(GLdouble size); glutSolidCube(GLdouble size);

34

Page 35: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Vẽ hình cầu glutWireSphere(GLdouble radius, GLdouble slices, GLdouble stacks); glutSolidSphere(GLdouble radius, GLdouble slices, GLdouble istacks);

Vẽ hình đế hoa glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLdouble nsides, GLdouble rings); glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLdouble nsides, GLdouble rings);

Vẽ hình ấm pha trà glutWireTeapot(GLdouble size); glutSolidTeapot(GLdouble size);

Vẽ hình nón glutWireCone(GLdouble Radius, GLdouble height, GLint slices, GLint stacks); glutSolidCone(GLdouble Radius, GLdouble height, GLint slices, GLint stacks);

4.2. Tập lồi và phép đạc tam giác hợp lệ (Convexity and Valid Triangulation)

4.2.1 Tập lồi Một tập S được gọi là lồi (convex) nếu với 2 điểm bất kì P,Q thuộc S thì đoạn thẳng PQ nằm trọn vẹn trong S

Hình 2.3 Tập lồi và tập không lồi Cho một tập hợp các điểm T={P1, P2,...., Pn}, bao lồi (convex hull) của T là tập lồi nhỏ nhất F chứa T. Khi đó bất kì điểm X thuộc F đều có thể biểu diễn dưới dạng như sau: X=α1P1+α2P2+....+αnPn (0≤αi, α1+α2+ ....+αn=1) Trường hợp đặc biệt: - Một đoạn thẳng AB luôn là một tập lồi, một điểm P bất kỳ thuộc AB được biểu diễn duy nhất dưới dạng P=α1A+α2B (αi≥0, α1+α2=1) - Một tam giác ABC luôn là một tập lồi, một điểm P bất kỳ thuộc ABC

35

Page 36: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

được biểu diễn duy nhất dưới dạng P=α1A+α2B+α3C (αi≥0, α1+α2+α3=1) OpenGL tô màu đoạn thẳng và tam giác bằng cách nội suy véc tơ màu (interpolating the color vectors) tại mỗi đỉnh của đoạn thẳng và tam giác. Ví dụ nếu màu tại ba đỉnh A, B, C của tam giác lần lượt là C1, C2, C3 khi đó nếu điểm P thuộc tam giác ABC và P=α1A+α2B+α3C thì màu tại điểm P là α1C1+α2C2+α3C3

4.2.2 Phép đạc tam giác hợp lệ Một phép đạc tam giác hợp lệ của một hình X là một tập hợp các tam giác thỏa mãn hai điều kiện sau: - Hợp của các tam giác chính là hình X - Hai tam giác bất kì thỏa mãn: Tách rời nhau hoặc chung nhau duy nhất 1 đỉnh hoặc chung nhau duy nhất một cạnh

Hình 2.4 Phép đạc tam giác hợp lệ và không hợp lệ

Trong OpenGL khi áp dụng một phép đạc tam giác cho một tập lồi thì yêu cấu bắt buộc đó phải là một phép đạc tam giác hợp lệ. Tại sao phải có yêu cầu như vậy? Ta hãy xét ví dụ tại hình 2.3(b), giả sử E là trung điểm cạnh AC và các véc tơ biểu diễn màu tại các điểm A, E và C lần lượt là (1,0,0), (0,1,0) và (0,0,1). Xét trong tam giác ABC, vì E là trung điểm cạnh AC do đó màu tại E là trung bình cộng màu tại A và C do đó màu tại E được tính là (0.5,0,0.5) điều đó có nghĩa là cùng tại một điểm E có hai cách biểu diễn màu khác nhau.

4.3.Phép biến đổi điểm nhìn và biến đổi mô hình (Viewing and Modeling Transformations)

OpenGL sử dụng ma trận và phép nhân ma trận để biểu diễn các phép biến đổi (ta sẽ đề cập đến vấn đề này sau) do đó một chú ý quan trọng là trước khi áp dụng các phép biến đổi ta phải xóa và thiết lập ma trận hiện thời (current matrix) về ma trận

36

Page 37: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

đơn vị (identity matrix) bằng lệnh glLoadIdentity().

4.3.1 Phép biến đổi điểm nhìn Phép biến đổi điểm nhìn tương tự như ta chọn vị trí và hướng của camera trong thực tế vì vậy ta sẽ quy ước tại vị trí điểm nhìn (viewpoint) có đặt một camera cho dễ hình dung. OpenGL sử dụng lệnh gluLookAt() để định vị vị trí và hướng của điểm nhìn, cú pháp lệnh như sau gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,

GLdouble centerx, GLdouble centery, GLdouble centerz,

GLdouble upx, GLdouble upy, GLdouble upz) Trong đó: (eyex, eyey, eyez) là vị trí của điểm nhìn, (centerx, centery, centerz) chỉ định một điểm nào đó thuộc đường ngắm và (upx, upy, upz) xác định hướng đỉnh của camera (bản chất chính là hướng từ đáy lên đỉnh của viewing volume). Ví dụ lệnh gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) sẽ định vị camera tại vị trí (0.0, 0.0, 5.0), đường ngắm là hướng là hướng âm của trục oz (nhìn về gốc tọa độ) và hướng của đỉnh là hướng dương của trục oy Mặc định vị trí của camera tại gốc tọa độ, đường ngắm là hướng âm của trục oz và hướng của đỉnh là hướng dương của trục oy

Hình 2.6 Vị trí mặc định của điểm nhìn là tại gốc tọa độ

4.3.2 Phép biến đổi mô hình Bản chất phép biến đổi mô hình là xác định ví trí và hướng của mô hình (đối tượng cần vẽ trong không gian). Ví dụ chúng ta có thể áp dụng các phép tịnh tiến (translation), phép quay (rotation), phép tỉ lệ (scale) hoặc kết hợp các phép biến đổi

37

Page 38: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

đó với nhau. Một số lệnh liên quan đến biến đổi mô hình của OpenGL Phép tịnh tiến: glTranslate{fd}(TYPE x, TYPE y, TYPE z);

Ý nghĩa: Tịnh tiến mô hình theo véc tơ tịnh tiến (x, y, z) Phép quay: glRotate{fd}(TYPE angle, TYPE x, TYPE y, TYPE z);

Ý nghĩa: Quay mô hình một góc angle ngược chiều kim đồng hồ xung quanh tia nối từ gốc tọa độ đến điểm (x, y, z) Phép tỉ lệ: glScale{fd}(TYPE x, TYPE y, TYPE z);

Ý nghĩa: Biến đổi tỉ lệ mô hình với hệ số tỉ lệ tương ứng với ba trục ox, oy, oz lần lượt là x, y, z Chú ý: Với x=-1, y=1, z=1 thì phép tỉ lệ trở thành phép đối xứng qua mặt phẳng oyz, tương tự với mặt phẳng oxy và oxz.

4.3.3. Cách chuyển đổi từ phép biến đổi điểm nhìn sang phép biến đổi mô hình Ta đã biết cách sử dụng câu lệnh gluLookAt() để thay đổi vị trí và hướng của camera, đây là câu lệnh thuộc OpenGL Utility Library (GLU). Ta cũng đã biết các câu lệnh của OpenGL thực hiện phép biến đổi mô hình đó là glTranslate*(), glRotate*() và glScale*(). Thực chất phép biến đổi điểm nhìn có thể được thay thế bởi một vài câu lệnh của phép biến đổi mô hình. Quay lại ví dụ gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) sau lệnh này camera được tịnh tiến từ vị trí mặc định là gốc tọa độ về điểm (0.0, 0.0, 5.0), điều đó tương đương với việc giữ nguyên camera tại gốc tọa độ và tịnh tiến vật thể 5 đơn vị về hướng ngược lại dọc theo trục oz glTranslatef(0.0, 0.0, -5.0). Xét trường hợp tổng quát: gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz) Để thay thế lệnh trên bằng các phép biến đổi mô hình ta áp dụng các phép biến đổi mục đích là đưa camera về vị trí mặc định (tại gốc tọa độ, hướng nhìn về hướng âm của trục oz và hướng đỉnh là hướng dương của trục oy).

Bước 1: Tịnh tiến camera về gốc tọa độ bằng cách áp dụng lệnh glTranslatef(-eyex,-eyey,-eyez)

Bước 2: Áp dụng các phép quay để đưa hướng nhìn của camera về hướng âm của oz và đưa hướng đỉnh của camera về hướng dương của oy

Ví dụ 2.3: Lệnh gluLookAt(5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, -1.0); được thay thế tương đương bởi các câu lệnh sau glRotatef(45, 0.0, 0.0, 1.0);//Quay một góc 45 độ quanh trục oz đưa hướng đỉnh camera về hướng dương trục oy glRotatef(-90, 0.0, 1.0, 0.0);//Quay một góc -90 độ quanh trục oy đưa hướng nhìn camera về hướng âm trục oz glTranslatef(-5.0, 0.0, 0.0);//Đưa camera về gốc tọa độ

Chú ý : Thứ tự thực hiện các lệnh theo thứ tự ngược lại thứ tự của các câu lệnh. Quá trình được minh họa trên hình 2.7, mũi tên màu xanh chỉ hướng nhìn của camera và mũi tên màu đỏ nét đứt chỉ hướng đỉnh của camera

38

Page 39: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Hình 2.8 Chuyển biến đổi điểm nhìn sang biến đổi mô hình

4.3.4. Phép biến đổi cổng nhìn (Viewport Transformation)

Cổng nhìn là một miền hình chữ nhật nằm phía trong một cửa sổ (window) đang mở trên màn hình cho phép hiển thị hình ảnh. Hình ảnh của vật thể sau khi được chiếu lên khung nhìn sẽ được ánh xạ lên cổng nhìn. Ta có thể định nghĩa nhiều cổng nhìn để hiển thị nhiều khung cảnh khác nhau trên cửa sổ. Định nghĩa cổng nhìn ta dùng câu lệnh sau: glViewport(GLint x, GLint y, GLsizei width, GLsizei height); Trong đó (x,y) xác định tọa độ góc trái phía dưới của cổng nhìn, tọa độ này là tọa độ trong cửa sổ đang mở. Góc trái phía dưới của cửa sổ có tọa độ là (0,0). Hai tham số width và height là chiều rộng và chiều cao của khung nhìn. Ở chế độ mặc định giá trị tham số của khung nhìn là (0, 0, winWidth, winHeight) trong đó winWidth và winHeight là kích thước của cửa sổ nơi hiển thị khung nhìn.

39

Page 40: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Hình 2.12 Màn hình máy tính, cửa sổ và cổng nhìn

Để định nghĩa của sổ trên màn hình ta dùng hai câu lệnh sau của GLUT: glutInitWindowPosition (int x, int y); //Chỉ định vị trí góc trái trên của

cửa sổ trong hệ tọa độ xoy của màn hình (Hình 2.11) glutInitWindowSize (int Width, int Height); //Chỉ định kích thước của cửa

sổ (pixels), hai kích thước thường được chọn bằng nhau để hình không bị méo Tỉ lệ hai kích thước của cổng nhìn nên chọn bằng với tỉ lệ hai kích thước của khung nhìn. Nếu hai tỉ lệ này không bằng nhau thì hình ảnh hiển thị trên màn hình sẽ bị méo (distorted). \

Hình không bị méo Hình bị méo

Hình 2.13 Ảnh được ánh xạ từ khung nhìn lên cổng nhìn

4.3.5. Điều khiển các ngăn xếp ma trận ( Matrix Stacks)

OpenGL biểu diễn các phép biến đổi điểm nhìn và mô hình bằng ngăn xếp ma trận Modelview (Modelview matrix stack). Ngăn xếp này có tối đa là 32 phần tử là các ma trận vuông 4x4 tương ứng các phép biến đổi tác động lên đối tượng vẽ và ma trận hiện thời luôn là ma trận nằm trên đỉnh của stack. Mỗi một phép biến đổi điểm nhìn hoặc biến đổi mô hình tạo ra một ma trận mới, ma trận này được nhân với ma trận hiện thời và kết quả trở thành ma trận hiện thời mới (ngăn xếp thêm 1 phần tử mới trên đỉnh). Để biểu diễn các phép chiếu, OpenGL sử dụng một ngăn xếp khác được gọi là ngăn

40

Page 41: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

xếp ma trận phép chiếu (Projection matrix stack) gồm tối đa 2 phần tử ma trận vuông 4x4. Ma trận hiện thời mô tả phép chiếu đang được áp dụng cho đối tượng vẽ, hay nói cách khác ma trận này mô tả viewing volume.

Hình 2.14 Ngăn xếp ma trận modelview và ma trận phép chiếu Ta có thể điều khiển

ma trận hiện thời bằng các câu lệnh sau:glLoadIdentity(); //Thiết lập ma trận hiện thời về ma trận đơn vị glLoadMatrix{fd}(const TYPE *m); // Thiết lập giá trị cho ma trận hiện

thời từ 16 giá trị được trỏ bởi m (m là ma trận một chiều hoặc hai chiều) glMultMatrix{fd}(const TYPE *m); //Nhân ma trận có 16 giá trị được trỏ

bởi m với ma trận hiện thời, kết quả là ma trận hiện thời mới glPushMatrix(); //Sao chép thêm một ma trận hiện thời và đưa lên đỉnh

của ngăn xếp glPopMatrix(); //Loại bỏ ma trận hiện thời khỏi ngăn xếp

41

Page 42: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Hình 2.15 Sao chép và loại bỏ ma trận hiện thời

Lệnh glPushMatrix(); được hiểu là "Ghi nhớ ta đã ở đâu" (remember where you are) và glPopMatrix(); được hiểu là "Quay lại vị trí ta đã ở" (go back to where you were). Đây là hai câu lệnh rất hữu ích của OpenGL.

Việc tổ chức các ma trận theo ngăn xếp là rất phù hợp khi ta cần xây dựng các mô hình có sự phân cấp (hierarchical models) trong đó một đối tượng phức tạp được xây dựng từ những đối tượng đơn giản hơn. Đặc biệt khi đó là một hình ảnh động, sụ thay đổi trạng thái của một bộ phận kéo theo sự thay đổi của nhiều bộ phận khác. Giả sử ta có đoạn chương trình như sau mô tả chuyển động một cánh tay của một robot:

glTranslatef(); //Lệnh T1 glRotatef(); //Lệnh T2

glPushMatrix(); glTranslatef(); //Lệnh T3 glRotatef(); //Lệnh T4 gluCylinder(); //Vẽ cánh tay phía dưới glPopMatrix();

glScalef(); //Lệnh T5 gluCylinder(); //Vẽ cánh tay phía trên

Đoạn chương trình trên có thể có thể được diễn tả bởi sơ đồ sau T1→ T2→ T5→ Cánh tay phía trên T3→ T4→ Cánh tay phía dưới Muốn biết ảnh hưởng của các câu lệnh với hai hình lăng trụ ta duyệt sơ đồ trên theo thứ tự ngược lại. Đây là những gì tác động lên cánh tay phía trên: T5: Kéo dãn đối tượng

42

Page 43: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

T2: Quay đối tượng quanh một trục xuyên qua tâm của nó T1: Tịnh tiến đối tượng Đây là những gì tác động lên cánh tay phía dưới: T4: Quay đối tượng quanh một trục xuyên qua trọng tâm của nó T3: Tịnh tiến đối tượng T2: Quay đối tượng một lần nữa, lần này trục quay không qua tâm của nó T1: Tịnh tiến đối tượng một lần nữa Nhận xét: - Phép quay T2 tương đương với động tác quay tại khớp vai và phép quay T4 tương đưong với động tác quay tại khuỷu tay - T3 và T4 nằm giữa hai lệnh glPushMatrix() và glPopMatrix() chỉ tác động lên cánh tay phía dưới - Lệnh T1 và T2 tác động lên cánh tay phía trên và đương nhiên cũng có tác động đến cánh tay phía dưới vì khi cánh tay phía trên chuyển động thì cánh tay phía dưới cũng chuyển động theo.

Phần B. Ví dụ mô phỏng

Phần ví dụ này, nhóm Sử dụng VC++ kết hợp với OpenGl để mô phỏng động học robot. Nhóm em xin trình bày chi tiết như sau:

I. Môi trường làm việc trong VC++:Trước hết ta khởi động VC++, chọn New để tạo một ứng dụng mới:

43

Page 44: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Trong hộp thoại này ta chọn MFC app Winzard(exe):tạo ứng dụng win32 với thư viện MFC.Sauk hi click OK ta được hộp thoại bên dưới:

44

Page 45: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Sau khi nhấn Finish, Winzard sẽ tạo cho chúng ta 5 lớp cơ bản:CDIEUApp: Đây là lớp dung để đăng ký tạo một ứng dụngCDIEUDoc: Đây là lớp để lưu trữ dữ liệuCDIEUView: Đây là lớp cài đặt những gì thể hiện lên màn hình của ứng dụngCAboutDlg: Đây là lớp quản lý một Dialog mà mục đích của Wizard cung cấp cho chúng ta để hiển thị thông tinCmainFrame: dùng để quản lý,khởi tạo các khung ứng dụng,các ToolBar,Menu…Ngoài ra chúng ta còn được cung cấp 3 thể để quản lý dự án:+Thẻ Class View: dùng để quản lý lớp và các hàm tự do nằm trong dự án+Thẻ ResourceView: dùng quản lý các tài nguyên của dự án+Thẻ FileView:Quản lý các file nằm trong dự án.Thêm thư viện OpenGL cho ứng dụng và thêm các file khai báo và cài đặt(ST_SplitterWnd.CPP và ST_SplitterWnd.H) Vào Project/Add to Project/Files..

45

Page 46: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Khai báo và sử dụng lớp ST_SplitterWnd: Đây là lớp dung để chia đôi khung ứng dụng khi chạy chương trình.Ta nhấn đúp vào lớp CMainFrame trong ClassView/file MainFrm.hTa phải khai báo thêm :#include"Splitter/ST_SplitterWnd.h"Các bạn cũng phải nhớ là phải setting trong project nhé:

46

Page 47: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Ngoài ra khi sử dụng thư viện OpenGl thì ta phải khai báo trong lớp View

#include "OpenGL_LIB\InitOpenGL.h"

47

Page 48: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

#include "OpenGL_LIB\ObjectsOpenGL.h"using namespace ObjectsOpenGL;Đồng thời khai báo các biến con trỏ để khởi tạo thiết bị vẽ và môi trường vẽ:

Ta tiến hành thiết lập môi trường đồ hoạ OpenGL trong File DIEUView.CPP

II- Sử dụng VC++ kết hợp với OpenGl để mô phỏng động học robot

Tiếp theo ta sẽ thêm tài nguyên Dialog vào để tạo ra được các form hiển thị cũng như các nút điều khiển (<< >>) Ta setting cho có thể link đươc thư viện:

48

Page 49: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Sauk hi hộp thoại hiện ra thì chọn IDD_FORMVIEW thì ta sẽ thấy hình ảnh form để design được thông qua control bar(có thể đặt tên của formview này thành 1 cái tên khác cho dễ quản lý.( Đối với việc chèn các biểu tượng như biểu tượng trường hay hình ảnh nào đó cũng làm tương tự, ta chỉ việc đổi từ chọn IDD_FORMVIEW

49

Page 50: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

thành BITMAP thôi.)).

Từ trên phần điều khiển formview mà ta vừa design, click chuột phải và chọn Class wizard, sau khi nhấn OK nó sẽ ra bảng sau: Ta nhập tên nào đó vào( ví dụ là CCTRPANEL), lớp cơ sở là CFormView.

50

Page 51: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

51

Page 52: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Sau khi nhấn OK thì ta đã thêm được lớp CCTRPANEL vào.

Thêm một số thuộc tính vào lớp CMainFrame để chia đôi khung ứng dụng( nếu không thì rất khó để quản lý và điều chỉnh).Tiếp theo, thêm các hàm Oncreate, On Destroy, và Onsize vào để có thể thay đổi kích thước hay giá trị:

52

Page 53: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Nhấn Ctrl+W. Sau đó chọn lớp CMainFrame ở trên class name, chọn CMainFrame ở Object IDs, Bên hộp messages chọn OnCreateClient. Sau khi nhấn edit code thì hàm BOOL CMainFrame:: OnCreateClient hiện ra, edit lại hàm như bên dưới:

53

Page 54: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Chú ý là nếu đặt giá trị biến là 1( m_Splitter.Create(this,RUNTIME_CLASS(CCTRPANEL),RU NTIME_CLASS(CCNCView),pContext,1) thì sẽ chia đôi theo hang dọc, còn 0 thì chia đôi theo hàng ngang.Tuy nhiên khi biên dịch chương trình có thể sẽ xảy ra hiện tượng lỗi, điều này có thể xảy ra khi ta đã sử dụng con trỏ *GetDocument() của lớp CCNCDoc mà chưa khai báo. Ta vào lớp CCNCView.h để sửa lại:Class CCNCDoc; vào.

Như vậy là cũng đã tương đối ổn rồi, tiếp theo ta chỉnh sửa trong project để link thư viện OpenGL vào dự án. Tiếp theo cài đặt hàm OnInitialUpdate() để có thể Update các khâu, khớp cũng như những thay đổi của robot:

54

Page 55: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Như vậy công đoạn cũng gần hoàn thiện, tiếp theo ta xây dựng hàm vẽ vật thể( Robot hay cơ cấu máy chúng ta đã thiết kế sẵn trên Solids, Catia hay Inventor rồi, sau đó ta lưu định dạng các file này dưới dạng *.STL), hàm OnDraw này sẽ chỉ có nhiệm vụ là kết nối các file này vào và lắp ghép chúng theo sự điều khiển củaât.Ta cần chú ý một điều là khoảng cách cũng như góc quay nó sẽ được tính theo khoảng cách của từng chi tiết (các part bạn thiết kế 3D) và được bắt đầu làm mốc từ O(0,0,0). Nếu ta đặt hệ tọa độ địa phương(coordinate) thì ta sẽ dựa vào đó để thực hiện hàm tịnh tiến và hàm quay.Tuy nhiên, thông thường người ta sẽ không vẽ trực tiếp trên hàm OnDraw này mà dùng hàm OnDraw để quản lý hàm paint():void CPSKView::OnDraw(CDC* pDC){CPSKDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);

55

Page 56: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Paint(); }Để có được hàm Paint() ta click chuột phải vào lớp CCNCView và chọn Add Member Function và làm như bên dưới:

Khi đã được hàm Paint() thì thiết lập chương trình vẽ vật thể trong đó. Phần bôi đen

56

Page 57: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

chính là phần mà dùng cài đặt màu của từng chi tiết.

Sau khi chỉnh sửa, ta có được hình ảnh của robot thông qua cửa sổ View. Việc tiếp theo ta cài đặt chương trình tính toán cho robot. Muốn được điều này ta phải có được bảng thông số động học cũng như giải được phương trình động học ngược. Khi đã có tất cả những thứ trên rồi, ta tiếp tục vào lớp CCTRPANEL để tạo ra hàm Calculate()(Cách làm tương tự hàm paint()).

Ta viết chương trình tính toán vào hàm đó. Khi đó ta sẽ thấy được các chuyển động của robot theo chương trình tính toán:

57

Page 58: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

58

Page 59: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Ngoài ra, để tạo các hiệu ứng như kéo nhả chuột, rê chuột.. chúng ta cũng có thể làm được bằng các hàm như OnLButtonDown() OnLButtonUp(), OnMouseMove(), OnMouseWheel()…Muốn có được các hàm này hãy nhìn bên hình vẽ dưới:

59

Page 60: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

Vậy nhóm em đã hướng dẫn cách mô phỏng chương trình robot thông qua VC++. Tất nhiên sẽ phải sửa nhiều chương trình thì mới chạy được.Sau đây là bonus một số chương trình hoàn thiện:

60

Page 61: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

61

Page 62: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

62

Page 63: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

63

Page 64: Bai Tap Lon Tim Hieu Co Ban Ve Opengl

TÀI LIỆU THAM KHẢO [1] http://www.videotutorialsrock.com/[2] Lập trình Opengl với thư viện AUX - Bùi Minh Trường [3] The OpenGL Utility Toolkit (GLUT) Programming Interface - Mark J. Kilgard. [4] www.hua.edu.vn[5]. Đề tài OpenGL các khóa trước…….

64