Direct3D – Fullscreen & Device Lost

Posted in Direct3D, Visual Studio 2005 on Tháng Mười 26, 2008 by Phạm Quang Hoà

Chế độ đồ hoạ toàn màn hình cho phép chương trình 3D khai thác tối đa sức mạnh đồ hoạ của máy tính. Hầu hết các chương trình game 3D hiện nay chỉ hỗ trợ chế độ toàn màn hình.

Khi chương trình khởi động chế độ toàn màn hình, màn hình sẽ tự động chuyển sang độ phân giải được chọn. Ở chế độ này, chương trình sẽ sử dụng toàn bộ không gian màn hình để hiển thị hình ảnh, tất cả các chương trình khác sẽ không hiển thị trên màn hình kể cả các chương trình đặt always on top.

Trong bài này, tôi giới thiệu một chương trình cho phép chạy ở chế độ toàn màn hình với độ phân giải 800×600. Chương trình này cải tiến từ chương trình D3DTexture giới thiệu trong bài trước.

Khởi động chế độ toàn màn hình

Để khởi động chế đồ hoạ toàn màn hình, cần phải đặt những thông số sau:

  1. Đặt thuộc tính FormBorderStyle của cửa sổ chính là None
  2. Đặt thuộc tính Windowed của đối tượng PresentParameters khi tạo Device là false.
  3. Đặt BackBufferWidth và BackBufferHeight bằng kích cỡ cửa sổ. Độ rông và độ cao của cửa sổ phải tương ứng với một chế độ đồ hoạ cần khởi động, ví dụ 800×600.
PresentParameters presentParameters = new PresentParameters();
presentParameters.Windowed = false;
presentParameters.BackBufferWidth = ClientSize.Width;
presentParameters.BackBufferHeight = ClientSize.Height;

Với những thông số như vậy, chương trình sẽ chạy ở chế độ toàn màn hình với độ phân giải tương ứng.

Vấn đề Device Lost và kỹ thuật xử lý

Khi chương trình chạy ở chế độ toàn màn hình, nếu nhấn Alt-Tab để chuyển về màn hình desktop của Windows hoặc chuyển sang chương trình khác, chương trình sẽ lỗi và thoát.

Nguyên nhân ở đây là do chương trình không có quyền vẽ lên màn hình khi chuyển sang chương trình khác. Hiện tượng này gọi là Device Lost.

Device Lost sẽ xảy ra khi chương trình 3D đang chạy ở chế độ toàn màn hình và bị mất focus, khi lock máy hoặc khi Windows chuyển sang chế độ Screen Saver. Khi xảy ra Device Lost, chương trình không thể vẽ được nữa, các thông tin RenderState của Device bị phá huỷ, các dữ liệu đã được nạp như VertexBuffer, Texture không sử dụng được nữa. Trong trường hợp này, nếu chương trình cứ tiếp tục vẽ và gọi phương thức Present của Device thì sẽ bị lỗi.

Khi người dùng nhấn Alt-Tab hoặc nhấn vào nút dưới thanh Taskbar để chuyển trở lại chương trình, khi đó Device ở trạng thái sẵn sàng reset, và có thể tiếp tục vẽ sau khi reset.

Để xử lý được vấn đề Device Lost, theo các bước sau:

  1. Kiểm tra trạng thái Device, nếu bình thường thì tiến hành vẽ, nếu không tức là đã xảy ra Device Lost, chuyển sang bước 2.
  2. Khi xảy ra Device Lost, dừng việc vẽ, giải phóng toàn bộ tài nguyên gắn với Device như Texture, VertexBuffer, Surface… bằng cách gọi phương thức OnDeviceLost trên các đối tượng này (nếu có), hoặc Dispose để huỷ hoàn toàn.
  3. Kiểm tra định kỳ trạng thái Device, nếu trạng thái Device vẫn là Lost thì chờ và tiếp tục kiểm tra sau một khoảng thời gian. Nếu trạng thái Device là sẵn sàng reset, chuyển sang bước 4.
  4. Tiến hành Reset Device và tạo lại toàn bộ dữ liệu đã giải phóng trước đó bằng cách gọi phương thức OnDeviceReset (nếu trước đó gọi OnDeviceLost) hoặc tạo lại bằng new (nếu trước đó giải phóng bằng Dispose), sau đó có thể tiến hành vẽ bình thường.

