PDA

View Full Version : DirectXGraphics8 chương 1 (post lên đây cho dễ đọc)



attilathehun
29-06-2003, 11:16
(những chỗ màu đỏ là chưa dịch được)

DIRECTX GRAPHICS FOR VISUAL BASIC
Jack Hoxley

GIỚI THIỆU
- DXGFVB là loạt bài hướng dẫn sử dụng thư viện đồ họa DirectX dành cho VB, do Jack Hoxley viết
- Địa chỉ email của tác giả: Jack.Hoxley@directX4VB.com, website: www.directX4VB.com

MỤC LỤC

Chương 1
Khởi đầu............................................ ........................
Viết chương trình đơn giản ...........................................
The Main Loop .................................................. ..........
Render() .................................................. ..................
Đồ họa – lý thuyết .................................................. ....
Đồ họa - thực hành .................................................. ...
Tổng kết .................................................. ..................

CHƯƠNG 1
Chương này sẽ tổng hợp các kiến thức cần thiết giúp bạn làm quen với DirectXGraphics.

Để học DirectXGraphics bạn cần phải đáp ứng một số điều kiện:
1. Có trình độ VB tương đối thông thạo
2. Có bản VB5 (hoặc 6). Chú ý: mã nguồn trong tài liệu này là phiên bản VB6
3. Có bản DirectX 8 (rất dễ tìm thấy trong các CD Game ^_^).

Nếu đã từng sử dụng DirectX8 với C/C++, bạn sẽ đọc tài liệu này rất dễ dàng

KHỞI ĐẦU

DirectXGraphics là một thành viên trong thư viện DirectX của Microsoft, còn được gọi là Direct3D (chúng ta gọi tắt là D3D). Bắt đầu với D3D tương đối đơn giản (về sau sẽ khó hơn). Có thể viết một chương trình D3D với 100 dòng code.

Trước tiên chúng ta cần biết cách gọi tham chiếu tới DirectX8 cho project. Bạn làm như sau:
 Bật VB, tạo project mới (dạng “Standard EXE”)
 Gọi menu Project – References - chọn “DirectX 8 for Visual Basic type library” và click OK

Bạn có thể tạo ra một template bằng cách save project trên vào thư mục VB98/Template/Projects, đặt tên là “D3D Application”. Từ nay nếu muốn tạo một ứng dụng D3D bạn chỉ cần chọn “D3D Application” trong cửa sổ New Project

attilathehun
29-06-2003, 11:18
VIẾT CHƯƠNG TRÌNH ĐƠN GIẢN

Trong phần này chúng ta sẽ cài đặt một ứng dụng DirectX đơn giản, chỉ làm một công việc là xóa màn hình
Đầu tiên là phần khai báo:
Dim Dx As DirectX8 'master object, tất cả mọi thứ đều từ đây mà ra ^_^
Dim D3D As Direct3D8 'quản lý tất cả các đối tượng 3D
Dim D3DDevice As Direct3DDevice8 'mô tả phần cứng (3D device)
Dim bRunning As Boolean 'cho biết ứng dụng tắt hay mở...
Ba biến đầu tiên (Dx, D3D, D3DDevice) đều là các object, chúng ta sẽ phải khởi tạo (init) và gỡ bỏ (terminate) nó. Biến thứ tư (bRunning) là một cờ trạng thái cho biết ứng dụng đang chạy hay không (cờ loại này thường được dùng với lệnh DoEvents)
Bây giờ hãy xem các object trên khác nhau như thế nào:

DirectX8: là object có bậc cao nhất trong cây object nhà DirectX, nó là gốc (parent) của mọi object khác

Direct3D8: giúp tạo & nhận biết tính năng của device

Direct3DDevice8: object này giúp điều khiển card 3D của bạn

Còn nhiều loại interfaces/objects khác, tuy nhiên chúng ta chưa cần biết vội. Sau này bạn sẽ lần lượt tiếp cận với chúng.
Bạn nên có một bản DirectX8 SDK (kèm trong MSDN ), nó sẽ giải thích và liệt kê tất cả thủ tục + tính năng của các interfaces/objects
Bây giờ chúng ta đi tiếp. Trước tiên bạn tạo một thủ tục gọi là “Initialise()”. Nó có chức năng đúng như tên gọi - khi thi hành xong (không có lỗi xảy ra), chúng ta có thể sử dụng tất cả các objects & bắt đầu làm việc.

Public Function Initialise() As Boolean
On Error GoTo ErrHandler:
Initialise = True '//thành công
Exit Function

ErrHandler:
Debug.Print "Error Number Returned: " & Err.Number, Err.Description
Initialise = False
End Function

