COM

Component Object Model (COM) là kỹ thuật giúp DirectX được lập trình một cách độc lập và có khả năng tương thích ngược.  Chúng ta thường dùng COM như là một interface, với mục đích là có thể sử dụng như là môt class C++. Điều duy nhất bạn phải biết đó là bạn có được con trỏ đến COM thông qua một số hàm đặt biệt hoặc thông qua một số interface khác của COM. Thêm nữa là khi bạn là kết thúc chương trình hoặc không dùng đến nó nữa thì bạn cần phải gọi hàm Release() (Tất cả COM đêu được kế thừa từ IUnknow có chứa phương thức Release()) thay vì delete con trỏ đó vì COM đã quản lý bộ nhớ của nó.

Lưu ý rằng COM  được bắt đầu với chữ I. ví dụ như COM quản lý texture 2d được gọi là ID3D10Texture2D

Textures và Định dạng tài nguyên

Một 2D Texture là một ma trận dữ liệu. Một là sử dụng 2D Texture để lưu trữ dữ liệu hình ảnh. nơi mà mỗi thành phần của texture lưu trữ màu sắc của Pixel. Tuy nhiên thì nó không chỉ được dùng như thế, ví dụ, trong các kỹ thuật nâng cao gọi là Normal mapping thì mỗi thành phần texture lưu trữ một vector 3D thay vì màu sắc. Do đó Texture thường được nghĩ một cách chung chung là lưu trữ hình ảnh. Texture không chỉ là mảng dữ liệu mà nó còn có thể có mipmap, và GPU thực hiện một vài thao tác đặt biệt lên chúng như là bộ lọc hoặc khử răng cưa (multisampling). Ngoài ra thì texture không thể lưu trữ tùy ý loại dữ liệu mà nó chỉ có thể lưu một số định dạng nhất định, được mô tả bởi DXGI_FORMAT enum. Một số ví dụ:

  • DXGI_FORMAT_R32G32B32_FLOAT: mỗi thành phần có 3 số kiểu float 32bit
  • DXGI_FORMAT_R16G16B16A16_UNORM: mỗi thành phần có 4 số được lưu trong khoản từ [0, 1]
  • DXGI_FORMAT_R32G32_UINT: mỗi thành phần có 2 số 32bit kiểu unsigned integer
  • DXGI_FORMAT_R8G8B8A8_UNORM: mỗi thành phần có 4 số 8bit được lưu trong khoản từ [0, 1]

Lưu ý rằng R,G,B,A viết tắt của Red, Green, Blue, Alpha. Màu sắc trên màn hình được hình thành bởi 3 màu cơ bản là đỏ, xanh lá cây và xanh dương. Alpha được sử dụng để thiết lập tính trong suốt của nó.  Tuy nhiên thì như mình đã nói ở mục trước thì texture không cần phải lưu trữ thông tin màu sắc. Ví dụ như định dạng:

DXGI_FORMAT_R32G32B32_FLOAT có 3 số thực và vì thế có thể lưu trữ một vector 3D với số thực.

The Swap Chain and Page Flipping

Để ngăn chặn sự nhấp nháy trong hiệu ứng thì cách tốt nhất là vẽ nó ở off-screen texture được gọi là back buffer. Một khi toàn bộ scene đã được vẽ lên back buffer thì nó được đưa lên màn hình để hoàn thành một khung hình. Theo cách này thì người xem không được xem quá trình vẽ mà chỉ được xem khung hình đã được vẽ xong. Để làm được điều này thì thì chúng ta cần 2 texture buffer được lưu trữ bởi phần cứng. một được gọi là front buffer và cái thứ 2 gọi là back buffer. Font Buffer lưu trữ hình ảnh đang được hiển thị trên màn hình. trong khi khung hình tiếp theo đang được vẽ ở back buffer. Sau khi đã vẽ xong ở back buffer thì vai trò của front buffer và back buffer được tráo đổi cho nhau. Front Buffer trở thành Back Buffer và back buffer trở thành front buffer. Tráo đổi vai trò của front buffer và back buffer được gọi là presenting.  Presenting là một thao tác có hiệu quả – con trỏ đến back buffer và front buffer được tráo đổi. Dưới đây là hình minh họa quá trình:
1

