Nén Hình Ảnh DXT - nói dối e blog

Nén Hình Ảnh DXT

Hai ngày nay mình đang viết chương trình giải mã định dạng DDS. DDS là định dạng hình ảnh do Microsoft phát triển dành cho DirectX, cấu trúc file được mô tả chi tiết trên MSDN: DDS File

Định dạng nén DXT hiện đã được hầu hết phần cứng card đồ họa 3D hỗ trợ. (Nó sử dụng thuật toán nén hình ảnh có mất mát do công ty S3 phát minh. Thú vị là trong cuốn sách mình đang đọc, trang 232 có đề cập đến vấn đề này). DXT còn có tên gọi khác là S3TC, hiện tại định dạng .dds là định dạng hình ảnh phổ biến duy nhất hỗ trợ loại nén này. Để thuận tiện cho việc phát triển engine, chúng mình cũng tích hợp khả năng tải file .dds.

Đồng nghiệp làm cùng team engine đề xuất dù phần cứng không hỗ trợ, vẫn cần tải và sử dụng texture bình thường. Từ đó nảy sinh nhu cầu giải mã DXT trên phần mềm (soft decoding). Rất may mình đã nghiên cứu qua trước đó nên việc viết code không gặp nhiều khó khăn.

DXT1 hỗ trợ kênh alpha 1 bit. Thực tế đây là tính năng tùy chọn. Mỗi khối 4x4 có thể chọn có hoặc không có hiệu ứng trong suốt. Khi không cần alpha, mỗi khối có 4 màu (trong đó 2 màu được nội suy); nếu cần alpha thì chỉ còn 3 màu, giá trị 11 được dành riêng để biểu diễn điểm trong suốt. Để phân biệt, ta cần so sánh hai giá trị màu đầu tiên trong khối là color_0color_1. Nếu color_0 (được coi là số nguyên không dấu) lớn hơn color_1 thì khối đó không có kênh alpha.

Tuy nhiên, định dạng file dds không hề ghi nhận thông tin: toàn bộ hình ảnh có chứa ít nhất một khối có alpha hay không. Trong các ứng dụng 3D, thông tin này rất cần thiết để tối ưu hiệu năng.

Đối với chương trình giải mã phần mềm, điều này càng quan trọng hơn. Vì khi có alpha, dữ liệu phải được giải thành định dạng RGBA5551; còn không có alpha thì dùng RGB565. Ban đầu mình tưởng phải quét toàn bộ dữ liệu để kiểm tra xem có khối nào color_0color_1 hay không. Nhưng khi thực nghiệm với vài file dds tạo bởi công cụ nén, mình mới nhận ra sai lầm. Công cụ nén DXT của NVIDIA cho phép người dùng chủ động chọn có dùng kênh alpha hay không. Nếu chọn không dùng, thứ tự màu color_0color_1 trong từng khối là không quan trọng (giúp cải thiện chất lượng hình ảnh do phương pháp nội suy khác biệt). Điều đó có nghĩa chỉ người nén hình mới biết có alpha hay không, còn trong file không hề lưu lại thông tin này.

DXT3 về cơ bản là DXT1 được bổ sung thêm kênh alpha 4 bit. Mỗi khối 4x4 sẽ tốn thêm 64 bit để lưu trữ thông tin alpha này. (Về mặt lưu trữ, dữ liệu alpha được đặt trước dữ liệu màu trong mỗi khối)

DXT5 cải tiến cách lưu trữ alpha một cách thú vị đáng kể. Thay vì lưu trực tiếp, nó vẫn dùng 64 bit cho 16 giá trị alpha. Trong đó 2 byte đầu tiên chứa giá trị alpha lớn nhất và nhỏ nhất của khối. 48 bit tiếp theo dành cho 16 pixel, mỗi pixel chiếm 3 bit.

Khi alpha_0 > alpha_1, 3 bit cho phép tạo ra 8 mức alpha nội suy. Ngược lại, 110 và 111 lần lượt biểu thị alpha = 0 và 255, giữa đó có 6 mức nội suy trung gian.

Thông tin chi tiết về thuật toán nén DXT3 và DXT5 có thể tìm thấy trên MSDN trong bài viết “Textures with Alpha Channels”.

Về DXT2 và DXT4, chúng ít được dùng hơn. Về mặt nén dữ liệu, chúng hoàn toàn giống DXT3 và DXT5, điểm khác biệt duy nhất nằm ở việc màu có được nhân với alpha trước (pre-multiplied) hay không.

Mình hơi băn khoăn khi định dạng dds có thể đánh dấu việc nhân alpha bằng 4 byte đặc biệt, nhưng lại không lưu thông tin kênh alpha quan trọng của DXT1 trong file header. Thật không hiểu nổi cách thiết kế định dạng này! >_<

0%