Phần lớn các thủ tục đều có cấu trúc cơ bản như trên. Những người chưa thạo coding có thể thắc mắc: tại sao phải viết “Initialise()” thành một hàm (function) - nó chẳng trả về một dữ liệu nào cả. Thực ra không cần làm như vậy. Nhưng theo tôi, nó giúp bạn kiểm soát lỗi (handler error) dễ dàng hơn - điều này rất hữu ích khi viết một chương trình phức tạp.
Bạn gọi thủ tục Initialise() như sau:
If Not Initialise() Then GoTo Error_Handler
Nếu gặp lỗi (chẳng hạn lỗi khi khai báo các DirectX Object), chương trình sẽ chuyển đến Label “Error_Handler” để xử lý.
Các thủ tục trong tài liệu này sẽ viết theo cấu trúc trên.

Tiếp theo, chúng ta khai báo một số cấu trúc và khởi tạo các objects:
Dim DispMode As D3DDISPLAYMODE '//mô tả Display Mode
Dim D3DWindow As D3DPRESENT_PARAMETERS '//mô tả khung nhìn (ViewPort)
Set Dx = New DirectX8 '//khởi tạo Master Object
Set D3D = Dx.Direct3DCreate() '//Tạo Direct3D Interface bằng Master Object

Hai cấu trúc trên dùng để khởi tạo Direct3DDevice8 object, nhưng hiện tại chỉ cần biết cách khai báo là được.
Dòng tiếp theo để khởi tạo DirectX8 Object - bạn thắc mắc rằng sao không viết: “Dim Dx As New DirectX8” cho gọn?. Ở đây tôi giải thích thêm: phương pháp khai báo ...As New... như trên được gọi là Early Binding, còn như của chúng ta thì gọi là Late Binding. Chúng khác nhau ở chỗ nếu bạn sử dụng Early Binding, khi biên dịch VB sẽ thêm vào trước mỗi tham chiếu tới object một mệnh lệnh như sau: “Nếu object = nothing, khởi tạo nó” (If the object is nothing, create it) ; còn Late Binding thì không.
Giả sử bạn viết một game 3D - loại ứng dụng thường cần tối ưu tốc độ - mà những object đó được sử dụng 1000 lần/giây – thật không thể tưởng tượng nổi bạn đã bỏ phí mất bao nhiêu tốc độ.
Tiếp theo chúng ta dùng Master Object (DirectX8) để khởi tạo Direct3D interface. Lưu ý là bạn luôn luôn có thể tạo ra Direct3D interface mà không ảnh hưởng gì đến phần cứng.
Bây giờ chúng ta sẽ sử dụng hai cấu trúc vừa khai báo:

D3D.GetAdapterDisplayMode D3DADAPTER_DEFAULT, DispMode '//nhận thông số về Display Mode hiện tại
D3DWindow.Windowed = 1 '//sử dụng chế độ Windowed (Windowed Mode)
D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC '//We'll refresh when the monitor does
D3DWindow.BackBufferFormat = DispMode.Format '//Sử dụng Format của Display Mode vừa nhận được

Hiện nay thì chưa – nhưng sẽ phức tạp hơn nếu muốn sử dụng FullScreen mode (tôi sẽ trình bày sau). Nếu sử dụng Windowed Mode thì bạn nên đặt kích thước cho form khoảng 400x300 pixel.
Tiếp theo là bước tạo instance cho Direct3D device – cần lưu ý đôi chút - nếu bạn send tham số mà computer không đáp ứng được thì sẽ gây ra lỗi. Điều này thường xảy ra khi sử dụng fullscreen mode và cần phải chọn độ phân giải/chế độ màu.
Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _
frmMain.hWnd, _
D3DCREATE_SOFTWARE_VERTEXPROCESSING, _
D3DWindow)
Đoạn code trên không qúa phức tạp. Tôi sẽ giải thích thêm về các tham số:

Adapater As Long: Thay đổi 3D card tùy theo primare/secondary devices. Hầu như toàn bộ các card màn hình đều tương ứng với Default Adapter (D3DADAPTER_DEFAULT) – do đó bạn không cần qúa quan tâm đến tham số này.

DeviceType As Const_D3DDEVTYPE: cho biết sử dụng device loại nào. Có 3 loại (2 chính, 1 phụ):

D3DDEVTYPE_HAL: sử dụng phần cứng (hardware acceleration) - để card 3D làm công việc render

D3DDEVTYPE_REF: một reference device (...) dành cho developers - hỗ trợ đầy đủ tính năng (full feature support)

D3DDEVTYPE_SW: không thể sử dụng nếu chưa đăng ký một software renderer (plugin của DirectX)

