Việc chiếu các hình ảnh 3D vào 2D phụ thuộc vào vị trí đặt của camera (tripod) và hướng của camera. Phần thể tích được nhìn thấy giới hạn bởi một hình chóp cụt (frushtum) như hình vẽ. Chỉ những đối tượng (phần đối tượng) nằm trong không gian này mới hiển thị lên màn hình.
III.3. Chọn đối tượng 3D bằng mouse.
Khi người dùng nhấn mouse trên hình ảnh của đối tượng 3D tại một tọa độ màn hình gồm hai thành phần (x,y), ta phải xác định xem tọa độ này thuộc đối tượng nào. Thư viện OpenGL cung cấp cho chúng ta ba giải pháp để thực hiện việc này.
a. Dùng cơ chế chọn lựa của OpenGL.
OpenGL cung cấp một cơ chế rất mạnh để chọn các đối tượng không gian từ một vùng tọa độ trên viewport.
OpenGL có ba chế độ dựng ảnh RENDER, SELECT và FEEDBACK. Chế độ
RENDER là chế độ chính dùng để xây dựng các hình ảnh 3D của đối tượng từ các dữ liệu về đỉnh, ánh sáng, màu…Chế độ FeedBack tương tự chế độ Render tuy nhiên đích
đến của việc dựng ảnh khơng là buffer của màn hình mà là buffer của người dùng. Còn
trong chế độ Select, OpenGL chỉ kiểm tra xem đối tượng 3D có nằm trong một vùng viewport do người dùng định nghĩa.
Khi tiến hành render các đối tượng, mỗi đối tượng sẽ được gán một tên là một số
nguyên. Các tên này được OpenGL lưu trữ thông qua một stack – namestack. Mỗi khi OpenGL render các đối tượng này có một pixel nằm trong vùng chọn thì OpenGL sẽ chuyển tồn bộ nội dung của name stack sang một bộ đệm do người dùng định nghĩa. Từ nội dung của bộ đệm này ta có thể dễ dàng phát hiện ra đối tượng nào đã được
chọn. Sau đây là khung mã của việc chọn đối tượng qua cơ chế Select.
glSelectBuffer(size, buffer); // buffer chứa name của các đối tượng. glRenderMode(GL_SELECT); // chuyển sang chế độ select.
glMatrixMode(GL_PROJECTION); // thiết lập ma trận chiếu. glLoadIdentity();
glPickMatrix(x,w,w,h,viewport); // xác định vùng chọn. gluPerspective(…);
glMatrixMode(GL_MODEL_VIEW); glInitName();
// xác định các đối tượng bằng các vertex của chúng.
int hitcount = glRenderMode(GL_RENDER); // chuyển sang chế độ render và xác định số đối tượng nằm trong vùng chọn.
Đây là một phương pháp rất mạnh của OpenGL, tuy nhiên nó địi hỏi q trình tính
tốn khá nhiều, đặc biệt là khi số lượng đối tượng là rất lớn, khi đó có thể ảnh hưởng
đến hiệu suất của chương trình nếu việc xác định các đối tượng được chọn được tiến
hành thường xuyên, liên tục.
Luận văn tốt nghiệp GVHH: Nguyễn Hữu Hải.
OpenGL cung cấp các hàm chiếu để chuyển từ tọa độ cửa sổ (window coordinate)
sang tọa độ của đối tượng (object coordinate) hay tọa độ thế giới của GL ( world
coordinate) thông qua phép chiếu ngược.
glUnProject(xw,yw,zw,modelMatrix,projectMatrix,viewport,xo,yo,oz)
Trong đó (xw,yw,zw) là ba thành phần của tọa độ cửa số cần đổi. Thành phần xw được xác định bằng tọa độ x của mouse, thành phần yw được xác định bằng cách lấy chiều dài của viewport trừ đi thành phần tọa độ y của mouse (do hệ tọa độ màn hình có trục y hướng xuống). Còn thành phần zw được xác định lần lược bằng 0 và 1. Qua hai phép chiếu ta xác định được hai điểm nằm trên mặt cắt gần và mặt cắt xa. Từ hai điểm này ta xây đựng một đoạn thẳng rồi đem nó giao với các đối tượng Tin để tìm ra tọa dộ mà người dùng đã chọn. Từ đó, xác định ra đối tượng Tin thực sự được chọn.
Phương pháp này rất tổng quát và xác định được tọa độ thực sự mà người dùng nhấn vào một đối tượng, nó đặc biệt cần thiết trong trường hợp đối tượng Tin có nhiều hơn một điểm như tam giác. Tuy nhiên, việc tính tốn để xác định một đối tượng vẫn cịn cần phải thực hiện nhiều phép tính tốn số thực dấu chấm động để có thể xác định ra
đối tượng mà người dùng muốn chọn. Mặc dù ta có thể hạn chế bớt các phép tính này
thơng qua phép chỉ mục đa chiều đã được trình bày ở phần cơ sở dữ liệu.
c. Dùng cơ chế Double Buffer.
Doubel Buffer được dùng để tăng tốc quá trình hiển thị hình ảnh của OpenGL. Các câu lệnh của OpenGL khi được render sẽ tạo nên những pixel ảnh. Các pixel này được chứa ở một bộ đệm. Sau khi q trình render hồn tất, GL mới đổ nội dung bộ đệm ra màn hình, do đó q trình render được hiển thị nhanh hơn, giảm thiểu hiện tượng flicker khi render ảnh. Cơ chế này được gọi là Double Buffer. Như vậy tại mỗi thời
điểm ln có Front Buffer chứa nội dung hình ảnh hiển thị trên màn hình, và back
buffer chứa nội dung ảnh sẽ hiển thị tiếp theo. Do đó nội dung của back buffer và front buffer có thể hồn tồn khác nhau. Tất cả những câu lệnh của GL đều tác động lên back buffer mà hồn tồn khơng ảnh hưởng đến hình ảnh đang được hiển thị trên màn hình. Như vậy ta có thể tận dụng tính chất này để xác định một cách nhanh chóng các
đối tượng 3D.
Đầu tiên, ngay khi quá trình render ảnh kết thúc bằng lệnh glFlush() và nội dung của
back buffer được đổ vào front buffer bằng lện SwapBuffer(), ta tiến hành render lại một lần nữa toàn bộ khung ảnh. Tuy nhiên ở lần render này, thay vì vẽ đối tượng bằng màu sắc bình thường, ta dùng định danh của đối tượng để tô màu, định danh này là một số nguyên. Để tăng độ chính xác của phương pháp này, ta phải tắt tất cả các hiệu
ứng về ánh sáng, pha trộn, khử răng cưa v.v…. Khi đó, khi người dùng click mouse tại
một tọa độ nào đó, bằng thủ tục glReadPixels() để xác định pixel màu tại vị trí mouse trong back buffer. Từ giá trị màu này ta hồn tồn có thể xác định được đối tượng nào
được chọn.
Phương pháp này đơn giản và đạt hiệu quả rất cao trong trường hợp ta phải xác định
đối tượng tại một vị trí có tọa độ 2D trên màn hình. Việc render lại các đối tượng hình ảnh chỉ làm một lần rồi sau đó ta có dùng để xác định các đối tượng tại các vị trí bất kỳ
một cách rất nhanh chóng và rất đơn giản chỉ thơng qua các phép tính đơn giản nên nó hồn tồn khơng ảnh hưởng đến performance của tồn chương trình. Điều này đặc biệt quan trọng, nó này cung cấp cho ta khả năng theo dõi các đối tượng theo sự di chuyển của mouse chứ không nhất thiết phải đợi người dùng click mouse. Ở phần sau, ta sẽ thấy điều khiển World3D sử dụng phương pháp này để giả lập các event:
OnMouseMove ,OnMouseEnter và OnMouseLeave đối với các đối tượng 3D, trong
khi nếu áp dụng hai phương pháp trên thì việc xử lý các biến cố này thật sự là một gánh nặng cho CPU.
III.4. Kéo-thả (drag-and-drop) các đối tượng 3D.
Drag–and–drop là một chức năng thật sự cần thiết cho việc chỉnh sửa dữ liệu một cách trực quan dễ dàng. Cách hiện thực tính năng này hồn tồn tương tự với việc drag’n’drop một đối tượng 2D. Tuy nhiên vấn đề đặt ra là khi người dùng kéo đối
tượng một khoảng (dx,dy) trên tọa độ màn hình thì ta cần phải thực hiện phép quy đối sang khoảng (dx’,dy’,dz’) trong tọa độ 3D sao cho khi di chuyển đối tượng một
khoảng (dx’, dy’, dz’) thì hình chiếu của đối tượng sang 2D là phù hợp với độ di
chuyển (dx,dy) trên màn hình. Ta hồn tồn có thể thực hiện điều này bằng các hàm
glProject() và glUnProject().
III.5. Sơ kết.
Tổng hợp những vấn đề đã trình bày ở trên, tầng Presentation đưa ra các hàm API
giúp cho việc hiển thị Tin lên màn hình cũng như các hàm giúp cho việc chọn các đối tượng Tin từ một vùng ở tọa độ hai chiều.
typedef enum tagAttributeProcAction { apSetupContext, apCleanupContext }
TAttributeProcAction;
typedef void (__stdcall* TAttributeProc)(TAttributeProcAction
action,HTAttrValue hValue, void* param);
typedef struct utdtDrawTinParams{
int size; // kích thước của struct. int dl; // dành riêng.
bool ShowVertices; // Hiển thị đỉnh. bool ShowWireFrame; // Hiển thị wireframe. bool ShowTriangles; // HIển thị các tam giác tô.
real* ViewRegion; // Vùng hiễn thị, mảng 6 giá trị real.
float PointSize; // kích thước của một đỉnh (glPointSize) float LineWidth; // kích thước của line (glLineWidth) int PtColor,LnColor,FillColor; // màu chung cho đỉnh, line...
bool UseObjectSetting[3]; // false-sử dụng màu chung,true-sử dụng
màu kèm theo của đối tượng cho đỉnh, cạnh, tam giác.
HTAttr hAttribute; // sử dụng Attribute để vẽ.
TAttributeProc proc; // hàm callback dùng để thiết lập đồ họa cho
mỗi giá trị của attribute.
void* proc_param; // tham số truyền cho hàm callback
bool UseDefValue; // sử dụng giá trị default cho các đối tượng
không liên kết với một giá trị nào của Attribute. } TDrawTinParams, *PDrawTinParams;
typedef struct utdtSelectTinParams{
int size; int dl; bool ShowVertices; bool ShowWireFra e; m real* ViewRegion; float PointSize; float LineWitdh;
unsigned int tagVertex; // dành riêng. unsigned int tagTriangle; // dành riêng.
unsigned int* SelectBuffer; // buffer dùng với hàm tinutSelectTin()
int BufSize; // kích thước buffer.
int HitCount; // trả về số record trong buffer.
HTQuery hQrTriangle; // dành riêng. HTQuery hQrVertex; // dành riêng.
} TSelectTinParams, *PSelectTinParams;
void PL_API tinutDrawTin(HTTin tin, PDrawTinParams params, bool render); int PL_API tinutSelectTin(HTTin tin, int select_region[4],
Luận văn tốt nghiệp GVHH: Nguyễn Hữu Hải.
int PL_API tinutExtractObj(PSelectTinParams params, HTObj objs[]); void PL_API tinutInitFastSelection(HTTin hTin, PSelectTinParams params,
bool ignore_displaylist);
int PL_API tinutFastSelect(int x, int y, int tolerance = 0);
Trong đó: hàm tinutDrawTin(…) dùng để render Tin lên active render context của GL; hàm tinutSelectTin() dùng để chọn các đối tượng Tin trong khoảng tọa độ windows, hàm tinutExtractObj() dùng để trích các đối tượng được chọn từ hàm tinutSelectTin(). Hai hàm này thực hiện chọn Tin theo cơ chế GL_SELECT của OpenGL. Hàm
tinutInitFastSelection() và tinutFastSelect() dùng để chọn nhanh một đối tượng Tin bất
kỳ dùng cơ chế Double Buffer.
Ngồi các hàm thực hiện hai chức năng chính trên, tầng Presentation cịn đưa ra một số hàm tiện ích khác bao đóng một số chức năng phức tạp của GL.
IV. ĐIỀU KHIỂN WORLD3D.
IV.1. Tính năng.
Song song với các hàm giao tiếp API, tầng Presentation còn chứa đựng điều khiển
World3D như là một giải pháp minh họa cho vấn đề trình diễn lưới tam giác và thực hiện các tương tác với người dùng.
World3D được thiết kế dựa trên nền tảng của thư viện Visual Component Library của Borland trong môi trường C++Builder. Do đó, người dùng có thể sử dụng điều khiển này như các điều khiển khác dùng trong thiết kế giao diện một cách đơn giản. Sau đây, ta xem xét các tính năng chính của World3D.
IV.1.1. Tương tác người dùng.
World3D là một container chứa đựng các đối tượng khác tương tự như một đối tượng GroupBox. Tuy nhiên các đối tượng nằm trong World3D không phải là điều khiển của Windows mà là các đối tượng 3D.
World3D tạo ra một môi trường 3D mô phỏng. Trong đó, các đối tượng được xác định trong khơng gian tọa độ ba chiều của thế giới thực. Mọi tương tác của người dùng từ
bên ngồi từ khơng gian hai chiều đều được chuyển sang không gian ba chiều trước khi các tương tác này được chuyển đến các đối tượng nằm trong trong World3D xử lý. Do đó việc giải quyết vần đề kéo-thả (drag-drop) đối tượng trở nên hồn tồn đơn giản trong mơi trường của World3D.
Ngồi ra, World3D cịn cho phép người dùng thiết lập vị trí của điểm nhìn (Camera Tripod) cho phép quan sát các đối tượng này ở mọi góc độ. Kết hợp với điều khiển
WorldMap cho phép người dùng quan sát từng vùng của World3D giúp khả năng tăng
tốc trong quá trình hiển thị các đối tượng.
IV.1.2. World3D là một điều khiển hoạt động độc lập và hoàn chỉnh.
World3D được thiết kế để có khả năng làm việc một cách độc lập và hoàn chỉnh so với
ứng dụng. Nghĩa là, World3D chỉ cần một dòng lệnh khởi động và sau đó, nó hồn
tồn có khả năng liên kết cơ sở dữ liệu, mở các đối tượng Tin, thay đổi các thuộc tính của đối tượng này v.v… mà khơng cần có thêm một dịng mã nào từ bên ngồi can thiệp vào. Có thể nói World3D đóng gói gần như đầy đủ các chức năng cho phép
người dùng có thể tạo, chỉnh sửa lưới tam giác.
IV.1.3. Khả năng hoạt động trên nhiều database.
Với cấu trúc hiện tại của mình, World3D có thể làm việc với nhiều đối tượng Tin
thuộc nhiều database khác nhau trong cùng một thời điểm. Như đã trình bày ở tầng DML, mặc dù ứng dụng có khả năng mở nhiều database khác nhau như tại một thời
điểm chỉ có một database là tích cực, nghĩa là chỉ có thể truy xuất các đối tượng trên
database này. World3D cung cấp khả năng tự chuyển đối database khi cần thiết. Do
đó, người dùng hầu như khơng cần quan tâm đến đối tượng Tin đó là thuộc database
nào.
IV.1.4. World3D là một kiến trúc mở.
World3D được thiết kế như là một đối tượng có kiến trúc mở. Nghĩa là người dùng có thể định nghĩa, can thiệp cũng như có thể thay đổi hầu hết các đáp ứng, các dialog
chuNn của World3D thông qua tập các biến cố. Ở đây, ta không dự định tìm hiểu chi tiết về các biến cố này mà chỉ điểm qua các nhóm biến cố chính. Chi tiết về các biến cố sẽ được trình bày rõ hơn ở phần phụ lục.
Các biến cố của World3D có thể phân thành các nhóm sau:
a. Nhóm các biến cố khi thay đổi trạng thái của World3D.
Các biến cố này được kích hoạt khi trạng thái của World3D thay đổi, bao gồm các biến cố sau:
• OnCameraChanged: kích hoạt khi vị trí của camera bị thay đổi.
• OnModeChanging, OnModeChanged: kích hoạt khi World3D đang/đã thay đổi
chế độ hoạt động.
• OnMessage: kích hoạt khi World3D hay các đối tượng trong World3D cần gởi
một thơng điệp ra ngồi, biến cố này thường dùng để báo lỗi hay thực hiện debug.
b. Nhóm biến cố hiển thị đối tượng.
Nhóm biến cố này cho phép người dùng can thiệp vào quá trình vẽ các đối tượng 3D, bao gồm các biến cố sau:
• OnPreRenderScene, OnPostRenderScene: kích hoạt khi Worl3D bắt đầu/kết
thúc q trình render các đối tượng.
• OnRenderObject, OnRenderFastSelection: kích hoạt khi World3D cần render
một đối tượng nằm trong nó.
• OnGDIDraw: kích hoạt sau khi q trình render hồn tất, cho phép người
người thực hiện các thao tác vẽ trên device context của World3D bằng các lệnh vẽ của GDI hoặc GDI+.
c. Nhóm biến cố tương tác với đối tượng.
Nhóm biến cố nào bao gồm các biến cố liên quan đến việc xử lý các đối tượng nằm trong World3D khi có tương tác với người dùng qua mouse, keyboard:
• OnMouseEnterObj, OnMouseLeaveObj, OnMouseMoveObj: kích hoạt khi
mouse di chuyển vào/ra/bên trong đối tượng 3D.
• OnMouseDownObj, OnMouseUpObj: kích hoạt khi mouse nhấn/thả trên một
Luận văn tốt nghiệp GVHH: Nguyễn Hữu Hải.
• OnDragObject, OnDraggingObj, OnDropObj, OnDroppedObj: kích hoạt khi
một đối tượng bắt đầu/ đang kéo và sắp/đã thả xuống.
• OnKeyDownObj, OnKeyUpObj: kích hoạt khi người dùng nhấn/thả phím khi
đang chọn một đối tượng.
d. Nhóm biến cố tạo/xóa đối tượng.
Nhóm biến cố tạo/xóa đối tượng được kích hoạt khi người dùng chèn/xóa đối tượng trong World3D, bao gồm các biến cố sau:
• OnCreatingObject, OnCreateObject: kích hoạt khi người dùng tạo đối tượng,
và đối tượng đã được chèn lớp danh sách các đối tượng của World3D.
• OnDeletingObject, OnDeleteObject: kích hoạt khi người dùng bắt đầu xóa đối
tượng và khi đối tượng đã được loại khỏi World3D.
e. Nhóm biến cố liên quan Action.
Nhóm các biến cố này cung cấp khả năng quản lý các hạng mục trong trình đơn popup của World3D khi người dùng click phải chuột, bao gồm các biến cố sau:
• OnCreateStdAction, OnPerformStdAction: kích hoạt khi World3D khởi tạo các
action chuNn tùy vào ngữ cảnh và khi người dùng chọn vào các action này. • OnCreateObjectAction, OnPerformObjectAction: tương tự như hai biến bố trên
nhưng áp dụng cho các action do các đối tượng 3D tạo ra.
IV.1.5. Undo buffer.
Undo buffer cung cấp cho người dùng khả năng phục hồi lại tình trạng trước đó. Tính năng này được xây dựng chủ yếu dành cho việc thay đối tác động lên các đối tượng Tin như : thay đổi tọa độ đỉnh, xóa tam giác, đỉnh v.v…