Thứ Hai, 4 tháng 6, 2012

Open GL Tiếng Việt Bài 6


OpenGL Tiếng Việt
Đọc bài gốc + download code minh họa bằng nhiều ngôn ngữ tại trang của tác giả. Đây chỉ là bản dịch do mình tự dịch.
http://nehe.gamedev.net/tutorial/texture_mapping/12038/
Bài 5 - Ánh xạ Texture (Texture Mapping)
Vẽ chi tiết lên bề mặt có rất nhiều lợi ích. Ví dụ bạn muốn một cái tên lửa bay qua màn hình. Bài này ta sẽ tạo ra một cái tên lửa. Với việc ánh xạ chi tiết hình ảnh lên bề mặt (texture mapping), bạn sẽ có một hình ảnh thực sự của cái tên lửa và làm nó bay qua màn hình. Cái tên lửa được xạ ảnh chỉ là 1 cái hình vuông bay qua màn hình. Một cái tên lửa có thể được làm từ hàng trăm, hàng ngàn đa giác. Ta dùng một cái hình vuông rồi xạ ảnh lên thì nó đỡ tốn năng lực xử lý máy tính hơn.
----------------------------------------
Lưu ý: cách load texture này không được khuyên dùng và đã không còn hoạt động với phiên bản hiện tại của Visual Studio. Lý thuyết mà tôi trình bày thì vẫn đúng. Bản code cập nhật được đặt tại đây: http://nehe.gamedev.net/tutorial/lesson_06_texturing_update/47002/
-----------------------------------------
Ta hãy bắt đầu bằng việc thêm năm dòng code vào trên của code ở phần 1. Đầu tiên là #include . Cái header này cho ta làm việc với file, sử dụng hàm fopen() ở đoạn code phía dưới thì ta phải include cái header stdio.h vào. Tiếp ta thêm ba biến mới là xrot, yrot, zrot. Mấy biến này dùng cho việc xoay khối hộp trên các trục x, y, z. Dòng cuối cùng là GLuint texture[1] để lưu trữ texture, nếu thích load nhiều texture hơn thì bạn thay số một bằng số texture mà bạn thích.
#include // Header File For Windows
#include // Header File For Standard Input/Output ( NEW )
#include // Header File For The OpenGL32 Library
#include // Header File For The GLu32 Library
#include // Header File For The GLaux Library
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag
bool fullscreen=TRUE;// Fullscreen Flag
GLfloat xrot; // X Rotation ( NEW )
GLfloat yrot; // Y Rotation ( NEW )
GLfloat zrot; // Z Rotation ( NEW )
GLuint texture[1]; // Storage For One Texture ( NEW )
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
Ngay sau đoạn code trên, và trước đoạn ReSizeGLScene(), ta thêm đoạn code sau. Đoạn code này sẽ load một file ảnh bitmap. Nếu file không tồn tài thì trả về NULL để thông báo rằng việc load texture thất bại. Trước khi tôi bắt đầu giải thích đoạn code thì có một số điều RẤT quan trọng mà bạn cần biết về các tấm ảnh mà bạn định dùng làm texture. Chiều rộng và dài của nó phải bằng 2^n. Chiều rộng và dài ít nhất phải là 64pixels, và vì lý do tương thích, không nên vượt quá 256 pixel. Nếu ảnh của bạn không rộng và dài 64, 128, 256 pixel thì dùng một phần mềm nào đó kéo cỡ nó về cỡ đó. Có rất nhiều điều cần nói về cái vấn đề này, nhưng ta cứ thế đã.
Trước tiên tạo một file handle. Một handle là một giá trị dùng để định danh một tài nguyên để chương trình của ta có thể truy cập nó. Khởi tạo giá trị đó = NULL.

AUX_RGBImageRec *LoadBMP(char *Filename) // Loads A Bitmap Image
{
FILE *File=NULL; // File Handle

Tiếp ta kiểm tra xem người ta có đưa filename thật không. Vì ta tất nhiên là không muốn load không-gì-cả :)

if (!Filename) // Make Sure A Filename Was Given
{
return NULL; // If Not Return NULL
}

