Thuật toán bóng khối là thuật toán tạo bóng dựa trên các thông tin về hình dạng của vật thể cần tạo bóng (Geometry Based Shadow Algorithm), vì thế nó đòi hỏi phải có các thông tin về tính kết nối của các lưới đa giác của tất cả các vật thể có trong khung hình (scene) để có thể tính toán một cách hiệu quả và chính xác.
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
33
Các thông tin về vật thể có thể được lấy từ một mô hình WireFrame, trong đó nó thể hiện hình dạng của đối tượng ba chiều bằng 2 danh sách:
Danh sách các đỉnh: Lưu tọa độ các đỉnh.
Danh sách các cạnh: Lưu các cặp điểm đầu và cuối của từng cạnh. Trong đó các đỉnh và các cạnh được đánh số thứ tự cho thích hợp.
Hình 2.4. Biểu diễn của một căn nhà.
Ngoài ra còn có thể lấy từ file .obj được tạo ra khi ta sử dụng các công cụ xây dựng mô hình ba chiều như Google Sketchup, 3DSmax….
Thuật toán bóng khối còn là thuật toán trên từng pixel (Per Pixel Algorithm) Vì ta sẽ thực hiện một phép kiểm tra “trong bóng” (in shadow) cho mỗi điểm được vẽ ra màn hình. Nó bao gồm 2 phần riêng biệt. Phần đầu tiên chúng ta phải thực hiện các tính toán liên quan đến việc tạo ra cái mà gọi là bóng khối.
2.2. Danh sách cạnh viền
Mỗi vật thể đối với mỗi nguồn sáng sẽ có một bóng khối. Để đơn giản ta sẽ chỉ xét với một nguồn sáng duy nhất. Ý tưởng để tạo ra bóng khối là ta sẽ xây dựng một lưới các đa giác bao quanh vùng bóng khối mà vật thể tạo ra do được chiếu sáng. Để làm được điều đó, ta phải tìm ra danh sách các cạnh viền của vật thể, chúng là những cạnh chủ yếu để tạo ra bóng khối, khi ánh sáng chiếu đến những
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
34
cạnh đó, nó sẽ không dừng lại mà đi tiếp. Có thể hiểu đó là những cạnh tiếp xúc của vật thể với tia sáng.
Hình 2.5. Cạnh viền (Silhouette Edge) được tô đỏ.
Hình 2.6. Khi nhìn từ vị trí của nguồn sáng ta sẽ không thấy bóng và rất dễ để xác định cạnh và đỉnh viền.
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 viền 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. [4]
Hình 2.7. Cạnh viền là cạnh có một mặt kề hướng ánh sáng còn mặt còn lại thì không
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
35
Thực tế việc tìm cạnh viền đã được phát triển thành 2 thuật toán hoàn toàn riêng biệt.
Thuật toán 1: Kiểm tra tất cả các tam giác của vật thể để tìm các cạnh có tính chất của cạnh viền. Cụ thể: để tìm được các cạnh viền, trước tiên ta cần xác định mặt nào của vật thể sẽ được chiều bởi ánh sáng và mặt nào thì không. Việc này khá đơn giản khi ta đã biết tọa độ của nguồn sáng, vectơ pháp tuyến của mặt và phương trình của mặt phẳng. Ta chỉ việc thay tọa độ nguồn sáng vào phương trình mặt phẳng, rồi tính kết quả, nếu kết quả >0 thì khi đó pháp tuyến và nguồn sáng nằm cùng một phía với mặt phẳng đó, và do đó nó được chiếu sáng. Thuật toán 1 tìm cạnh viền như sau:
Bƣớc 1: Lặp cho tất cả các tam giác của vật thể.
Bƣớc 2: Nếu tam giác hướng về phía nguồn sáng ( tích vô hướng của vector hướng ánh sáng và vector pháp tuyến của tam giác đó >= 0):
Bƣớc 2-a: Chèn 3 cạnh (là 3 cặp vertices) của tam giác đó vào edge stack. Bƣớc 2-b: Kiểm tra trong stack xem 3 cạnh vừa chèn đó đã xuất hiện rồi hay chưa (tính luôn thứ tự đảo của cạnh, ví dụ AB = BA).
Bƣớc 2-c: Nếu cạnh đó đã tồn tại trước trong stack, gỡ bỏ cả hai cạnh khỏi stack.
Bƣớc 3: Cuối cùng, các cạnh còn lại trong stack là các cạnh viền. Thủ tục xác định mặt được chiếu sáng sẽ như sau:
Gọi P[i](x,y,z) = a*x + b*y + c*z + d là phương trình của mặt thứ i của vật thể. L = (Lx, Ly, Lz) là vị trí của nguồn sáng. n: Số mặt của vật thể Procedure VisiblePlaneTest( ) Begin Side: interger; For i = 0 to n do Begin
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
36
Side =a*Lx + b*Ly + c*Lz + d; if (Side>0) then P[i].visible = True else P[i].visible = False;
End End [4], [5]
Mỗi cạnh sẽ có 2 mặt chứa nó, mỗi cạnh viền sẽ phải có một đa giác kề được chiếu tới bởi ánh sáng và một thì bị che. Bởi vì nếu cả 2 đa giác đó đều được chiếu sáng hoặc là cả 2 đều bị che thì cạnh đó sẽ không phải là cạnh viền. Khi đó ta có thuật toán tìm danh sách các cạnh viền dưới dạng mã giả như sau:
Gọi P[i] là đa giác thứ i của vật thể. n: là số đa giác.
Procedure Danhsachcanhvien()
Begin
for i = 0 to n do // Kiểm tra tất cả các đa giác.
if (P[i].visible = true} // Nếu mặt chứa đa giác đó được chiếu sáng
Begin
for {tất cả cạnh của đa giác} do
if {cạnh đó đã có ở trong danh sách cạnh viền} - Loại bỏ nó ra khỏi danh sách.
else
- Thêm cạnh đó vào danh sách.
End;
End;
Ưu điểm:
Đơn giản do sử dụng CPU để thực hiện.
Bóng khối tạo ra có số mặt tối thiểu, render nhanh. Khuyết điểm:
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
37
Skinning (dùng cho diễn hoạt khung xương) phải thực hiện trước trên CPU. [4], [5]
Thuật toán 2: Tạo ra một vật thể mới (shadow volume mesh) từ vật thể chắn sáng nhưng có thêm các mặt được bổ sung ở các cạnh, rồi dùng Vertex Shader để tạo hình khối của bóng khối (shadow volume).
Cách tạo Shadow Volume Mesh
Như ta đã biết nội dung chủ yếu của thuật toán 2 là phải tính được shadow volume mesh và dùng Vertex Shader để tạo hình khối shadow volume từ mesh này. Hình vẽ sau đây minh họa cách tạo ra shadow volume mesh.
Hình 2.8. Dựng shadow volume mesh bằng các thêm vào các mặt phụ
Thuật toán tạo shadow volume mesh
Bƣớc 1: Lặp cho tất cả các mặt trong vật thể
Bƣớc 2: Tính vector pháp tuyến cho mỗi mặt.
Bƣớc 3: Lặp cho 3 cạnh của mỗi mặt.
Bƣớc 3-a: Thêm cạnh đó vào 1 list kiểm tra.
Bƣớc 3-b: Nếu cạnh đó đã xuất hiện ở trong list ( ta đã tìm thấy cạnh được dùng chung cho 2 mặt):
+ Nếu pháp tuyến của các mặt kề cạnh đó không song song với nhau, thêm 1 tứ giác (degenerate quad) vào list kết quả.
+ Ngược lại, chỉ thêm cạnh đó vào list kết quả.
Bƣớc 3-c: Gỡ bỏ cạnh đang xử lý và các cạnh tương tự ra khỏi list kiểm tra.
Bƣớc 4: Tạo mảng dữ liệu để chứa dữ liệu của shadow volume mesh, mỗi vertex của shadow volume mesh chỉ gồm vị trí và pháp tuyến mà thôi.
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
38
Bƣớc 5: Nếu còn cạnh nào trong list kiểm tra thì vật thể đang xử lý không phải là khối đặc vì trong khối đặc tất cả các cạnh đều có 2 mặt kề với nó.
Ưu điểm:
Tốc độ nhanh do thực hiện ngay trên GPU (Vertex Shader), giải phóng CPU.
Có thể thực hiện skinning trên phần cứng. Khuyết điểm:
Phức tạp do thuật toán tạo vật thể mới, việc tính toán chậm. Phải sử dụng thêm Vertex Shader.
Shadow Volume có số mặt tạo ra lớn hơn rất nhiều so với vật thể gốc, render sẽ chậm hơn.
Sử dụng thuật toán 2 cho ra tốc độ nhanh hơn hẳn thuật toán 1 dù khi render có chậm hơn do có nhiều mặt hơn. [4], [5]
2.3. Xác định tứ giác bao quanh
Khi chúng ta đã có danh sách các cạnh viền rồi, chúng ta sẽ tạo ra bóng khối bằng cách xây dựng các tứ giác từ mỗi cạnh viền đó dựa vào vị trí của nguồn sáng. 2 đỉnh đầu của tứ giác là 2 đỉnh của cạnh viền. 2 đỉnh tiếp theo sẽ nằm trên 2 đường thẳng nối giữa nguồn sáng và 2 đỉnh đầu. 2 đỉnh này theo lý thuyết sẽ được chiếu ra vô cực nhưng như thế sẽ không cần thiết vì thế ta sẽ chỉ cho chúng các giá trị tọa độ lớn là được.
1
v và v2 là 2 đỉnh của một cạnh viền bất kỳ trong danh sách. L là vị trí của nguồn sáng.
3
v và v4 sẽ là 2 điểm cần tìm tọa độ để tạo ra tứ giác.
Const He_so_chieu 100 //Hệ số chiếu này là một số lớn.
chieu so He x L x v x v3 ( 1 ) _ _ ; chieu so He y L y v y v3 ( 1 ) _ _ ; chieu so He z L z v z v3 ( 1 ) _ _ ; chieu so He x L x v x v4 ( 2 ) _ _ ;
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ 39 chieu so He y L y v y v4 ( 2 ) _ _ ; chieu so He z L z v z v4 ( 2 ) _ _ ; 2.9. .
Từ các điểm này ta sẽ vẽ được các tứ giác bao ngoài bóng khối. Vấn đề còn lại cần phải giải quyết với bóng khối là phải “đậy nắp” (Capping) 2 đầu của khối lại để nó trở thành một khối kín. Lúc đó ta có thể thực hiện các phép kiểm tra một cách chính xác nhất. Để “nấp” phía trước thì đơn giản là ta dùng luôn các mặt trước của vật thể đối với vị trí của ánh sáng. Nấp mặt sau thì ta chỉ cần chiếu từng mặt sau của vật thể với ánh sáng đó ra vô cực. Phần này không cần thiết lắm bởi vì ta đã chiếu nó ra gần như là vô cực. Nên những điểm đó không cần xét đến nhiều. [2]
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
40
Để hiểu rõ hơn ta xét: cơ sở là khối bao của đối tượng theo nguồn sáng. Vậy công việc đầu tiên là phải tính được khối bao tạo bởi đối tượng với nguồn sáng (khối bóng). Trong trường hợp đơn giản nhất xét một tam giác như trong “Hình 2.11”.
Hình 2.11. Khối bao của tam (ABC) giác với nguồn sáng điểm L.
Khối bao là khu vực được in đậm, ta thấy rằng khối bao của một tam giác là một khối được tạo bởi toàn bộ cạnh của tam giác đó và các đường nối với nguồn sáng. Như vậy, khi xét một đối tượng, trong trường hợp đơn giản nhất khối bao của đối tượng sẽ là tập hợp các khối bao của từng đa giác cấu thành đối tượng. Với cách xây dựng khối bao như vậy thì công việc xây dựng các khối bao cho đối tượng sẽ không cần giải quyết vì mỗi đa giác sẽ xác định ngay được các cạnh của nó. Tuy vậy, với việc lựa chọn khối bao như thế số lượng khối bao cấu thành khối bao của đối tượng sẽ là rất lớn, dẫn tới thời gian tính toán bóng đổ là rất lớn do đó người ta thường lựa chọn khối bao cho đối tượng là khối bao cấu thành từ đường bao của đối tượng theo nguồn sáng.
Vậy tại sao chỉ cần sử dụng đường bao của đối tượng theo hướng nguồn sáng và việc tính các đường bao này như thế nào?. Xét hình vẽ sau “Hình 2.12”:
B A C L L A E D C F
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
41
Hình 2.12. Đường bao của một đa giác trong không gian hai chiều.
Ta thấy rằng các điểm C,D,E mặc nhiên nằm trong khu vực có bóng đổ vì nó không nhìn thấy nguồn sáng, các cạnh che toàn bộ là các cạnh BA, AF, nếu không tính không gian của chính đối tượng cạnh đại điện cho đa giác ABCDEF với nguồn sáng chỉ là BF mà thôi. Xét tính chất tại các điểm B và điểm F ta thấy rằng các cạnh tạo bởi đỉnh B là AB, BC có hướng vector pháp tuyến ngược nhau so với vector
hướng ánh sáng tới tại điểm B hay cụ thể là: dot(NAB,lB) 0
và dot(NBC,lB) 0
, với
dot là hàm tích vô hướng, N là vector pháp tuyến của cạnh, l là hướng nguồn sáng tại mỗi điểm. Mở rộng hơn ta có thể tìm được đường bao của một đối tượng trong không gian ba chiều theo nguồn sáng. Thuật toán cụ thể có thể trình bày sơ lược như sau:
Thuật toán tìm đường occluder light.
Bƣớc 1: Lấy danh sách các cạnh của đối tượng.
Bƣớc 2: Xét với mỗi cạnh.
Nếu tại cạnh đó chỉ có một đa giác liên đới thì bổ xung cạnh đó vào danh sách các đường (occluder light) .
Nếu có hai hoặc nhiều hơn hai đa giác tại cạnh đang xét thì xét nếu tồn tại một cặp đa giác sao cho dot(Nci,l) dot(Ncj,l) 0, với ci là đa giác thứ i chứa cạnh
c, cj là đa giác thứ j chứa cạnh c, thì bổ xung cạnh đó vào danh sách các đường (occluder light).
Bƣớc 3: Sau khi tìm được danh sách các cạnh là đường (occluder light) tiến hành nối các cạnh này lại để được các đường bao. Việc nối các cạnh này dựa vào chỉ số của từng cạnh.
Sau các bước này chúng ta đã có danh sách các đường (occluder light, người ta còn gọi các đường này là đường Silhouette) cho đối tượng, chúng là các đường đóng. Khi thực hiện thuật toán tạo bóng đổ bằng bóng khối ta phải vẽ kín cả hai đầu như trong “Hình 2.12” ta phải vẽ cả đường BAF, tuy nhiên trong môt số trường hợp đơn giản ta chỉ cần vẽ cạnh BF để giảm số lượng đa giác cần vẽ. Vậy với mỗi tập
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
42
đường Silhouette chúng ta phải tiến hành tam giác hóa trong trường hợp muốn giảm thiểu tối đa việc vẽ khối bóng để tăng tốc độ hiển thị.
Ngoài cách tính đường Silhouette như trên, chúng ta cũng có thể xây dựng đường Silhouette dựa vào bản đồ chiều sâu “depthmap” lấy được bằng cách coi nguồn sáng như là một điểm nhìn và tiến hành render chỉ lấy bản đồ chiều sâu từ điểm nhìn đó. Công việc tính các đường Silhouette dựa theo depthmap sử dụng thuần các kỹ thuật trích biên, tách ngưỡng trong xử lý ảnh hai chiều. Nhìn chung ưu điểm của phương pháp này là dễ cho việc tính toán, tổ chức lưu trữ, nhưng nhược điểm lớn nhất của phương pháp này là tốc độ do phải xử lý trên ảnh, mặt khác kích thước ảnh cũng ảnh hưởng đến chất lượng của khối bóng do không có sự ánh xạ hoàn toàn gữa không gian ảnh và không gian đối tượng. [4], [5]
2.4. Kỹ thuật tạo bóng Z - Pass
Sau khi tính được các đường Silhouette công việc tiếp theo là phải tính bóng đổ theo khối bóng được tại ra bởi các đường Silhouette. Z-Pass là một thuật toán sử dụng để tính bóng dựa vào các đường này, thuật toán này sử dụng kết hợp hai bộ đệm (Depth buffer, và Stencil buffer). Chúng ta có thể mô tả tư tưởng chính của thuật toán như sau:
Hình 2.13. Tư tưởng chính của Z-Pass.
Tại điểm nhìn ta xây dựng các tia nhìn vào cảnh giá trị khởi tạo cho mỗi tia nhìn là 0 và khi tia nhìn đến một khối bóng thì giá trị của tia nhìn được tăng lên 1 đơn vị, tương tự như vậy khi nó đi khỏi khối bóng thì giá trị của tia nhìn được giảm đi 1 đơn vị. Tia nhìn sẽ kết thúc khi nó gặp bề mặt của một đối tượng hay cụ thể
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
43
hơn giá trị depth tại bề mặt đến - đi của khối bóng tại vị trí cắt tia nhìn lớn hơn giá trị hiện tại của depth bufer. Ta có thể hình dung trực quan hơn thông qua “Hình 2.13”. [2]
Khi ta đã tạo được lưới các đa giác bao ngoài bóng khối. Chúng ta phải thực sự vẽ bóng của vật thể ra, hay nói chính xác là vẽ ra vật thể cùng với bóng của nó. Để làm được việc đó ta phải xác định được một pixel có nằm trong vùng bóng khối đó hay không. Thuật toán xác định một pixel có nằm trong vùng bóng khối đó hay không khá đơn giản. Tư tưởng của nó là, nối điểm cần kiểm tra với điểm đặt camera (mắt nhìn). Nếu số mặt trước và số mặt sau của bóng khối mà nó cắt bằng nhau thì