Lập trình DirectX 11¶
Các bước render hình ảnh¶
Biểu diễn model¶
Mỗi scene (cảnh) chứa nhiều object (hay model). Ví dụ, mỗi màn chơi trong Mario là một scene. Khi chơi mỗi màn trong Mario thì các object (hay model) sẽ là nhân vật Mario, cục gạch, con rùa, ... Các model thực chất được biểu diễn bởi nhiều hình tam giác (triangle mesh) một cách xấp xỉ. Càng nhiều tam giác, hình ảnh càng chi tiết và đẹp hơn, và tất nhiên cũng tốn bộ nhớ hơn để lưu trữ và tính toán.
Lý do của việc biểu diễn model bằng các tam giác là vì trong không gian ba chiều \(Oxyz\), các đường cong phức tạp đòi hỏi các phương trình toán học phức tạp để biểu diễn hoặc thậm chí không thể biểu diễn bằng phương trình sơ cấp. Bằng việc ghép các tam giác lại, ta có thể xấp xỉ đường cong bởi nhiều đọan thẳng liền kề.
Cấu trúc đỉnh (Vertex Format).
Với mỗi đỉnh của tam giác ta cần định nghĩa cấu trúc của nó:
Cách 1: tọa độ \((x, y, z)\) và màu tại đỉnh đó. Với cách này, khi khai báo ba đỉnh của tam giác thì màu được tô bên trong tam giác có thể là màu của đỉnh đầu tiên hoặc trung bình của ba màu tùy theo thiết lập.
Cách 2: tọa độ \((x, y, z)\), pháp tuyến tại đó và tọa độ texture. Với cách này ta xác định pháp tuyến tại vị đỉnh, thường dùng khi có ánh sáng để tính toán độ sáng tối, và tọa độ texture thuộc khoảng \([0, 1]\) để xác định texture khi load lên sẽ có 4 góc tương ứng thế nào với hình chữ nhật (ghép hai tam giác).
Tam giác (Triangle).
Để định nghĩa một hình chữ nhật với 4 đỉnh theo chiều kim đồng hồ là v0->v1->v2->v3 ta cần định nghĩa hai tam giác v0->v1->v2 và v0->v2->v3. Như vậy cần 6 vertices để biểu diễn hai tam giác.
Lưu ý: thứ tự của đỉnh rất quan trọng và được gọi là winding order.
Chỉ số (Index - Indices)
Việc khai báo 6 vertices như trước gây lãng phí bộ nhớ (v0 và v2 khai báo hai lần). Do đó mình sẽ dùng một mảng liệt kê danh sách các vertices gọi là vertex buffer, và một mảng xác định chỉ số của vertex cấu thành tam giác gọi là index buffer.
Lúc này, index buffer là mảng WORD xác định index của vertex tạo nên tam giác, ví dụ 0, 1, 2 cho tam giác đầu và 0, 2, 3 cho tam giác sau. Việc dùng WORD tiết kiệm bộ nhớ hơn.
Virtual Camera¶
Lấy một điểm nằm ngoài màn hình máy tính làm gốc. Mình chọn góc dọc và ngang về hướng màn hình, khi đó vùng nhìn thấy sẽ có dạng hình chóp vô hạn. Mình tiếp tục giới hạn lại vùng này bằng mặt phẳng xa và mặt phẳng gần. Khoảng không gian có dạng nón cụt này sẽ được thể hiện trên màn hình khi render game và được gọi là frustum. Quá trình loại bỏ mọi thứ bên ngoài vùng này khi render gọi là clipping.
Hình 29 Virtual Camera¶
Ở hình trên, mặt phẳng màu xanh lá gọi là projection window ứng với \(z = 1\). Đây là mặt phẳng sẽ được hiển thị trên cửa sổ game.
Mặt phẳng màu đỏ là mặt phẳng gần và màu xanh là mặt phẳng xa. Hai mặt phẳng này giới hạn vùng không gian sẽ được hiển thị trên cửa sổ. Quá trình clipping sẽ xác định phần nào sẽ được hiển thị dựa vào điểm ngắm và ba mặt phẳng trên, cùng với góc dọc và góc ngang giúp xác định kích thước hình chữ nhật ở ba mặt phẳng.
Rendering Pipeline (Quy trình render)¶
Local Space \(\to\) World Space \(\to\) View Space \(\to\) Backface culling \(\to\) Lighting \(\to\) Clipping \(\to\) Projection \(\to\) View Point Space \(\to\) Rasterization.
Local space, hay còn gọi là modeling space, khi một model đứng riêng lẻ, ta có thể chọn một hệ tọa độ lấy tâm là tâm của chính model đó. Trên hệ tọa độ này hiện tại chỉ có duy nhất một model.
World space. Trên thực tế có rất nhiều object trong không gian. Mỗi object là một local space với gốc ở \((x, y, z)\). Quá trình chuyển từ local space sang world space được gọi là transform, bao gồm: phép tịnh tiến (translation), phép quay (rotation) và phép co dãn (scaling).
View space. Ta cần đặt góc nhìn (camera) ở đâu? Thông thường các phép tính sẽ khá phức tạp nên ta thường đưa về gốc tọa độ và hướng (trục) theo chiều dương của trục \(z\). Đồng thời cũng tính toán lại vị trí của object theo camera mới này. Toàn bộ quá trình này gọi là view space transformation. Có hai loại view space là left-hand (LH) và right-hand (RH).
Backface culling. Một đa giác có hai mặt và ta định nghĩa một mặt là frontface còn mặt kia là backface. Direct3D sẽ bỏ đi backface khi render (không nhìn thấy) và việc này được gọi là backface culling.
Lighting. Ánh sáng là một object đặc biệt cho phép thể hiện độ sáng tối của game gần với thế giới thực.
Clipping. Ta cần cull (bỏ đi khi render trên view space) các phần nằm trong frustum. Việc này được gọi là clipping và có ba trường hợp:
Object hoàn toàn nằm trong frustum.
Object hoàn toàn nằm ngoài frustum.
Object nằm trong một phần và nằm ngoài một phần. Khi đó ta chỉ render phần nằm trong.
Projection (phép chiếu). Vùng nhìn thấy là một không gian 3D. Việc thể hiện không gian 3D lên màn hình 2D được gọi là projection (chiếu). Có nhiều cách chiếu nhưng ta quan tâm phép chiếu tâm (perspective projection). Lúc này vật càng xa thì càng nhỏ, càng gần thì càng lớn. Tỉ số aspect (aspect ratio) thường dùng là rộng/cao.
Viewport transform. Đôi khi ta không render trên cả window mà chỉ một phần của nó. Ví dụ như một cảnh trong Megaman rất lớn, khi di chuyển ta dời vị trí đi thì cũng dời không gian được render. Việc đưa từ tọa độ projection lên một vùng hình chữ nhật trên màn gọi là viewport.
Rasterization. Sau khi tất cả vertices đã được chiếu lên màn, ta có một danh sách tam giác. Rasterization sẽ tính toán màu sắc của mỗi pixel và vẽ từng tam giác cho tới khi render xong.
Vertex buffer và Index buffer¶
Trong DirectX 11 thì hai buffer này được tạo y hệt nhau bằng hàm:
HRESULT CreateBuffer(
[in] const D3D11_BUFFER_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Buffer **ppBuffer
);
Lighting¶
Có ba loại ánh sáng:
Amibient light (ánh sáng xung quanh).
Diffuse light (khuyếch tán).
Specular light (đặc thù).
Đối với specular light cần nhiều tính toán nên thường sẽ bị off.
Materials¶
Màu của object khi hiển thị trên window thực chất là màu đã được phản chiếu từ ánh sáng. Để biểu diễn ta dùng D3DMATERIAL.
Vertex Normals¶
Normal ở đây có nghĩa là pháp tuyến. Trong hình học \(Oxyz\) thì mỗi mặt phẳng xác định khi biết một điểm thuộc nó và một vector pháp tuyến của nó. Tương tự, ở đây mỗi đỉnh của mỗi object trong game sẽ có một normal.
Trong quá trình biến đổi độ dài của normal sẽ thay đổi nên cần thường xuyên kiểm tra và chuẩn hóa lại độ dài thành \(1\).
Light source¶
Nguồn sáng gồm ba loại là: point light, direction light và spot light.
Texture Coordinates¶
Tọa độ của texture được thể hiện bằng cặp \((u, v)\). Ta gọi đó là texel.
Với mỗi tam giác trên không gian 3D ta muốn định nghĩa tam giác tương ứng trên texture mà map với tam giác 3D đó.