Nếu đưa filename thật thì kiểm tra xem file đó có tồn tại trên máy tính không. Dòng dưới đây sẽ mở file.
File=fopen(Filename,"r"); // Check To See If The File Exists
Nếu mở được file này thì có nghĩa là nó tồn tại. Ta đóng file lại với fclose(File) sau đó trả về dữ liệu ảnh. auxDIBImageLoad(Filename) đọc vào dữ liệu.
if (File) // Does The File Exist?
{
fclose(File); // Close The Handle
return auxDIBImageLoad(Filename); // Load The Bitmap And Return A Pointer
}

Nếu ta không mở được file thì ta trả về NULL, để thông báo rằng file không load được. Sau này trong chương trình ta sẽ kiểm tra xem file có được load không. Nếu chưa được load thì ta sẽ thoát chương trình với một thông báo lỗi.
return NULL; // If Load Failed Return NULL
}


Đây là đoạn code sẽ load ảnh bitmap (gọi đoạn ở trên) và chuyển đổi nó để đưa vào texture.
int LoadGLTextures() // Load Bitmaps And Convert To Textures
{


Ta sẽ thiết lập một biến gọi là Status. Ta sẽ sử dụng biến này để theo dõi xem ta đã load ảnh bitmap và dựng texture hay chưa. Ta khởi tạo Status bằng FALSE.
int Status=FALSE; // Status Indicator
Giờ ta tạo một bản ghi ảnh để ta lưu ảnh bitmap vào. Bản ghi này lưu thông tin rộng, dài và dữ liệu ảnh.
AUX_RGBImageRec *TextureImage[1]; // Create Storage Space For The Texture
Ta xóa trắng bản ghi này để cho nó rỗng cái đã.
memset(TextureImage,0,sizeof(void *)*1); // Set The Pointer To NULL
Giờ ta load bitmap và chuyển đổi đưa nó vào một texture.
TextureImage[0]=LoadBMP(“Data/NeHe.bmp”) sẽ gọi hàm LoadBMP(). Cái file NeHe.bmp trong thư mục Data sẽ được load. Nếu mọi thứ suôn sẻ, dữ liệu ảnh sẽ được lưu trong TextureImage[0], Status được đặt = TRUE, và ta bắt đầu dựng texture.
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
{
Status=TRUE; // Set The Status To TRUE


Giờ thì ta vừa mới load dữ liệu ảnh vào trong TextureImage[0], ta sẽ xây dựng texture sử dụng dữ liệu này. Dòng đầu tiên, glGenTextures(1,&texture[0]) nói cho OpenGL biết rằng ta muốn ta muốn sinh một tên texture (tăng số lên nếu bạn muốn load nhiều hơn 1 texture). Nhớ rằng ở phần trước của bài này ta đã tạo một không gian cho một texture bằng dòng GLuint texture[1]. Và nhớ là trong C thì mảng bắt đầu từ 0.
Dòng thứ hai glBindTexture(GL_TEXTURE_2D, texture[0]) bảo OpenGL gán texture đã được đặt tên là texture[0] vào một kết cấu đích (texture target). Các 2D texture có cả chiều cao lẫn chiều rộng. Mục đích chính của hàm glBindTexture là gán một tên texture vào dữ liệu texture. Trong trường hợp này ta bảo với OpenGL là có một vùng nhớ khả dụng tại &texture[0]. Khi ta tạo texture, nó sẽ được lưu trữ tại vùng nhớ mà &texture[0] tham chiếu tới.


glGenTextures(1, &texture[0]); // Create The Texture
// Typical Texture Generation Using Data From The Bitmap
glBindTexture(GL_TEXTURE_2D, texture[0]);


Tiếp đến ta tạo texture thật sự. Dòng tiếp đây nói cho OpenGL biết là texture đó sẽ là một texture 2D (GL_TEXTURE_2D). Cái chỗ mà ta để số 0 là mức độ chi tiết của các tấm ảnh, thường thì ta để nó bằng 0. 3 là số thành phần dữ liệu. Bởi vì ảnh được tạo bởi các dữ liệu màu đỏ, lục, lam nên ta để nó là 3. TextureImage[0]->sizeX là độ rộng của texture. Nếu bạn biết độ rộng của nó thì ghi vào đây, nhưng mà cứ để máy tính nó tự làm thì dễ hơn. TextureImage[0]->sizeY là chiều cao của texture. Số không tiếp theo là viền, thường ta để bằng 0. GL_RGB bảo với OpenGL là dữ liệu ảnh được tạo từ các dữ liệu màu đỏ, lục, lam theo thứ tự đó. GL_UNSIGNED_BYTE nghĩa là dữ liệu ảnh ở đây là kiểu BYTE không dấu. Cuối cùng, TextureImage[0]->data chỉ cho OpenGL biết nơi để nó lấy dữ liệu texture. Trong trường hợp này nó chỉ đến dữ liệu lưu trong bản ghi TextureImage[0].


// Generate The Texture
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);


Hai dòng tiếp theo ta chỉ định kiểu bộ lọc (filtering) sử dụng khi mà ảnh nó to hơn (GL_TEXTURE_MAG_FILTER) hoặc bị kéo dãn so với texture gốc, hoặc khi nó nhỏ hơn (GL_TEXTURE_MIN_FILTER) so với texture thực sự. Tôi thường dùng GL_LINEAR cho cả 2. GL_LINEAR làm cho texture nhìn mịn hơn khi ở xa hay gần màn hình. Dùng GL_LINEAR tốn hiệu năng xử lý của vi xử lý/card đồ họa cho nên nếu máy bạn chậm thì bạn nên dùng GL_NEAREST. Texture được lọc (filtered) với GL_NEAREST nhìn sẽ vỡ vỡ vuông vuông khi nó bị kéo dãn. Bạn cũng có thể kết hợp cả hai. Lọc khi nó ở gần, và không lọc khi ở xa.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
}