Để kiểm tra trạng thái Device xem có bình thường hay không, dùng phương thức Device.CheckCooperativeLevel(). Nếu phương thức này trả về true thì có nghĩa là bình thường, nếu trả về false là Device Lost.

Trong khi Device Lost, để kiểm tra xem Device có ở trạng thái sẵn sàng cho việc reset hay chưa, dùng phương thức Device.TestCooperativeLevel(). Nếu phương thức trả về bình thường, hoặc quăng ra ngoại lệ DeviceNotResetException có nghĩa là đã sẵn sàng cho việc reset, nếu không phương thức sẽ quăng ra ngoại lệ DeviceLostException.

Để reset Device, gọi phương thức Device.Reset().

Chi tiết về các lệnh này có thể tham khảo trong chương trình minh hoạ.

Managed Pool và Default Pool

Nếu sử dụng chương trình D3DFrame được giới thiệu trong bài trước và bổ sung các phần hỗ trợ Fullscreen và xử lý Device Lost như trên, khi chạy nếu nhấn Alt-Tab để chuyển thì khi chuyển về chương trình chạy tiếp bình thường. Tuy nhiên nếu lock máy thì chỉ lần đầu quay trở lại là thành công, từ lần thứ 2 trở đi sẽ bị lỗi.

Một số ý kiến cho rằng hiện tượng này là lỗi của bộ DirectX SDK, tuy nhiên không thấy Microsoft đề cập trong MSDN.

Khi tạo các đối tượng như VertextBuffer, Texture, Surface… có một tham số phải chỉ ra đó là Pool. Thông thường, các lệnh tạo chỉ hợp lệ khi dùng Pool.Managed hoặc Pool.Default, các Pool khác có thể đọc thêm trong MSDN.

Default Pool là vùng nhớ ngầm định được sử dụng khi tạo các đối tượng. Các đối tượng này sẽ KHÔNG thể sử dụng lại nếu Device Lost.

Managed Pool là vùng nhớ được quản lý, các đối tượng tạo trong Managed Pool sẽ CÓ thể sử dụng lại khi Device Lost.

Chưa thấy tài liệu nào đề cập đến vấn đề hiệu năng khi chọn lựa giữa hai vùng này, tuy nhiên qua thực nghiệm cho thấy tốc độ là như nhau. Do đó chúng ta nên sử dụng vùng nhớ Managed Pool trong mọi trường hợp nếu có thể.

Đối với VertexBuffer, tạo với Pool.Managed và Usage.WriteOnly. Nếu tạo với Pool.Default, chương trình sẽ bị lỗi sau 2 lần lock máy.

Đối với Texture, tạo với Pool.Managed và Usage.None.

Chi tiết về cách tạo các đối tượng này có thể xem thêm trong chương trình minh hoạ.

Khi chạy, chương trình sẽ hiển thị toàn màn hình, người dùng có thể ấn Alt-Tab, hoặc lock máy nhiều lần, chương trình sẽ tiếp tục khi focus.

image

Tải về: chương trình minh hoạ

Ghi chú: Để dịch và chạy được chương trình này, máy tính cần cài đặt các công cụ cần thiết. Để biết thêm chi tiết, xem thêm các bài trước về Direct3D.

Direct3D – Texture Mapping

Posted in Direct3D, Visual Studio 2005 on Tháng Mười 14, 2008 by Phạm Quang Hoà

Texture Mapping là kỹ thuật dùng ảnh thật để lát lên bề mặt vật thể, tạo cảm giác thật hơn. Thông thường, ảnh dùng để làm texture có kích cỡ là số mũ của 2. Direct3D có thể dùng ảnh với kích cỡ bất kỳ để làm texture nhưng hiệu quả cao nhất khi kích thước ảnh là số mũ của 2.

Khi một texture được tạo ra, dữ liệu ảnh được lưu trong bộ nhớ màn hình. Kích thước texture càng lớn thì càng tốn bộ nhớ và làm giảm hiệu năng. Do đó cần phải cân bằng giữa chất lượng hình ảnh với hiệu năng.

Texture có thể được tạo ra bằng cách tải một tệp ảnh từ ngoài, hoặc sử dụng ảnh lưu trong tài nguyên chương trình. Để tải ảnh từ ngoài, dùng các phương thức của lớp TextureLoader, để tạo texture từ một đối tượng Bitmap có sẵn, tạo mới một đối tượng texture dùng toán tử new với hàm tạo tương ứng.

