Phương Pháp Đơn Giản Để Render Font Viền
Trong game của chúng tôi, việc render chữ cần có hiệu ứng viền. Một giải pháp phổ biến là vẽ chữ nhiều lần với các độ lệch 1-2 pixel theo mọi hướng bằng màu đen, sau đó phủ lớp chữ màu cần thiết lên trên. Ưu điểm của phương pháp này là dễ triển khai, tuy nhiên nhược điểm là hiệu suất rendering bị ảnh hưởng do mỗi ký tự phải vẽ nhiều lần.
Vào những năm gần đây, một phương pháp tiên tiến hơn đã ra đời mà bạn có thể tìm thấy bằng từ khóa “Signed Distance Field Font Rendering” trên Google. Cơ chế chính là tiền xử lý dữ liệu font, ghi lại khoảng cách từ mỗi pixel đến nét chữ dưới dạng độ xám trên texture, sau đó sử dụng shader chuyên dụng để render. Ưu điểm vượt trội là khả năng co giãn font mượt mà không răng cưa, đồng thời dễ dàng tạo hiệu ứng viền. Tuy nhiên nhược điểm là cần tiền xử lý dữ liệu font bên ngoài.
Cả dự án mobile và game client 3D của chúng tôi đều sử dụng rộng rãi font viền. Tôi mong muốn tận dụng trực tiếp font hệ thống mà không cần tiền xử lý hay đóng gói file font vào client. Gần đây tôi đã phát triển một mô-đun quản lý texture font động để đáp ứng yêu cầu này. Nhân tiện, nền tảng Apple cung cấp các API cấp cao cho phép tạo font viền trực tiếp.
Vấn đề phát sinh khi dữ liệu font viền chứa đồng thời thông tin viền và phần chính của chữ. Việc lưu trữ toàn bộ thông tin này thành một kênh (channel) đơn trở nên phức tạp, gây khó khăn cho việc tô màu. Hãy quan sát hình ảnh này (char_a_org.png) - phần viền màu đen và thân chữ màu trắng thể hiện qua các sắc độ xám, nhưng kênh alpha lại không đồng nhất. Đặc biệt ở vùng chuyển tiếp từ viền sang thân chữ, màu sắc là xám nhưng giá trị alpha vẫn là 1.0, chứng tỏ kênh alpha hoàn toàn độc lập.
Thông thường chúng ta cần hai kênh riêng biệt (màu sắc và alpha) để render chính xác, nhưng phần lớn phần cứng GPU không hỗ trợ kết cấu hai kênh. Giải pháp thay thế là sử dụng kết cấu RGBA 4 kênh hoặc hai kết cấu đơn kênh riêng biệt.
Tôi đã sáng tạo một phương pháp khéo léo để nén toàn bộ thông tin vào một kênh duy nhất:
- Các pixel có alpha = 1.0 sẽ được ánh xạ độ sáng từ 0.5 đến 1.0
- Các pixel có alpha < 1.0 sẽ được ánh xạ giá trị alpha từ 0 đến 0.5
Phương pháp khả thi vì các pixel có alpha < 1.0 sau khi viền đen đều có giá trị RGBA bằng nhau (màu đen). Như vậy chỉ mất 1 bit thông tin nhưng vẫn bảo toàn được dữ liệu cần thiết.
Kết cấu sau xử lý có dạng như này (char_a_trans.png). Nhờ quá trình chuyển sắc mượt mà, việc lấy mẫu texture hoàn toàn chính xác. Chỉ cần một shader đơn giản là có thể tô màu chữ theo ý muốn. Kết quả cuối cùng như hình này (char_a_color.png).
Shader xử lý rất đơn giản:
|
|
Công thức này cho phép tái tạo chính xác cả kênh màu và kênh alpha gốc từ dữ liệu kết cấu một kênh.