Giờ ta giải phóng bộ nhớ đã dùng để lưu giữ liệu ảnh bitmap. Ta kiểm tra xem dữ liệu ảnh có được lưu trong TextureImage[0] không. Nếu có ta giải phóng nó đi. Sau đó ta giải phóng image structure.
if (TextureImage[0]) // If Texture Exists
{
if (TextureImage[0]->data) // If Texture Image Exists
{
free(TextureImage[0]->data); // Free The Texture Image Memory
}


free(TextureImage[0]); // Free The Image Structure
}


Cuối cùng trả về trạng thái. Nếu mọi thứ ổn cả thì biến Status sẽ là TRUE. Nếu có cái gì không ổn thì Status sẽ để là FALSE.
return Status; // Return The Status
}


Tôi mới thêm mấy dòng vào hàm InitGL. Để tiện theo dõi thì tôi sẽ post lại đoạn code. Dòng đầu if(!LoadGLTexture()) gọi cái hàm LoadGLTexture() để load ảnh bitmap và tạo texture từ nó. Nếu có vấn đề gì thì nó trả về FALSE. Nếu mọi thứ ổn cả, texture sẽ được tạo ra, ta enable 2D texture mapping (bật xạ kết cấu 2D lên). Nếu bạn quên không bật xạ texture thì hình của bạn thường sẽ có màu trắng trông chả đẹp tí nào.
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
if (!LoadGLTextures()) // Jump To Texture Loading Routine ( NEW )
{
return FALSE; // If Texture Didn't Load Return FALSE ( NEW )
}
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping ( NEW )
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
return TRUE;
}

Giờ ta vẽ khối hộp. Bạn có thể thay đoạn code DrawGLScene bằng đoạn code dưới đây, hoặc có thể thêm các dòng mới vào đoạn code chúng ta tạo trước đó ở bài 1. Đoạn code này được chú thích chi tiết nên rất dễ hiểu. glClear() với lại glLoadIdentity() thì giống như trong bài 1. glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) sẽ xóa màn hình bằng màu mà ta đã chọn trong InitGL(). Trong trường hợp này màn hình bị xóa thành màu đén. Bộ đệm chiều sâu (the depth buffer) cũng sẽ bị xóa. Tầm nhìn (the view) được reset với glLoadIdentity().
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Matrix
glTranslatef(0.0f,0.0f,-5.0f); // Move Into The Screen 5 Units


Ba dòng code tiếp đây sẽ xoay hình khối trên trục x, y rồi z. Xoay bao nhiêu thì tùy vào mấy biến xrot, yrot, zrot.
glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis
glRotatef(zrot,0.0f,0.0f,1.0f); // Rotate On The Z Axis