hFocusWindow As Long: cho Direct3D biết cửa sổ cần làm việc, giúp Direct3D check hình ảnh trong trường hợp cửa sổ bị ẩn, bị đóng, v.v...; tham số luôn có dạng <tên form>.hwnd, lưu ý thuộc tính visible của form phải bằng true

BehaviousFlags As Long: cho biết device hoạt động như thế nào. Có 3 tùy chọn:
D3DCREATE_SOFTWARE_VERTEXPROCESSING: trên phần lớn các computer - hoặc computer mà phần cứng không có khả năng transform/lighting
D3DCREATE_HARDWARE_VERTEXPROCESSING: 3D Card sẽ thực hiện công việc transform/lighting
D3DCREATE_PUREDEVICE - mới support trong Direct3D8, chỉ dùng được với Card màn hình chip GeForce2 Ultra giá £300 trở lên (tại thời điểm tác giả đang viết)

PresentationParameters As D3DPRESENT_PARAMETERS: chỉ cần đặt vào cấu trúc viewport (D3DWindow)

Như vậy là phần khởi tạo đã hoàn tất. Nếu không gặp lỗi, chúng ta sẵn sàng làm việc.

Nói thêm một chút về lỗi phát sinh do DirectX – chỉ có thông báo “Automation Error” kèm theo một số nguyên khoảng -2000000 (chẳng hạn -2001230). Nếu muốn biết thông báo lỗi, bạn tìm đọc DirectX8 SDK (tiếng Anh). Bạn có thể mở Object Browser (nhấn F2), chọn DxVBLibA/CONST_D3DERR để xem các hằng số lỗi tương ứng (bắt đầu bằng D3DERR_).


Trước khi tiếp tục, tôi muốn nói về Enumeration (bạn có thể nghe nói đến nó - nếu từng làm việc với DirectX)
Enumeration là sự phân tích phần cứng (3D Card), giúp biết những tính năng nào được (hoặc không được) hỗ trợ. Do liên quan đến qúa trình tạo Device, tôi sẽ trình bày luôn ở đây.
Khi tùy chọn các tham số, chẳng hạn chọn device D3DDEVTYPE_HAL (hardware acceleration), chúng ta không biết computer có hỗ trợ nó hay không?. Cũng như khi sử dụng fullscreen mode, chúng ta cần phải biết độ phân giải/chế độ màu mà phần cứng hỗ trợ tốt nhất,... Có thể làm điều đó qua đoạn code sau:

Dim DevCaps As D3DCAPS8
On Local Error Resume Next
D3D.GetDeviceCaps D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, DevCaps
If Err.Number = D3DERR_INVALIDDEVICE Then
‘ không thể nhận dữ liệu tử phần cứng - card 3D không tồn tại...
D3D.GetDeviceCaps D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, DevCaps
Err.Clear '//xóa error value...
End If

'//For Hardware vertex processing:
If (DevCaps.DevCaps And D3DDEVCAPS_HWTRANSFORMANDLIGHT) Then
Debug.Print "Hardware Transform and lighting supported"
Else
Debug.Print "Hardware Transform and lighting is not supported"
End If

'//For Pure Device processing:
If (DevCaps.DevCaps And D3DDEVCAPS_PUREDEVICE) Then
Debug.Print "Pure Device is supported."
Else
Debug.Print "Pure device is not supported"
End If

'//To check the rest we use:
If D3D.CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _
DispMode.Format, DispMode.Format, 1) = D3D_OK Then
Debug.Print "The selected device format is acceptable"
Else
Debug.Print "The selected device format is not acceptable"
End If

Lúc này đoạn code trên chỉ giúp cho bạn - lập trình viên - biết phần cứng có thể (hoặc không thể) làm gì.
Tôi giải thích thêm một chút về đoạn code.
Dòng thứ hai giúp bạn xử lý lỗi - nếu computer không có card3D – chương trình sẽ chuyển sang device D3DDEVTYPE_REF (hỗ trợ hầu hết computer).
Cấu trúc D3DCAPS8 mô tả tất cả các thông tin và tính năng của phần cứng. Bạn nên đọc DirectX SDK, nó liệt kê tên và ý nghĩa của tất cả các flags có dạng D3DDEVCAPS_ . Chẳng hạn như: CANRENDERAFTERFLIP, HWRASTERIZATION, TEXTUREVIDEOMEMORY,v.v...
Ở ví dụ trên, đầu tiên chúng ta check tính năng transform/lighting của card đồ họa – tương ứng với flag D3DCREATE_HARDWARE_VERTEXPROCESSING. Sau đó đến pure device – tương ứng với flag D3DCREATE_PUREDEVICE. Nếu một trong hai flag được chấp nhận – ta sử dụng nó khi gọi thủ tục CreateDevice. Nếu ngược lại – D3DCREATE_SOFTWARE_VERTEXPROCESSING sẽ được gọi.
Cuối cùng, khi tạo được device rồi – chúng ta check xem nó có chạy được ở chế độ màn hình vừa thiết lập không (hàm CheckDeviceType).

