Khóa luận Nghiên cứu và xây dựng thử nghiệm 3D Engine - Phần 5
Điều kiện tiên quyết của thuật toán đổbóng là ta phải tính được hình khối của
shadow volume mà nội dung chính là ta phải tìm được các cạnh bao. Một cạnh (bất
kỳ) được cấu tạo bởi hai điểm và có từ1 đến 2 mặt kềliền với nó, cạnh đó được gọi
là cạnh bao khi nó chỉcó 1 mặt kềhay có 2 mặt kềnhưng một mặt hướng vềphía
ánh sáng trong khi mặt còn lại thì không.
trời. 4.3.1. Cơ sở lý thuyết ¾ Cách biểu diễn bầu trời bằng hình khối và texture. Vì bầu trời là khung cảnh đóng nên các hình khối đóng được sử dụng khá nhiều để thể hiện bầu trời. Các hình khối thường được sử dụng là hình khối vuông (box), hình cầu hay bán cầu (sphere). Các texture được sử để tạo khung cảnh bầu trời thường là các texture biểu hiện 6 mặt của không gian xung quanh (dùng cho box) hay các texture liền nhau ở các cạnh (dùng cho sphere). Chương 4. Các thuật toán Vertex và Pixel Shader - 65 - Hình 4-9 Texture liền nhau ở các cạnh dùng cho sky sphere Hình 4-10 Texture 6 mặt dùng cho sky box ¾ Các đặc tính của bầu trời trong thực tế Khung cảnh bầu trời trong thực tế có các đặc tính đây mà ta cần quan tâm khi muốn thiết kế Vertex Shader. Rất xa so với tầm nhìn, khi ta nhìn tập trung vào 1 huớng thì dù ta có di chuyển đến đâu di nữa thì theo hướng nhìn (với điều kiện khoảng cách không quá lớn) thì hình ảnh mà ta nhận được từ bầu trời là không đổi. Tuy nhiên hình ảnh từ bầu trời mà ta nhận được sẽ thay đổi khi ta nhìn ở các hướng khác nhau. Từ các đặc tính trên của bầu trời ta xác định được cách thức biểu diễn bầu trời trong 3D như sau: Sử dụng một hình khối để làm vật chứa và sử dụng 1 texture có hình khung cảnh bầu trời. Chương 4. Các thuật toán Vertex và Pixel Shader - 66 - Vì ta không thể nào đi xuyên qua bầu trời, nên ta phải luôn cập nhật vị trí của hình khối bầu trời = vị trí hiện thời của camera (hay vị trí của mắt) (thỏa mãn tính chất 1). Chỉ cập nhật vị trí mà không thay đổi góc xoay của hình khối bầu trời nhằm khiến cho hình khối bầu trời không thay đổi theo hướng xoay của camera (thỏa mãn tính chất 2). Sau đây là hình vẽ minh họa cho ý tưởng. Hình 4-11 Tọa độ của skybox được cập nhật theo tọa độ camera 4.3.2. Vertex Shader cho skybox Vertex Shader cho skybox khá đơn giản như sau: struct VS_INPUT { float4 position : POSITION; float2 texcoord : TEXCOORD0; }; struct VS_OUTPUT { float4 position : POSITION; float4 color : COLOR0; float2 texcoord : TEXCOORD0; }; VS_OUTPUT main( VS_INPUT i ) Chương 4. Các thuật toán Vertex và Pixel Shader - 67 - { VS_OUTPUT o; // Skybox local transform float3 worldPos = mul( float4( i.position.xyz, 0 ), cModel[0] ); // Vertex world position = eye position worldPos += cEyePos; // Transform vertex to projection space float4 projPos = mul( float4( worldPos, 1 ), cViewProj ); // Sky is far away, so depth value = 1.0f projPos.z = 0.9999f * projPos.w; o.position = projPos; // Final color o.color = 1.0f; o.texcoord = i.texcoord; return o; } Trước tiên ta phải biến đổi sky box bằng ma trận thế giới, việc này có vẻ như là không cần thiết vì ta sẽ sử dụng vị trí của mắt (hay camera) làm vị trí cho skybox. Nhưng thực tế công việc này cho phép ta triển khai 1 số thuộc tính ban đầu cho skybox như độ cao đối với tầm mắt, độ phóng đại… Sau khi cộng thêm tọa độ của mắt vào, ta phải biến đổi vertex vào không gian chiếu bằng cách nhân với ma trận View * Projection. Vì skybox ở rất xa nên ta cho độ sâu = 1.0f (để các điểm ảnh của skybox không thể vượt qua giá trị độ sâu của các điểm ảnh khác khi kiểm tra độ sâu (depth test)) công đoạn này phải nhân với projPos.w vì để chuẩn bị cho giai đoạn chuẩn hóa hệ tọa độ thuần nhất sau khi kết thúc Vertex Shader. 4.3.3. Một số kết quả đạt được Khung cảnh bầu trời được vẽ ra đảm bảo đúng các đặc tính của bầu trời trong thực tế, cho dù ta có di chuyển camera thế nào đi nữa, ta cũng không thể “đi xuyên” qua bầu trời được. Các cảnh sau đây được chụp từ Game demo. Chương 4. Các thuật toán Vertex và Pixel Shader - 68 - Hình 4-12 Khung cảnh bầu trời chính diện Hình 4-13 Một góc nhìn khác của bầu trời Chương 4. Các thuật toán Vertex và Pixel Shader - 69 - 4.4. Chiếu sáng theo điểm ảnh (per-pixel lighting) sử dụng normal map và specular map Hiện nay hiệu ứng chiếu sáng trên từng điểm ảnh được sử dụng khá phổ biến trong các Game nhằm tăng cường chất lượng đồ họa cho Game. Thay vì chiếu sáng theo từng đỉnh vertex, per-pixel lighting cho chất lượng đồ họa cao hơn hẳn do có thể áp dụng nhiều thuật toán mới trong đồ họa 3 chiều như bump bề mặt bằng normal map, phản chiếu bề mặt bằng specular map… Ứng dụng khi tự thực hiện chiếu sáng trên điểm ảnh bằng Shaders phải giải quyết các tất cả các vấn đề về chiếu sáng như diffuse lighting, specular lighting… Diffuse lighting trong Engine chủ yếu sử dụng bump bằng normal map và specular lighting chủ yếu sử dụng specular map, 2 thuật toán chiếu sáng này sẽ được trình bày kỹ ở phần này. 4.4.1. Cơ sở lý thuyết Ở phần này sẽ đề cập chi tiết vào qui trình chiếu sáng trên điểm ảnh được Engine hỗ trợ. Qui trình chiếu sáng trên điểm ảnh chủ yếu phân ra làm 2 công đoạn riêng biệt: thực hiện tính toán màu chính (diffuse color) và tính toán màu phản chiếu (specular color). ¾ Tính toán màu diffuse (có bump bề mặt bằng normal map) Trước khi đi chi tiết vào thuật toán ta cần xem qua 1 số khái niệm mới dùng trong phần này Không gian tiếp tuyến của vật thể (tangent space). Tọa độ texture tại mỗi đỉnh (vertex) hình thành một hệ trục tọa độ 3 chiều với trục U (tiếp tuyến), trục W (pháp tuyến) và trục V (binormal = U x W). Hệ trục tọa độ này gọi là không gian tiếp tuyến hay không gian texture của vật thể tại các đỉnh (vertex). Chương 4. Các thuật toán Vertex và Pixel Shader - 70 - Hình 4-14 Không gian tiếp tuyến Normal map là gì? Normal map là một texture nhưng có đặc tính khá đặc biệt, thay vì chứa thông tin về điểm màu như texture thông thường, normal map lai chứa thông tin về không gian tiếp tuyến (tangent space) hay không gian texture (texture space) của vật thể, hay nói cách khác nếu các điểm ảnh của texture biểu diễn màu sắc của vật thể tại 1 điểm thì normal map sẽ biểu diễn không gian tiếp tuyến của vật thể tại điểm đó. Mỗi điểm ảnh của normal map có định dạng là RGBA trong đó 3 thành phần RGB có giá trị [0..1] được ánh xạ từ 3 trục U, V, W có giá trị trong khoảng [-1, 1]. Normal map có thể tạo ra bằng 2 cách, dùng height map (texture dạng graycale chứa thông tin độ sâu về bề mặt của vật thể trong đó màu sáng hơn biểu thị độ cao lớn hơn). Cách thứ 2 phức tạp hơn do phải tạo thêm 1 vật thể khác có độ chi tiết cao hơn, sau đó ta so sánh sự khác nhau giữa 2 vật thể để tạo ra normal map (quá trình này có thể được thực hiện bằng tool Melody của NVidia). Hình 4-15 Tạo normal map từ height map Chương 4. Các thuật toán Vertex và Pixel Shader - 71 - Hình 4-16 Tạo normal map từ vật thể có độ chi tiết cao hơn bằng Melody (NVidia) Bump bề mặt sử dụng normal map Bump bề mặt chủ yếu được thực hiện trên Pixel Shader cho từng điểm ảnh. Thuật toán này sử dụng giá trị normal trong normal map để xác định mức độ của ánh sáng tác động vào điểm ảnh đó bằng cách nhân tích vô hướng giá trị normal trên với vector hướng ánh sáng trong không gian tiếp tuyến. Sau đó giá trị này được nhân với màu sắc của vertex và màu lấy mẫu từ texture để tính ra màu diffuse (màu của vertex được tính trong Vertex Shader) light factor = dot product (normal, light vector ) diffuse color = light factor * vertex color * texture color; Trong đó normal. Vector pháp tuyến (normal vector) tại điểm đó, normal vector có được do lấy mẫu từ normal map. light vector. Vector hướng ánh sáng trong không gian tiếp tuyến (tangent space), vector này được tính trong Vertex Shader và được truyền vào Pixel Shader để sử dụng. vertex color. Màu của vertex sau khi thực hiện chiếu sáng trên vertex (per- vertex lighting) trong Vertex Shader. texture color. Màu của texture chính, có được do lấy mẫu texture. Chương 4. Các thuật toán Vertex và Pixel Shader - 72 - Hình 4-17 Chiếu sáng theo từng vertex trong Vertex Shader Hình 1. Dữ liệu vertex trong bộ nhớ (được vẽ dưới dạng wireframe) Hình 2. Chiếu sáng trên từng đỉnh (per-vertex lighting) bằng Vertex Shader Hình 4-18 Chiếu sáng trên từng điểm ảnh trong Pixel Shader Hình 1. Chiếu sáng trên từng pixel (sử dụng tích vô hướng giữa normal và vector hướng ánh sáng). Hình 2. Sau khi kết hợp với lấy mẫu từ texture chính. ¾ Tính toán màu specular (sử dụng specular map) Specular map là gì ? Specular map là texture dạng grayscale, specular map có tác dụng cho biết vùng nào của vật thể phản chiếu nhiều ánh sáng vùng nào phản chiếu ít ánh sáng (tương ứng với màu trong specular map từ sáng tới tối). Tính độ phản chiếu của ánh sánh Độ phản chiếu (phản xạ) của ánh sáng trên vật thể thì phụ thuộc vị trí của mắt (hay camera). Khi mắt nằm ngay trên đường phản xạ của ánh sáng thì mắt sẽ nhìn Chương 4. Các thuật toán Vertex và Pixel Shader - 73 - thấy một vùng ánh sáng chói do toàn bộ năng lượng của ánh sáng được truyền thẳng vào mắt. Hình 4-19 Sự phản xạ của tia sáng trên bề mặt Muốn tính màu specular của điểm ảnh ta phải xác định được mức độ ánh sáng phản chiếu tại điểm đó. Công thức tính vector phản chiếu (phản xạ) như sau: R = 2(L dot N)N - L Trong đó: L. Light vector R. Reflection vector N. Normal Mức độ phản chiếu của ánh sáng phụ thuộc rất nhiều vào chất liệu bề mặt của vật thể, các bề mặt nhẵn bóng có độ phản chiếu lớn trong khi các bề mặt gồ ghề lại có độ phản chiếu thấp. Để tránh công việc phải phân rã vật thể ra thành nhiều thành phần để dựng hình với các mức phản chiếu khác nhau người ta dùng specular map như một lookup table để xác định mức độ phản chiếu của ánh sánh trên từng điểm ảnh. reflection vector = 2 * dotproduct (normal, light vector) * normal - light vector specular factor = dotproduct (reflection vector, view vector) specular color = (specular factor ^ specular constant) * specular lookup Trong đó normal. Vector pháp tuyến (normal vector) tại điểm đó, normal vector có được do lấy mẫu từ normal map.
File đính kèm:
- thu_nghiem_3d_engine_5.pdf