Đoạn code tiếp chọn texture mà ta định dùng. Nếu muốn dùng nhiều hơn một texture trong scene của bạn thì bạn phải chọn texture bằng glBindTexture(GL_TEXTURE_2D, texture[số texture sử dụng]) Nếu muốn thay đổi texture, bạn phải gán (bind) vào texture mới. Một điều cần lưu ý là bạn không được gán (bind) texture giữa hai dòng glBegin() và glEnd(). Bạn phải làm việc này trước hoặc sau chúng. Chú ý cách ta dùng glBindTextures để chỉ định texture nào được tạo và chọn texture chỉ đinh.
glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Texture
Để xạ ảnh một texture vào một hình vuông, bạn phải chắc rằng góc phải-trên của texture được xạ vào góc phải-trên của hình vuông, trái-trên vào trái-trên, phải-dưới vào phải-dưới và trái-dưới vào trái-dưới. Nếu các góc của texture mà không khớp với các góc của hình vuông, ảnh có thể bị lệnh.
Tham số đầu của glTexCoord2f là tạo độ X. 0.0f là trái cùng của texture, 0.5f là giữa của texture, và 1.0f là phải cùng của texture. Giá trị thứ hai của glTexCoord2f là tọa độ Y. 0.0f là dưới cùng của texture, 0.5f là giữa của texture, và 1.0f là trên cùng của texture.
OK giờ ta có tọa độ góc trái-trên của texture là 0.0f trên trục X và 1.0f trên trục Y, và đỉnh trái-trên của hình vuông là -1.0f trên trục X, và 1.0f trên trục Y. Giờ tất cả những gì ta phải làm là ba tọa độ texture với 3 tọa độ còn lại của hình vuông.
Thử nghịch một tí với các giá trị x và y của glTexCoord2f. Đổi 1.0f thành 0.5f thì ta thấy nó sẽ chỉ vẽ nửa trái của một texture từ 0.0f (trái) đến 0.5f (giữa của texture). Đổi 0.0f thành 0.5f thì ta thấy nó sẽ chỉ vẽ nửa phải của texture từ 0.5f (giữa) đến 1.0f (phải).
glBegin(GL_QUADS);
// Front Face
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
// Back Face
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
// Top Face
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
// Bottom Face
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
// Right face
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
// Left Face
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glEnd();


Giờ ta tăng giá trị của xrot, yrot và zrot. Bạn hãy thử đổi độ tăng, hoặc đổi + thành - để theo dõi khối hộp nó xoay thế nào.
xrot+=0.3f; // X Axis Rotation
yrot+=0.2f; // Y Axis Rotation
zrot+=0.4f; // Z Axis Rotation
return true; // Keep Going
}


Giờ thì chắc là bạn đã hiểu hơn về texture mapping (xạ ảnh). Giờ bạn có thể xạ ảnh lên bất kỳ mặt nào của hình vuông với tấm ảnh mà bạn thích. Khi bạn đã hiểu rõ rồi thì hãy thử xạ 6 texture khác nhau lên 6 mặt của hình khối.
Texture mapping không khó để hiểu khi mà bạn đã hiểu về tọa độ texture. Nếu thấy khó hiểu phần nào của bài thì hãy nói cho mình biết. Tôi sẽ sửa lại bài hoặc trả lời bạn qua mail. Chúc vui :)


Open GL Tiếng Việt Bài 5


OpenGL Tiếng Việt
Đọc bài gốc + download code minh họa bằng nhiều ngôn ngữ tại trang của tác giả. Đây chỉ là bản dịch do mình tự dịch.
http://nehe.gamedev.net/tutorial/3d_shapes/10035/
Bài 5 - Hình 3D


Tiếp theo bài trước, giờ ta tạo đối tượng 3D thật sự. Ta thực hiện việc này bằng cách thêm các mặt trái, sau và phải cho hình tam giác, và các mặt trái, phải, sau, trên và dưới cho hình vuông. Và ta sẽ có hình kim tự tháp và hình khối vuông.
Ta sẽ vẽ màu trộn lên kim tự tháp, tạo đối tượng có màu được làm mịn và với hình vuông ta vẽ mỗi mặt một màu.
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(-1.5f,0.0f,-6.0f); // Move Left And Into The Screen
glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Pyramid On It's Y Axis
glBegin(GL_TRIANGLES); // Start Drawing The Pyramid