attilathehun
29-06-2003, 11:21
THE MAIN LOOP

Phần tiếp theo này rất rõ ràng & đơn giản, tuy nhiên lại quan trọng cho bất cứ ứng dụng Direct3D nào.
Nếu có đôi chút kiến thức về game (commercial game – game thương mại), bạn sẽ biết thuật ngữ “frame rates” - có nghĩa là tốc độ cập nhật hình ảnh (số lần/giây); tốc độ cao thì game hay, tốc độ thấp thì game dở.
Bây giờ chúng ta cài đặt chương trình trên một vòng lặp. Các sự kiện (event) vẫn được xử lý bình thường. Hình ảnh chỉ được update khi có sự thay đổi. Nhớ đừng bao giờ sử dụng timer control (hoặc tương tự) – chẳng những chúng không chính xác mà còn làm chậm chương trình của bạn. Giả sử bạn đặt interval của timer là 1ms thì trên thực tế nó lên tới 50-100ms (frame rate cao nhất khoảng 10-20 frames/giây - tức fps).

Vì thế chúng ta phải sử dụng vòng lặp - một nerver ending loop. Vòng lặp là cơ sở quyết định frame rate của chương trình, do đó phải được thực thi càng nhanh càng tốt. Vòng lặp sử dụng một flag để biết chương trình đang chạy hay không, khi flag nhận giá trị false vòng lặp sẽ kết thúc & thoát chương trình.

Đây là đoạn code thể hiện Main Loop:

Private Sub Form_Load()
Me.Show '//Chắc chắn form đang visible
bRunning = Initialise()
Debug.Print "Device Creation Return Code : ", bRunning 'Giúp bạn thấy điều gì đang xảy ra...
Do While bRunning
Render '//Cập nhật khung hình (update frame)...
DoEvents '//Nhường cho ứng dụng xử lý events; nếu không có lệnh nảy sẽ biến thành một vòng lặp kín...
Loop '//Tiếp tục...
'//Vòng lặp đã kết thúc
' Bây giờ chúng ta gỡ bỏ object. Có vẻ không cần thiết -
' nhưng giúp bạn luyện tập kỹ năng coding
On Error Resume Next 'Nếu object chưa được khởi tạo
' (thủ tục initialise thất bại) chúng sẽ gây ra lỗi khi bị gỡ bỏ...
Set D3DDevice = Nothing
Set D3D = Nothing
Set Dx = Nothing
Debug.Print "All Objects Destroyed"
'//Final termination:
Unload Me
End
End Sub

Thực ra không tối ưu khi đặt đoạn code trên vào sự kiện Form_Load. Khi thiết kế một dự án lớn, tôi luôn đặt vòng lặp vào thủ tục Sub Main() - trong một module riêng biệt. Các đoạn code phụ khác đều viết dưới dạng class & chia thành nhiều loại: graphics, utilities, maths, audio, physics, AI, File,v.v...

Lại giải thích về đoạn code. Dòng đầu tiên (Me.Show) làm cho form visible (bởi vì - thông thường khi kết thúc sự kiện Form_Load form mới được show). Như đã nói, sẽ bị trục trặc nếu form ẩn (visible=false) hoặc chưa được load.
Dòng tiếp theo để khởi tạo (initialise) Direct3D, ở đây – thay cho khuôn mẫu mà tôi đã trình bày - ta gán giá trị trả về cho một flag; cách này tiện lợi ở chỗ nếu không initialise được (trả về false), vòng lặp tự động gỡ bỏ.
Phần quan trọng nhất là Main Loop, một cấu trúc Do While ... Hiện tại (và sau này) nó cũng chỉ gồm hai lệnh. (lưu ý, render() ở đây là thủ tục xử lý - được trình bày ở phần sau). Một điểm quan trọng nữa đó là lệnh DoEvents - được gọi ở cuối vòng lặp. Không có nó chương trình sẽ sa vào vòng lặp kín & treo máy. Lệnh DoEvents dành thời gian cho hệ thống xử lý công việc (keyboard, mouse input, event) - kể cả những ứng dụng khác.
Cuối cùng chúng ta đi gỡ bỏ object – việc này không hoàn toàn cần thiết. Ứng dụng sẽ tự gỡ bỏ chúng. Nhưng theo tôi tự làm lấy vẫn tốt hơn.

attilathehun
29-06-2003, 11:22
RENDER()

Ở phần trên, bạn đã gặp thủ tục Render() trong vòng lặp chính (Main Loop). Phần này trình bày nội dung của nó.
Render() là thủ tục có nhiệm vụ xử lý và hiển thị hình ảnh.