Front Buffer và Back Buffer được quản lý bởi một swap chain. Trong DirectX thì một swap chain được biểu diễn bởi IDXGISwapChain. Interface này lưu trữ front và back buffer texture. Interface này có chứa hàm thay đổi kích thước của buffer ( (IDXGISwapChain::ResizeBuffers)) và biểu diễn (IDXGISwapChain::Present). Chúng ta sẽ thảo luận nó ở phần sau.

Kỹ thuật sử dụng 2 buffer (font và back) được gọi là double buffering. Chúng ta có thể dùng nhiều hơn 2 buffer ví dụ như dùng 3 buffer được gọi là triple buffering. Tuy nhiên thì 2 buffer là đủ.

Lưu ý. mặc dù back buffer là một texture( mỗi thành phần được gọi là texel) chúng ta thường gọi một thành phần là pixel, Trong trường hợp của back buffer nó lưu thông tin màu sắc. Đôi khi mỗi thành phần của một texture được gọi là pixel thậm chí nó không lưu trữ màu sắc. ví dụ pixel của một normal map.

Depth Buffering

Depth buffer là một ví dụ về texture không chưa dữ liệu hình ảnh, nhưng chưa thông tin về “chiều xâu” của pixel. Giá trị của depth nằm trong khoản từ 0.0 đến 1 .0  – 0.0 biểu thị điểm gầ n nhất của một thực thể có thể xem và 1.0 biểu thị điểm xa nhất từ người xem. Có một sự tương ứng một – một giữa thành phần của depth buffer và back buffer (Ví dụ như phần tử thứ [i][j] của back buffer tương ứng với phần tử thứ [i][j] trong depth buffer. Vì vậy nếu back buffer có độ phần giải 1024×1024 thì depth buffer cũng có độ phần giải là 1024×1024.

Hình dưới đây cho thấy một cảnh đơn giản có một số đối tượng này nằm sau đối tượng kia. Để DirectX xác định pixel của vật nào là nằm trước tất cả thì nó sử dụng kỹ thuật gọi là depth buffering hay z-buffering. Để tôi nhấn mạnh rằng với depth buffer thì thứ tự mà chúng ta vẽ các đối tượng không quan trọng.

2010092021200351
Một nhóm các đối tượng che khuất nhau một phần.

Ghi chú: Để xử lý vấn đề chiều xâu. Ta có thể vẽ các đối tượng theo thứ tự từ xa đến gần. Bằng cách này các đối tượng ở gần sẽ được vẽ trên các đối tượng ở xa.Và ta sẽ có kết quả chính xác. Đây là cách họa sẽ vẽ một khung cảnh. Tuy nhiên thì phương pháp này có vấn đề riêng của nó – sắp xếp một lượng lớn dữ liệu và các hình học giao nhau là một khó khăn. Thay vì cách đó thì phần cứng đồ họa đã cung cấp cho ta depth buffer.

Để minh họa cách depth buffer hoạt động thì chúng ta sẽ nhìm vào ví dụ dưới đây.
Hình bên dưới minh họa vùng mà người xem thấy (trái) và chiều 2D của nó (phải). Từ hình đó thì chúng ta quan sát được rằng có 3 pixel được vẽ lên pixel P trong View Window (Dĩ nhiên chúng ta biết rằng pixel gần nhất sẽ được vẽ lên pixel P và nó làm mờ hoặc che khuất những pixel sau nó nhưng máy tình thì không như vậy). Trước hết, trước khi bất kỳ quá trình vẽ nào diễn ra thì back buffer được đưa vè màu mặc định (như trắng hoặc đen) và depth buffer cũng được đưa về giá trị mặc đinh – thường là 1.0. Bấy giờ giả sử rằng thứ tự vẽ các vật sẽ là hình trụ, hình cầu và hình nón. Bảng dưới đây chỉ ra cách pixel P và giá trị depth được cập nhật khi các đối tượng được vẽ. Một quá trình tương tự cho các pixel khác.

Untitled
View Window là một ảnh 2D (back buffer) chúng ta tạo ra từ 3D. Chúng ta thấy rằng 3 pixel khác nhau có thể chiếu lên Pixel P. Trực giác nói với chúng ta rằng pixel P1 sẽ được chiếu lên P vì nó gần với người xem và che khuất 2 pixel khác. Thuật toán depth buffer sẽ xác định điều này trên máy tính. Lưu ý rằng giá trị depth được chuẩn hóa trong khoản từ [0, 1] khi được lưu trong depth buffer.

Thao tác

P

d

Mô tả

Đưa về mặc định

Màu đen

1.0

Pixel và giá trị depth tương ứng được khởi tạo.

Vẽ hình trụ

P3

d3

Vì d3d = 1.0, nên P = P3 và d = d3.

Vẽ hình cầu

P1

d1

Vì d1d = d3,  nên P = P1 và d = d1.

Vẽ hình nón

P1

d1

Vì d2 > d = d1, Nên không cập nhật pixel P và giá trị depth d

Như bạn có thể thấy là chúng ta cập nhật pixel và giá trị depth tương ứng khi chúng ta thấy  một pixel nhỏ hơn giá trị depth. Bằng cách này thì sau tiến trình đó thì pixel gần nhất sẽ được vẽ. Bạn có thể đọc lại lần nữa nếu chưa hiểu.

Tóm tắt lại là depth buffer làm việc bằng cách tính toán depth cho mỗi pixel. sau đó so sánh depth của pixel này với depth của pixel tường ứng trong back buffer. Pixel nào gần nhất hay nhỏ nhất sẽ được ghi vào back buffer.

Depth buffer là một texture vì vậy nó phải được tạo ra với định dạng dữ liệu nhất định. Cách định dạng dùng cho depth buffer được đưa ra như sau:

  • DXGI_FORMAT_D32_FLOAT_S8X24_UINT: Chỉ định một số thực 32bit cho giá trị depth với 8bit cho (số nguyên không dấu) dành cho stencil buffer được chỉ định trong khoản [0, 255] và 24bit được dùng cho buffer.

  • DXGI_FORMAT_D32_FLOAT: Chỉ định một số thực 32bit cho giá trị depth.
  • DXGI_FORMAT_D24_UNORM_S8_UINT: Chỉ định một số thực 24bit cho giá trị depth trong khoản [0, 1] với 8bit cho (số nguyên không dấu) dành cho stencil buffer được chỉ định trong khoản [0, 255]
  • DXGI_FORMAT_D16_UNORM: Chỉ định một số 16bit cho giá trị depth trong khoản [0, 1]

Một ứng dụng không bắt buột phải có là stencil buffer.Nhưng nếu có thì nó luôn được gắn liền với depth buffer.Ví dụ một đinh dạng 32bit:

DXGI_FORMAT_D32_FLOAT_S8X24_UINT dùng 24bit cho depth buffer và 8bit cho stencil buffer. Vì lý do đó mà depth buffer được gọi là depth/stencil buffer. Sử dụng stencil buffer sẽ được đề cập ở những phần sau.

Texture Resource Views

Một texture có thể được đưa đến nhiều tiến trình khác nhau của Rendering Pipeline. Ví dụ: Texture thường được dùng như Render Target và như tài nguyên shader. Một Texture được tạo cho hai mục đích đó thì sẽ được gắn flag:
D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE

sẽ chỉ ra 2 tiến trình mà texture sẽ được đưa vào. Quả thực là tài nguyên không được đưa thẳng vào tiến trình mà thay vào đó là các tài nguyên liên quan được đưa vào các tiến trình khác nhau. Với mỗi cách chúng ta muốn sử dụng texture, DirectX yêu cầu chúng ta phải tạo một resource View của texture đó tại thời điểm khởi tạo. Ví dụ của việc sử dụng texture như là một Render Target và Shadre Resource, chúng ta sẽ cần tạo 2 thứ : Một Render Target View (ID3D10RenderTargetView) và Một Shader Resource View (ID3D10ShaderResourceView). Resource View thực chất làm 2 điều: Chúng nói với DirectX3D rằng tài nguyên sẽ được dùng như thế nào (tức là tiến trình nào bạn sẽ đưa vào)

Multisampling

Bời vì các điểm ảnh trên màn hình không phải là vô cùng nhỏ, một dòng tùy ý không thể được biểu diễn hoàn hảo trên màn hình máy tính. Đoạn thẳng ở hình dưới minh họa một hiệu ứng (aliasing) “bậc thang”, có thể xảy ra khi vẽ đoạn thẳng của một ma trận các pixel.

ScreenShot_20160604203432
Ở đoạn thẳng trên chúng ta thấy đoạn thẳng như một bậc thang. Ở đoạn thẳng dưới là đoạn thẳng đã được khử bật thang, được tạo ra ở màu cuối cùng bởi việc lấy mẫu và sử dụng những pixel gần kề. Kết quả là đoạn thẳng mượt hơn và làm bớt những bậc thang.

Giảm kích thức của pixel bằng việc tăng độ phân giải của màn hình có thể làm giảm vấn đề một cách đáng kể.

Khi tăng độ phân giải màn hình là không thể hoặc không đủ, chúng ta có thể áp dụng kỹ thuật khử răng cưa. Direct3D hỗ trợ một kỹ thuật khử răng cưa được gọi là multisampling, mà hoạt động bằng cách lấy các điểm ảnh lân cận vào xem xét khi tính toán màu sắc cuối cùng của một pixel. Do đó, kỹ thuật này được gọi là multisampling bởi vì nó sử dụng nhiều mẫu pixel để tính toán màu sắc cuối cùng của một pixel.

Trong phần tiếp theo, chúng ta sẽ được yêu cầu điền vào một cấu trúc DXGI_SAMPLE_DESC. Cấu trúc này có hai thành viên và được định nghĩa như sau:

typedef struct DXGI_SAMPLE_DESC {
    UINT Count;
    UINT Quality;
} DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;

Biến Cuont cho biết số lượng mẫu mà chúng ta sẽ lấy mỗi pixel và biến Quality cho biết chất lượng mà ta muốn lấy.

Một chất lượng cao hơn là tốn kém hơn, do đó, sự cân nhắc giữa chất lượng và tốc độ phải được thực hiện. Phạm vi của các mức chất lượng phụ thuộc vào định dạng texture và số lượng mẫu lấy trên mỗi pixel. Sử dụng các phương pháp sau đây để truy vấn số cấp độ chất lượng cho một định texture nhất định và số lượng mẫu:
HRESULT ID3D10Device::CheckMultisampleQualityLevels( DXGI_FORMAT Format, UINT SampleCount, UINT *pNumQualityLevels);

Hàm này trả về 0 nếu tổ hợp định đạng và số lượng mẫu không được hỗ trợ bở phần cứng nếu không số lượng của Quality sẽ được trả về thông qua biến pNumQualityLevels. Giá trị hợp lệ cho Quality cho mỗi tổ hợp định dạng và số lượng mẫu nằm trong khoản từ 0 đến pNumQualityLevels -1

Ở trong hướng dẫn này sẽ không dùng multisampling. Để làm điều này thì mình sẽ để Cuont là 1 là Quality là 0.
Ghi chú là một cấu trúc DXGI_SAMPLE_DESC cần được điền đầy đủ cho cả Swap Chain buffer và depth buffer.