Về Kỹ Thuật Ánh Xạ Bề Mặt
Trước hết, tôi không phải là người hoạt động chuyên sâu trong cộng đồng 3D, vì vậy tôi không nắm rõ các phương pháp kết xuất bề mặt đa dạng trong giới này. Bài viết hôm nay xuất phát từ việc tôi vừa giúp đồng nghiệp phụ trách phát triển module 3D giải quyết một vấn đề thuật toán cách đây không lâu.
Từ những hiểu biết hạn chế về engine 3D, ban đầu tôi biết đến phương pháp kết xuất mặt đất kiểu ghép mảnh 2D tương tự như công nghệ tile. Những ai từng chơi với trình chỉnh sửa bản đồ của StarCraft chắc hẳn hiểu tôi đang nói về điều gì. Đến phiên bản Warcraft III, dù engine đã chuyển sang 3D nhưng phương pháp vẫn không thay đổi lớn - dùng các texture tạo thành ô vuông để ghép lại. Tôi nhớ rằng Neverwinter Nights cũng áp dụng kỹ thuật tương tự, dù thời gian quá lâu nên không dám khẳng định chắc chắn.
Sau này khi tìm hiểu giải pháp của Big World, tôi biết đến kỹ thuật lưu hệ số trộn texture tại các đỉnh lưới độ cao mặt đất. Mỗi chunk (thường 25 ô lưới) có thể sử dụng tối đa bốn loại texture khác nhau, sau đó dựa vào hệ số trộn tại các đỉnh lưới để tạo ra hiệu ứng tổng thể. Phương pháp này tiết kiệm tài nguyên, nghe nói nhiều engine 3D hiện nay vẫn áp dụng cách này để kết xuất mặt đất.
Tuy nhiên, lưới mặt đất mặc định của Big World có kích thước mỗi ô là 4 mét, dẫn đến hiện tượng chuyển tiếp bề mặt khá thô ráp. Các họa sĩ cảnh quan của game Tianxia 2 từng phàn nàn riêng với tôi về vấn đề này, cho rằng không thể tạo ra hiệu ứng đẹp như World of Warcraft. Đến phiên bản Westward Journey, do yêu cầu khắt khe từ đội ngũ mỹ thuật, kích thước ô lưới đã được điều chỉnh xuống còn 2 mét để cải thiện chất lượng hình ảnh, đương nhiên lượng dữ liệu cũng tăng gấp 4 lần.
Làm thế nào để tiếp tục nâng cao độ chi tiết của texture bề mặt? Đơn giản tăng độ phân giải lưới không phải giải pháp tối ưu. Thực tế, độ phân giải của heightmap không cần quá cao vẫn có thể tạo ra hình ảnh đẹp, nhưng lượng dữ liệu tăng lên lại rất đáng kể.
Đội ngũ lập trình 3D của chúng tôi đã đề xuất một phương pháp mới: độ phân giải texture có thể cao hơn độ phân giải lưới. Tất nhiên, lúc này không thể áp dụng cách cũ là lưu thông tin trộn tại đỉnh lưới nữa, mà phải chuyển sang lưu trữ trong texture độc lập.
Ví dụ, với một chunk 32x32 ô, nếu dùng texture 32x32 với 4 kênh RGBA, ta có thể mô tả thông tin trộn của 5 loại texture (vì tổng hệ số trộn bằng 1, lớp thứ 5 có thể tính ngược từ 4 lớp trước). Hiệu quả tương đương phương pháp lưu tại đỉnh lưới, nhưng phải tiêu hao thêm một texture. Nếu nâng lên texture 64x64, độ chính xác trộn sẽ tăng 4 lần, thậm chí có thể dùng texture 256x256 hay 1024x1024. Tuy nhiên độ phân giải quá cao sẽ làm tăng gánh nặng xử lý, lúc đó thà lưu luôn texture trộn hoàn chỉnh còn hơn.
Về lý thuyết, chúng ta có thể yêu cầu mỹ thuật chỉ sử dụng texture trộn độ phân giải cao ở những khu vực người chơi tập trung chú ý, còn các vùng xa hoặc ít quan trọng thì dùng texture 2x2 thậm chí 1x1. Nhưng vấn đề nằm ở công cụ biên tập: làm sao tự động xác định độ phân giải thông tin trộn cần tạo?
Ở đây phát sinh vấn đề khớp nối giữa các chunk. Nếu một chunk có độ phân giải trộn cao tiếp giáp với chunk có độ phân giải thấp, đường nối có thể bị lộ rõ. Dù engine có thể xử lý nhiều mức độ phân giải, nhưng việc lựa chọn mức nào trong quá trình chỉnh sửa lại trở thành bài toán nan giải, đặc biệt khi mỹ thuật có thể tùy ý chỉnh sửa trong editor mà không nên bị làm phiền bởi các vấn đề kỹ thuật.
Ban đầu công cụ của chúng tôi xử lý phần này chưa tốt. Vài hôm trước tôi đã dành thời gian suy nghĩ và tìm ra một giải pháp khả thi. Trước tiên, trong editor cũ, việc tồn tại quá nhiều mức độ phân giải là không cần thiết, điều này liên quan đến cách định nghĩa brush vẽ bản đồ. Dù brush có kích thước khác nhau, nhưng đều có chung nguyên lý chuyển tiếp từ tâm ra ngoại vi. Hơn nữa thông tin trộn được lưu dưới dạng float.
Kết quả là nếu muốn hoàn toàn không bị răng cưa, độ phân giải trộn thực tế chỉ nên chia làm hai loại: mức cao nhất hỗ trợ và mức thấp nhất (màu đồng nhất). Vậy làm thế nào tận dụng độ phân giải đa cấp từ code? Tôi đề xuất cung cấp các brush với độ mịn (granularity) khác nhau cho mỹ thuật lựa chọn. Những khu vực không cần chi tiết sẽ dùng brush độ mịn thấp hơn (hiển thị nhiều hiệu ứng mosaic hơn).
Dữ liệu trộn có thể lưu theo cấu trúc cây tứ phân (quadtree) tự thích nghi, trực tiếp lưu thành 4 kênh RGBA thay vì float. Dù float có độ chính xác cao hơn, nhưng kết quả kết xuất chỉ hiển thị ở 16 mức, mỹ thuật điều chỉnh dựa trên phản hồi hình ảnh trực quan. Các mức độ phân giải cao hơn thực tế không mang lại giá trị thực tế.
Cấu trúc quadtree này có thể hợp nhất các thông tin trộn giống nhau ở ô lưới liền kề. Cuối cùng độ phân giải texture sẽ được xác định dựa trên độ sâu tối đa của quadtree. Nhân tiện, khi viết code cho quadtree, tôi phát hiện việc chuỗi hóa dữ liệu quadtree rất thú vị - có thể chỉ cần lượng nhỏ metadata để ghi lại cấu trúc cây (đơn giản chèn bit vào luồng dữ liệu để biểu thị nhánh có cần chia tiếp hay không).
Có một thủ thuật thông minh để định vị lá của quadtree khi biểu diễn không gian hình chữ nhật có chiều rộng là lũy thừa của 2. Ví dụ với không gian 256 đơn vị (x và y từ 0-255, mỗi tọa độ 8 bit), ta trộn từng cặp bit từ cao xuống thấp của x và y để tạo thành dãy 8 số 2 bit. Dãy này sẽ lần lượt chỉ định vị trí của nút ở từng cấp độ