Vì là chương trình đầu tiên, ví dụ dưới đây có thể chưa gây thú vị cho bạn:

Public Sub Render()
'//1. Xóa màn hình
' Chúng ta làm điều này trước khi render...
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, &HCCCCFF, 1#, 0
'số hexa ở giữa biểu thị màu - giống trong HTML
'//2. Tiếp theo chúng ta render (ví dụ này chỉ đưa ra một khuôn mẫu)
D3DDevice.BeginScene
'Các thủ tục render đặt giữa hai dòng này
D3DDevice.EndScene
'//3. Update frame
' Tương tự như lệnh Primary.Flip trong DirectX 7
' Các tham số dưới đây làm việc với hầu hết các trường hợp...
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub

Không qúa phức tạp, thủ tục Render luôn theo trình tự sau – Xóa, Vẽ & Hiển thị lên màn hình. Thủ tục Clear xóa tất cả những gì trên frame buffer (có thể dịch là Vùng đệm khung - một phần bộ nhớ màn hình lưu giữ nội dung của ảnh). Sau đó chúng ta vẽ (hình tam giác, hình hộp hay những gì bạn tưởng tượng ra ^_^). Cuối cùng là thủ tục update (cập nhật hình ảnh) – giúp hiển thị những gì vừa vẽ lên màn hình.

Tóm lại trên đây là khuôn mẫu cho thủ tục vẽ (Render) - áp dụng cho các chương trình sau này. Bạn lưu ý thủ tục Render phải đạt tốc độ càng nhanh càng tốt. Vấn đề này rất quan trọng – nó ảnh hưởng trực tiếp đến frame rate.

attilathehun
29-06-2003, 11:23
ĐỒ HỌA –LÝ THUYẾT

Trong các bài trước, bạn đã học cách khởi tạo ứng dụng Direct3D cho VB. Tương đối khó hiểu & dĩ nhiên không làm bạn thích thú. Tôi chắc rằng bạn muốn học một phần thục tế hơn - đồ họa.
Đồ họa với Direct3D hoàn toàn đơn giản - một khi bạn hiểu rõ. Để đạt đến trình độ đó, bạn cần phải chú tâm nhiều hơn. Đương nhiên bạn sẽ học nhanh hơn nếu trí nhớ tốt ^_^.

Trước tiên ta tìm hiểu các thuật ngữ và khái niệm. Ở đây chỉ nói qua, tôi sẽ giới thiệu tỉ mỉ hơn trong các bác bài sau.

1. Vertices (số nhiều của Vertex)
Vertex có nghĩa là đỉnh - đỉnh của tam giác, hình vuông,... Sử dụng Vertex để dựng hình 2D; 3D với các thuộc tính khác nhau; vertex được mô tả bằng một kiểu dữ liệu tự định nghĩa (user defined type) của VB - gồm các thuộc tính như vị trí (position), màu, hệ tọa độ (mô tả).

2. Polygon
(đa giác) Polygons are what you’ll have heard about by “normal” people most of the
time – so and so 3D card pumps out 101 million polygons a second (or whatever), in
fact, Direct3D renders all of it’s primitives using triangles. But then again, a triangle
is the simplest possible polygon. Lists of triangles are stored as arrays of the vertex
type that you are using.

3. Face
Face là mặt. Một Face được xác định bằng 3 vertex but it also has
an orientation – you can tell which way it is facing . Các thuộc tính của vertex cũng ảnh hưởng đến face – ví dụ như màu (colour) – chúng ta sẽ tìm hiểu sau. Các mô hình 3D có thể được cấu tạo bởi hàng trăm, hàng ngàn faces.

4. Textures
Texture là những ảnh bitmap 2D được nạp từ ổ đĩa - giống như giấy dán tường – nó hiện lên các mặt polygons sau khi render.

5. Mesh
Mesh (lưới) là một dạng mô hình – nó thường được miêu tả bởi một đối tượng (object) trong chương trình. Mesh có thể chứa tới hàng trăm, hàng ngàn faces, ngoài ra còn gồm các mô tả về vật liệu (materials) + texture.

Sau khi đã hiểu qua các khái niệm, chúng ta tiếp tục. Lưu ý đừng qúa bận tâm – trên đây chỉ là những lời giải thích đơn giản & chưa có ý nghĩa gì cả. Bạn sẽ hiểu rõ thêm qua thực hành cũng như qua các bài viết sau.

Đầu tiên tôi giới thiệu 3 loại vertex có thể sử dụng. Whilst the
structure of Direct3D allows for 100’s of different combinations there are three types
that will serve most situations – and all other situations will be adaptations or
modification of these three.

