Quản Lý Nguồn Sáng Điểm Trong Engine 3D
Trước đây, phần mã liên quan đến nguồn sáng điểm trong engine 3D của chúng tôi được thiết kế khá lộn xộn. Vài ngày gần đây, tôi quyết định tự tay thiết kế lại toàn bộ mô-đun này. Dù hiệu năng là yếu tố quan trọng, nhưng ưu tiên hàng đầu là tái cấu trúc để tách biệt các thành phần, giảm độ phụ thuộc giữa các module.
Vấn đề cần giải quyết
Hiện tại khả năng xử lý của GPU còn hạn chế, không thể xử lý vô hạn nguồn sáng theo thời gian thực. Khi có quá nhiều nguồn sáng trong cảnh, hệ thống cần chọn ra những nguồn sáng có khả năng ảnh hưởng nhiều nhất đến vật thể đang render, sau đó truyền thông tin này cho driver xử lý.
Thiết kế cũ trộn lẫn logic quản lý nguồn sáng vào module cảnh và module render. Dù chức năng vẫn hoạt động, nhưng việc bảo trì rất phức tạp. Cấu trúc này cũng gây khó khăn cho việc tối ưu hiệu năng, chưa kể đến việc thay đổi người bảo trì hệ thống.
Tư duy thiết kế mới
Cảnh ảo có cấu trúc dạng cây phân cấp là thiết kế tự nhiên, nhưng lại khó thao tác. Tuy nhiên, với yêu cầu hiện tại không cần xử lý hiện tượng che khuất, chúng ta chỉ cần biết vị trí tuyệt đối của các nguồn sáng đang hoạt động trong không gian.
Tôi đã thiết kế module quản lý nguồn sáng với nguyên tắc:
- Ẩn thông tin vị trí của nguồn sáng bên trong module
- Tạo một “cảnh ảo” phi phân cấp để quản lý vị trí nguồn sáng
- Khi render, chỉ cần truy vấn các nguồn sáng gần vị trí cần chiếu sáng
Khái niệm “vật thể nhận sáng”
Để mở đường cho tối ưu sau này, tôi đưa vào khái niệm “vật thể nhận sáng” (light receiver). Mỗi vật thể này sẽ tự quản lý vị trí của mình trong “cảnh ảo” của hệ thống ánh sáng. Thay vì truyền trực tiếp tọa độ, chúng ta sẽ cung cấp đối tượng vật thể nhận sáng cho module quản lý ánh sáng. Cách tiếp cận này cho phép xây dựng lớp cache để tăng tốc độ truy vấn.
Quy trình render mới
- Duyệt cây cảnh:
- Node renderable → thêm vào hàng đợi render
- Node nguồn sáng → thêm vào module quản lý ánh sáng
- Xử lý hàng đợi render:
- Sau khi sắp xếp và lọc → dựa vào vật thể nhận sáng để truy vấn nguồn sáng lân cận
- Gửi thông tin ánh sáng đến driver
Các phương pháp tối ưu tiềm năng
-
Caching giữa các frame:
- So sánh cảnh ảo giữa các frame
- Với các đối tượng không thay đổi, sử dụng kết quả tính toán từ frame trước
-
Cấu trúc dữ liệu phân vùng không gian:
- Octree (cho game 3D)
- Quadtree (cho game góc nhìn 3D cố định)
- Grid-based (phân chia không gian thành ô lưới)
-
Kỹ thuật lưới đơn giản:
- Thiết lập kích thước ô lưới bằng 80-90% bán kính ảnh hưởng của nguồn sáng
- Mỗi nguồn sáng đăng ký vào 3 ô lưới theo hình tam giác đều
- Khi truy vấn, chỉ cần kiểm tra các ô lưới lân cận vật thể nhận sáng
Triết lý phát triển
Ban đầu nên sử dụng thuật toán đơn giản O(N*N) để đảm bảo tính đúng đắn trước. Sau đó mới áp dụng các kỹ thuật tối ưu phức tạp. Điều này tuân thủ nguyên tắc KISS (Keep It Simple, Stupid) trong phát triển phần mềm.
Lợi ích thiết kế mới
- Tách biệt rõ ràng giữa các module
- Dễ dàng bảo trì và mở rộng
- Tạo nền tảng cho các kỹ thuật tối ưu sau này
- Hỗ trợ đồng thời cả nguồn sáng tĩnh và động mà không lộ chi tiết triển khai
Thiết kế này không chỉ giải quyết vấn đề hiện tại mà còn tạo ra một kiến trúc linh hoạt, có thể thích ứng với các yêu cầu phát triển trong tương lai.