Một vài người ở bài trước đã làm một cái hình 3D rồi. Và một vài người đã hỏi là “Sao cái hình của mình nó không quay quanh trục của nó mà lại quay quanh cả màn hình luôn. Để hình của bạn quay quanh một trục, nó phải được thiết kế quanh cái trục đó. Bạn hãy nhớ rằng tâm của hình vẽ phải là 0,0,0 trên trục X,Y,Z.
Đoạn code dưới đây sẽ tạo ra hình kim tự tháp quanh trục. The top of the pyramid is one high from the center, the bottom of the pyramid is one down from the center. The top point is right in the middle (zero), and the bottom points are one left from center, and one right from center.
Chú ý là tất cả tam giác được vẽ ngược chiều kim đồng hồ. Điều này rất quan trọng và sẽ được nói đến ở các bài kế, bây giờ ta chỉ cần biết rằng ta nên vẽ các hình cùng theo chiều kim đồng hồ hoặc cùng ngược chiều kim đồng hồ, trừ khi có lý do đặc biệt.
Ta bắt đầu bằng việc vẽ mặt trước. Bởi vì tất cả các mặt đều có chung điểm ở đỉnh kim tự tháp, ta sẽ vẽ đỉnh này màu đỏ. Mặt trước có màu tại đỉnh trái dưới là lục, và tại đỉnh phải dưới là lam.
glColor3f(1.0f,0.0f,0.0f); // Red
glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Front)
glColor3f(0.0f,1.0f,0.0f); // Green
glVertex3f(-1.0f,-1.0f, 1.0f); // Left Of Triangle (Front)
glColor3f(0.0f,0.0f,1.0f); // Blue
glVertex3f( 1.0f,-1.0f, 1.0f); // Right Of Triangle (Front)
Giờ ta vẽ mặt phải.
Nhớ rằng trong glBegin(GL_TRIANGLES) và glEnd() cứ 3 điểm là một hình tam giác.
glColor3f(1.0f,0.0f,0.0f); // Red
glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Right)
glColor3f(0.0f,0.0f,1.0f); // Blue
glVertex3f( 1.0f,-1.0f, 1.0f); // Left Of Triangle (Right)
glColor3f(0.0f,1.0f,0.0f); // Green
glVertex3f( 1.0f,-1.0f, -1.0f); // Right Of Triangle (Right)
Giờ vẽ mặt sau. Tiếp tục đổi màu. Điểm bên trái là màu lục, bởi vì nó cũng chính là điểm màu lục của cái mặt phải.
glColor3f(1.0f,0.0f,0.0f); // Red
glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Back)
glColor3f(0.0f,1.0f,0.0f); // Green
glVertex3f( 1.0f,-1.0f, -1.0f); // Left Of Triangle (Back)
glColor3f(0.0f,0.0f,1.0f); // Blue
glVertex3f(-1.0f,-1.0f, -1.0f); // Right Of Triangle (Back)
Cuối cùng vẽ mặt trái.
Đến đây coi như vẽ xong cái kim tự tháp. Tại ta sẽ xoay nó quanh trục Y nên chả cần mặt dưới cho nó vì ta sẽ không thấy nó. Tí nữa vẽ hình khối hộp ta xoay quanh trục X thì vẽ thêm cả mặt dưới vào.
glColor3f(1.0f,0.0f,0.0f); // Red
glVertex3f( 0.0f, 1.0f, 0.0f); // Top Of Triangle (Left)
glColor3f(0.0f,0.0f,1.0f); // Blue
glVertex3f(-1.0f,-1.0f,-1.0f); // Left Of Triangle (Left)
glColor3f(0.0f,1.0f,0.0f); // Green
glVertex3f(-1.0f,-1.0f, 1.0f); // Right Of Triangle (Left)
glEnd(); // Done Drawing The Pyramid
Giờ đến phần vẽ khối hộp. Khối hộp gồm 6 mặt hình vuông. Tất cả các mặt được vẽ ngược chiều kim đồng hồ. Nghĩa là điểm đầu ở trên-phải, thứ hai là ở trên-trái, thứ 3 là ở dưới-trái, cuối cùng là dưới-phải. Khi ta vẽ mặt sau, nhiều người nghĩ ta đang vẽ theo chiều kim đồng hồ (tại nhìn thấy giống giống), nhưng mà nếu ta chạy ra mặt sau của cái khối hộp đó mà nhìn nó vẽ thì sẽ thấy rằng nó vẽ ngược chiều kim đồng hồ.
Để ý là ở bài này ta dịch hình khối sâu hơn về phía màn hình. Theo đó thì cỡ của hình khối sẽ gần bằng cớ của hình kim tự tháp. Nếu bạn chỉ dịch nó 6 đơn vị về phía màn hình, hình khối nhìn sẽ to hơn hình kim tự tháp và một phần của nó có thể bị cắt bởi cạnh màn hình. Bạn có thể thử thay đổi các thông số để xem sự khác biệt giữa việc dịch vào gần và ra xa về phía màn hình. Lý do là do luật phối cảnh gần xa: càng xa nhìn càng bé.
glLoadIdentity();
glTranslatef(1.5f,0.0f,-7.0f); // Move Right And Into The Screen
glRotatef(rquad,1.0f,1.0f,1.0f); // Rotate The Cube On X, Y & Z
glBegin(GL_QUADS); // Start Drawing The Cube
We'll start off by drawing the top of the cube. We move up one unit from the center of the cube. Notice that the Y axis is always one. We then draw a quad on the Z plane. Meaning into the screen. We start off by drawing the top right point of the top of the cube. The top right point would be one unit right, and one unit into the screen. The second point would be one unit to the left, and unit into the screen. Now we have to draw the bottom of the quad towards the viewer. so to do this, instead of going into the screen, we move one unit towards the screen. Make sense?
glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green
glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Top)
glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Top)
glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Quad (Top)
glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Quad (Top)
Mặt dưới được vẽ tương tự như mặt trên. Nó được vẽ phía dưới so với tâm khối hộp. Và tọa độ Y luôn là 1.
Nếu bạn không quan tâm đến thứ tự đa giác được vẽ (theo chiều kim đồng hồ hoặc không), bạn chỉ cần copy đoạn code cho mặt trên rồi đổi tọa độ Y thành -1. Nhưng mà vẽ mà không quan tâm đến thứ tự các đỉnh như thế thì đến khi ta ánh xạ texture (texture mapping) có thể ta sẽ thu được kết quả không mong muốn.
glColor3f(1.0f,0.5f,0.0f); // Set The Color To Orange
glVertex3f( 1.0f,-1.0f, 1.0f); // Top Right Of The Quad (Bottom)
glVertex3f(-1.0f,-1.0f, 1.0f); // Top Left Of The Quad (Bottom)
glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Bottom)
glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Bottom)