1. Untransformed and Unlit vertex
Loại vertex này là những điểm (points) trong không gian 3D với định hướng (orientation) và hệ tọa độ . Direct3D sẽ làm công việc lighting (chiếu sáng) cho bạn - chỉ cần cài đặt các light, vertex, Direct3D sẽ làm phần còn lại. Đây là vertex thường được sử dụng nhất trù khi muốn dùng lightmaps hoặc pre-calculated lighting.

2. Untransformed and Lit vertex
Loại vertex này cũng giống như loại thứ nhất – là điểm trong không gian 3D – nhưng có thêm giá trị màu (colour value). Chúng cho phép tạo các hình 3D mà không cần quan tâm đến lighting. Khi bắt đầu với đồ họa 3D, đây là loại đầu tiên chúng ta dùng – chúng rất đơn giản.

3. Transformed and Lit vertex
Đây là loại vertex dùng cho đồ họa hai chiều. Với loại vertex này, Direct3D chỉ làm công việc: đặt texture, cắt (clip) và vẽ (draw). Bạn có khả năng xác định giá trị màu (colour value) và vị trí (position) trong không gian 2D.

Thêm một điều bạn cần biết: Flexible Vertex Formats (định dạng Vertex). Như đã nói, Direct3D cho phép hàng trăm loại vertex khác nhau – thông qua Flexible Vertex Formats (FVF). Mô tả FVF là một biến kiểu Long - kết hợp giữa các flag, giúp Direct3D biết loại vertex mà bạn sử dụng. Nếu bạn sử dụng sai loại vertex, hình ảnh sẽ không được render hoặc bị render lộn xộn.

Đây là vertex formats của ba loại (chính) vừa đề cập:

Const FVF_TLVERTEX = (D3DFVF_XYZRHW Or D3DFVF_TEX1 Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR)
Const FVF_LVERTEX = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX1)
Const FVF_VERTEX = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)

Còn đây là User-Defined-Type (UDT) của chúng:

Private Type TLVERTEX
X As Single
Y As Single
Z As Single
rhw As Single
color As Long
Specular As Long
tu As Single
tv As Single
End Type

Private Type LITVERTEX
X As Single
Y As Single
Z As Single
color As Long
Specular As Long
tu As Single
tv As Single
End Type

Private Type VERTEX
X As Single
Y As Single
Z As Single
nx As Single
ny As Single
nz As Single
tu As Single
tv As Single
End Type

Ngay bây giờ , bạn chưa sử dụng tất cả chúng. Tôi đưa ra chủ yếu chỉ để tham khảo.
Trong phần kế tiếp chúng ta sẽ mở rộng ứng dụng để render các hình 2D cơ bản & hình 3D trong chế độ full screen.

attilathehun
29-06-2003, 11:25
ĐỒ HỌA - PHẦN THỰC HÀNH

Như tôi đã nói, Direct3D render các hình theo triangles (tam giác) (tôi thích sử dụng triangle hơn các đa giác nhiều cạnh). Các triangle được mô tả bởi tập hợp đỉnh (vertex) - mỗi đỉnh lại gồm những thuộc tính khác nhau.
Đầu tiên chúng ta đi tạo array chứa vertex. Công việc này cần một đoạn code rất dài! (nếu từng sử dụng đồ họa Pascal chắc bạn biết điều đó). Mỗi vertex cần một dòng code, mỗi tam giác cần ba dòng code (vì có 3 vertex) – ngay cả một khối lập phương đơn giản cũng cần cả trăm dòng... Vì thế tôi chọn đa giác đơn giản nhất làm ví dụ - tam giác.

Bước 1: Setting things up
Đầu tiên chúng ta tạo array vertex:
Dim TriVert(0 To 2) As TLVERTEX '//cần 3 vertex cho một tam giác...

Sau đó bạn thêm hai dòng sau vào thủ tục Initialise():
D3DDevice.SetVertexShader FVF_TLVERTEX
D3DDevice.SetRenderState D3DRS_LIGHTING, 0

Dòng đầu tiên cho device biết loại vertex mà chúng ta sử dụng – thông qua constant mà chúng ta đã khai báo. Dòng thứ hai cho Direct3D biết ta không muốn nó làm công việc lighting – đây là mặc định.

Bước 2: Vẽ tam giác
Công việc này rất đơn giản - chỉ là gán dữ liệu vào vertex array. Để sáng sủa, chúng ta đặt thêm một thủ tục gọi là InitialiseGeometry() – nó sẽ được gọi khi kết thúc qúa trình khởi tạo.

