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