Giờ vẽ mặt trước của khối hộp.
glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red
glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Front)
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Front)
glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Front)
glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Front)


Vẽ mặt sau của khối hộp.
glColor3f(1.0f,1.0f,0.0f); // Set The Color To Yellow
glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Back)
glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Back)
glVertex3f(-1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Back)
glVertex3f( 1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Back)


Giờ ta chỉ còn 2 hình vuông cần vẽ nữa là xong. Như thường lệ bạn sẽ thấy là có một toạ độ được giữ nguyên. Trong trường hợp này thì tọa độ X của mỗi điểm là không đổi. Đó là bởi vì ta vẽ các điểm về phía trái của tâm hình khối (ta vẽ mặt trái mà).
glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Left)
glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Left)
glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Left)
glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Left)


Giờ đến mặt cuối cùng, mặt mà tọa độ X luôn = 1, vẽ ngược chiều kim đồng hồ.
glColor3f(1.0f,0.0f,1.0f); // Set The Color To Violet
glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Right)
glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Right)
glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Right)
glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Right)
glEnd(); // Done Drawing The Quad
rtri+=0.2f; // Increase The Rotation Variable For The Triangle
rquad-=0.15f; // Decrease The Rotation Variable For The Quad
return TRUE; // Keep Going
}


Sau bài này bạn đã hiểu cách tạo ra một hình 3D trong không gian. Bạn có thể coi màn hình Open GL như một miếng giấy lớn với rất nhiều lớp giấy trong suốt ở bên trong nó. Cũng như là coi cái hình khối hộp nó được tạo nên bởi các điểm. Nếu bạn có thể tưởng tượng tốt về chiều sâu trong không gian thì bạn sẽ không gặp phải vấn đề khi thiết kế hình 3D.
Nếu vẫn thấy khó hiểu hình 3D thì cũng không nên thất vọng. Ban đầu có thể hơi khó một tí. Hình khối hộp là một ví dụ đơn giản để ta có thể học hỏi. Nếu để ý thì bạn thấy mặt sau được vẽ y như mặt trước, chỉ có điều là nó được vẽ sâu hơn về phía màn hình. Cứ đọc rồi sửa code và bạn sẽ hiểu nhanh thôi. Email cho tôi (tác giả ấy) nếu bạn gặp vấn đề gì.