Private Sub InitialiseGeometry()
TriVert(0) = CreateTLVertex(0, 0, 0, 1, &HFF0000, 0, 0, 0)
TriVert(1) = CreateTLVertex(175, 0, 0, 1, &HFF00&, 0, 0, 0)
TriVert(2) = CreateTLVertex(0, 175, 0, 1, &HFF&, 0, 0, 0)
End Sub

Có ba điểm cần lưu ý ở đây – thứ nhất là thủ tục CreateTLVertex. Thủ tục này giúp ta gán dữ liệu một cách ngắn gọn hơn:

Private Function CreateTLVertex(X As Single, Y As Single, Z As Single, rhw As Single, _
Color As Long, Specular As Long, tu As Single, _
tv As Single) As TLVERTEX
CreateTLVertex.X = X
CreateTLVertex.Y = Y
CreateTLVertex.Z = Z
CreateTLVertex.rhw = rhw
CreateTLVertex.Color = Color
CreateTLVertex.Specular = Specular
CreateTLVertex.tu = tu
CreateTLVertex.tv = tv
End Function

Thứ hai là giá trị long biểu thị màu (color). Lưu ý ở đây hàm RGB() không chính xác nữa – có thể đảo ngược lại thành RGB(B, G, R) – nhưng tốt hơn nên sử dụng số Hexa. Thực ra cũng tương tự như mã màu của HTML vậy.

Thứ ba, “tinh vi” hơn, là thứ tự tạo các vertex. Trên thực tế, điều này rất quan trọng - nếu không chính xác Direct3D sẽ loại bỏ (cull / remove) và không render hình tam giác. Đây là lỗi thường gặp khi bạn chạy ứng dụng Direct3D mà không thấy hình vẽ xuất hiện. Như ở trên, các vertex được tạo theo chiều kim đồng hồ:
{ảnh không copy được}
-> /
<

Bạn có thể cho Direct3D biết loại triangle (tam giác) nào bị loại bỏ (cull): clockwise (theo chiều kim đồng hồ), counter-clockwise(ngược chiều kim đồng hồ) hoặc none (không có). Mặc định là counter-clockwise; có nghĩa là Direct3D chỉ render những tam giác theo chiều kim đồng hồ. Thủ tục SetRenderState sẽ giúp bạn làm điều đó:

D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE ‘ none
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW ‘ clockwise
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW ‘ counter-clockwise

Nếu triangle (tam giác) của ta theo chiều kim đồng hồ thì bạn set là D3DCULL_CCW (mặc định), nếu ngược chiều kim đồng hồ thì dùng D3DCULL_CW.

Cuối cùng tôi lưu ý một chút về transformed and lit Vertex (TLVertex). Như đã nói, TLVertex dùng trong không gian 2D - chỉ có trục X và trục Y, vậy tại sao ở đây lại có cả cao độ Z? Z nhận giá trị trong khoảng 0.0 – 1.0. Tương tự như Z-Order trong các control của VB, triangle nào có giá trị Z cao hơn sẽ nằm bên trên (tưởng tượng như là hình chiếu bằng – nhìn từ trên xuống). Ví dụ hai triangle có cùng toạ độ thì triangle có z=1 sẽ nằm trên (che khuất) triangle có z=0.

Bước 3: Rendering the triangle
Chúng ta quay lại với thủ tục Render() & thêm lệnh để render triangle vừa tạo. Hiện tại chúng ta chỉ nên làm đơn giản, không cần textures, không cần transformation - do đó chỉ dùng một lệnh. Về sau sẽ sử dụng nhiều thủ tục tối ưu & thủ thuật hơn.

D3DDevice.BeginScene
'Các thủ tục render ở giữa hai dòng này
D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, 1, TriVert(0), Len(TriVert(0))

D3DDevice.EndScene

Hoàn toàn đơn giản. Chúng ta sử dụng thủ tục “DrawPrimitiveUP” để render một tập hợp vertex tùy ý - ở đây là TRIANGLELIST (sẽ nói ở dưới). Tham số đầu tiên có tên là PrimitiveType, bao gồm các tùy chọn sau:

D3DPT_POINTLIST: Direct3D vẽ từng vertex và không nối các điểm lại với nhau. Dùng cho particle effects (nhưng có cách hay hơn)

D3DPT_LINELIST: Direct3D vẽ đoạn thẳng giữa từng cặp vertex (0 -1, 2 -3, 4 -5,v.v...)

D3DPT_LINESTRIP: tương tự như LINELIST, nhưng điểm đầu của một đoạn là điểm cuối của đoạn trước nó – giúp tạo các đoạn thẳng liên tiếp (0-1, 1-2, 2-3, 3-4, v.v...)

D3DPT_TRIANGLELIST: Direct3D vẽ tam giác với mỗi 3 vertex (0-1-2, 3-4-5, 6-7-8, v.v...) & được tô màu kín.