Để ảnh có thể lát chính xác lên bề mặt của vật thể, thì mỗi đỉnh ngoài 3 toạ độ không gian cần có thể 2 toạ độ dùng trong texture mapping, thông thường được ký hiệu là u, v. Theo đó, đỉnh có (u, v) = (0, 0) sẽ tương ứng với góc trên trái của ảnh, đỉnh có (u, v)=(1, 1) tương ứng với góc dưới phải của ảnh. Như vậy giả sử một hình chữ nhật với đỉnh trên trái có (u, v) = (0, 0) và dưới phải có (u, v) = (2, 3) thì ảnh sẽ được lát 6 lần lên bề mặt, gấp hai theo chiều dọc và gấp ba theo chiều ngang.

Bài này giới thiệu một chương trình sử dụng kỹ thuật texture mapping lát một ảnh lên các mặt của một khối lập phương. Có thể áp dụng kỹ thuật texture mapping để tạo ra hình nền bằng cách tạo một hình chữ nhật bằng kích cỡ màn hình và lát lên đó hình nền. Tuy nhiên khi đó kích thước texture có thể rất lớn, do đó với hình nền nên dùng phương pháp vẽ thẳng ảnh vào vùng nhớ.

image

Tải về: chương trình minh hoạ

Ghi chú: Để dịch và chạy được chương trình này, máy tính cần cài đặt các công cụ cần thiết. Để biết thêm chi tiết, xem thêm các bài trước về Direct3D.

Direct3D – Tạo hình cơ bản

Posted in Direct3D, Visual Studio 2005 on Tháng Mười 3, 2008 by Phạm Quang Hoà

Bài này giới thiệu một chương trình đơn giản minh hoạ cách tạo các hình đơn giản. Hình đơn giản nhất là hình tam giác được xác định bằng toạ độ của 3 điểm trong không gian. Hình tam giác là hình cơ bản nhất trong đồ hoạ 3D, vì một hình khối phức tạp có thể đưa về thành tập các hình tam giác tạo thành bề mặt hình khối.

Thông thường để vẽ một tập các hình tam giác kề nhau, người ta không cần phải cung cấp 3 đỉnh cho mỗi hình tam giác, mà có thể sử dụng lại 2 đỉnh của tam giác trong việc vẽ một tam giác liền kề. Do đó, để vẽ một hình chữ nhật thì chỉ cần cung cấp 4 đỉnh.

Để tạo ra các đỉnh, cần phải định nghĩa một cấu trúc lưu thông tin về một đỉnh có dạng như sau:

 image

Cấu trúc này gồm các trường X, Y, Z là toạ độ điểm trong không gian; U, V là toạ độ dùng trong khi lát ảnh (Texture Mapping); Diffuse là màu của đỉnh.

Để tạo ra một hình chữ nhật, khai báo một mảng gồm 4 đỉnh:

 image

Direct3D cho phép vẽ trực tiếp từ mảng các đỉnh này, tuy nhiên như thế không hiệu quả bằng việc chuyển sang một dạng bộ đệm chuẩn của Direct3D. Để tạo bộ đệm, khai báo một biến kiểu VertexBuffer và nạp dữ liệu vào bộ đệm này:

image

Hàm BuildPrimitive() được gọi khi khởi tạo cửa sổ.

Để hiển thị được hình này, sửa lại hàm GenerateFrame() trong ví dụ ở bài trước và cung cấp các đoạn mã vẽ.

 image

Phép chiếu được sử dụng ở đây là phép chiếu phối cảnh, mắt đặt ở điểm (0, 0, -10), nhìn vào điểm (0, 0, 0) và hướng đầu dọc theo trục y.

Hình vuông được phóng to lần lượt theo các chiều là (4, 4, 1), được dịch một khoảng theo mỗi chiều là (-2, -2, 0) để đưa tâm hình vuông về gốc toạ độ, được xoay với góc rotation dọc theo trục Y.

Khi chạy chương trình sẽ thu được một hình vuông xoay quanh trục thẳng đứng.

image

Tải về: chương trình minh hoạ

Ghi chú: Để dịch và chạy được chương trình này, máy tính cần cài đặt các công cụ cần thiết. Để biết thêm chi tiết, xem thêm các bài trước về Direct3D.