D3DPT_TRIANGLESTRIP: tương tự như TRIANGLELIST, nhưng đỉnh đầu (vertex đầu) của một tam giác là đỉnh cuối của tam giác trước nó (0-1-2, 2-3-4, 4-5-6, v.v...). Cũng được tô màu kín như TRIANGLELIST.

D3DPT_TRIANGLEFAN: vẽ các tam giác có cùng đỉnh (chung một vertex - vertex đầu tiên) – dùng cho các octagonal, hexagonal or circular type shapes (0-1-2, 0-3-4, 0-5-6, v.v...). Chúng cũng được tô màu kín.

Mỗi phương pháp có cái thuận lợi & không thuận lợi. Cùng một hình vẽ, bạn nên chọn cách nào cần ít vertex hơn – như vậy Direct3D sẽ gửi dữ liệu cho 3D Card nhanh hơn. Trên cơ sở đã chỉ dẫn, bạn nên tự mày mò tìm hiểu thêm – đó là cách tốt nhất.
Tham số thứ hai gọi là primitive count, mô tả số tam giác (hoặc số điểm – tùy theo primitive type). Trong ví dụ này chúng ta chỉ vẽ một tam giác nên đặt primitive count=1.

Tham số thứ ba chính là vertex data - phải là một phần tử trong array ; thường là phần tử đầu tiên. Lưu ý số vertex phải tương ứng với primitive count (đã nói ở trên).

Tham số cuối cùng cho biết kích thước (bytes) của cấu trúc vertex – được dùng “nội bộ” trong Direct3D. Không cần hiểu rõ Direct3D làm việc như thế nào. Nhưng cơ bản có thể giải thích: ba tham số primitive count, vertex data và size giúp Direct3D xác định được chính xác vertex array. Dùng hàm Len() trên phần tử đầu tiên của array cho tham số này.

Bây giờ bạn có thể tự thực hành. Hãy thử vẽ một hình vuông (bằng những cách khác nhau), một đa giác đều bằng TRIANGLEFAN, hay một hình vòm, hình chữ nhật bo góc (rounded rectangle) bằng TRIANGLE STRIP. Tất cả các hình cơ bản đều được tạo thành bởi các tam giác. Với một chút kiến thức toán, bạn có thể dễ dàng xây dựng thuật toán vẽ hình vòm, hình cung, v.v... từ các tam giác.

attilathehun
29-06-2003, 11:26
TỔNG KẾT

Nếu cho rằng chẳng học được gì trong chương này - bạn đã sai lầm. Đây là kiến thức cơ bản, nếu không nắm rõ về sau sẽ gặp khó khăn.
Trong chương kế tiếp chúng ta sẽ bắt đầu với đồ họa 3D & học các khái niệm mới như vertex buffers, index buffers, fullscreen mode, depth buffers, lighting,v.v...
Thắc mắc và góp ý xin gửi về Jack.Hoxley@DirectX4VB.com, hoặc qua website www.Direct4VB.com - tập hợp hơn 100 tutorials và bài viết. (tôi không dám chắc có thể trả lời mọi câu hỏi!)

KEM_WALL
29-06-2003, 18:04
bao giờ có chương 2 thế :D , rất hoan nghênh mấy cái tutor này

attilathehun
29-06-2003, 20:09
các u sang topic:
http://www.diendantinhoc.com/showthread.php?s=&threadid=20152

để download ví dụ

White_Rose
30-06-2003, 01:11
Cái này lúc down lúc không, nếu may mắn thì vẫn xem tốt :D

http://207.150.221.94/ws-ihatelov/vbasp/vbasp.asp?id=346

attilathehun
01-07-2003, 15:35
Tuyệt wá, WR ơi, cho em xin mấy bài đó nhé
Anh làm website hay dzậy mà sao không giới thiệu cho mọi người??

KEM_WALL
01-07-2003, 18:55
anh ta giới thiệu lâu lắm rùi, nhưng từ khi xuất hiện cái vụ virus, anh ta thay đổi chữ kí

White_Rose
01-07-2003, 23:14
Xin nói rõ cái địa chỉ trên bây giờ là "hàng tồn kho", không còn ai quản lý và nói chung dữ liệu cũ rồi, không được cập nhập. Cái host đó cũng hâm hâm, lúc run, lúc không :D
Dự định khoảng 1 năm nữa sẽ có phiên bản mới hoàn thiện và qui mô hơn (dự định thôi :D)
Bạn có thể tuỳ ý sử dụng thông tin cung cấp trong đó nhưng nếu có post lại hay dùng lại ở đâu thì đề nghị giữ lại tên, homepage hay email của tác giả (của tác giả chính). Nếu có nhã ý thì để tên người dịch càng hay :-)