Cấu trúc ShaderBlendMaskBlitter và Cơ chế Blending trong Raqote

Giới thiệu về ShaderBlendMaskBlitter

Trong quy trình render đồ họa 2D của Raqote, ShaderBlendMaskBlitter đóng vai trò trung gian quan trọng để xử lý việc tô màu các pixel. Cấu trúc này tích hợp ba thành phần chính: bộ tạo màu (Shader), mặt nạ độ trong suốt (Mask) và hàm trộn màu (Blend Function). Dưới đây là phân tích chi tiết về cách thức hoạt động và cấu trúc nội bộ của nó.

Định nghĩa Cấu trúc Dữ liệu

Để quản lý trạng thái render, cấu trúc này lưu trữ các tham số cần thiết cho vùng đích và các công cụ xử lý màu. Các trường dữ liệu chính bao gồm:

pub struct ShaderBlendMaskBlitter<'a> {
    pub origin_x: i32,                  // Tọa độ X bắt đầu của vùng đích
    pub origin_y: i32,                  // Tọa độ Y bắt đầu của vùng đích
    pub color_shader: &'a dyn Shader,   // Đối tượng shader tạo màu pixel
    pub pixel_cache: Vec<u32>,          // Bộ đệm tạm chứa màu từ shader
    pub target_buffer: &'a mut [u32],   // Con trỏ tới buffer ảnh đích
    pub row_stride: i32,                // Số lượng pixel trên mỗi hàng (stride)
    pub compose_func: fn(&[u32], &[u8], &mut [u32]), // Hàm trộn màu tùy chỉnh
}

Triển khai Trait Blitter

Phương thức blit_span chịu trách nhiệm xử lý một dòng pixel cụ thể. Logic này tính toán vị trí bộ nhớ, gọi shader để lấy màu nguồn, sau đó áp dụng hàm trộn cùng với dữ liệu mask.

impl<'a> Blitter for ShaderBlendMaskBlitter<'a> {
    fn blit_span(&mut self, y: i32, x_start: i32, x_end: i32, mask_data: &[u8]) {
        // Tính toán vị trí bắt đầu của dòng hiện tại trong buffer đích
        let row_offset = (y - self.origin_y) as usize * self.row_stride as usize;
        
        // Xác định số lượng pixel cần xử lý trong đoạn này
        let pixel_count = (x_end - x_start) as usize;
        
        // Yêu cầu shader tạo màu cho đoạn pixel và lưu vào cache
        self.color_shader.shade_span(x_start, y, &mut self.pixel_cache[..], pixel_count);
        
        // Thực thi hàm trộn màu với đầu vào là màu nguồn, mask và buffer đích
        (self.compose_func)(
            &self.pixel_cache[..],                      // Dữ liệu màu nguồn
            mask_data,                                  // Dữ liệu mặt nạ alpha
            &mut self.target_buffer[(row_offset + x_start - self.origin_x) as usize..] // Vùng nhớ đích
        );
    }
}

Đặc điểm Kỹ thuật Chính

  • Tích hợp Shader linh hoạt: Màu sắc pixel không cố định mà được sinh động bởi color_shader thông qua phương thức shade_span, kết quả được lưu trữ tạm thời trước khi ghi.
  • Chiến lược Trộn màu (Blending): Hàm compose_func được truyền vào từ bên ngoài, cho phép thay đổi thuật toán hòa trộn (như alpha blending, additive, v.v.) mà không cần sửa đổi logic của Blitter.
  • Hỗ trợ Mặt nạ (Masking): Dữ liệu mask được truyền vào mỗi lần gọi blit_span, giúp kiểm soát độ trong suốt từng pixel dựa trên hình dạng đường viền hoặc vùng phủ.
  • Tối ưu truy xuất bộ nhớ: Sử dụng row_stride để hỗ trợ các buffer có bộ nhớ không liên tục, đồng thời xử lý theo từng đoạn (span) để tăng hiệu suất.

So sánh với Phiên bản Clip

Phiên bản này được tối giản hóa so với ShaderClipBlendMaskBlitter. Sự khác biệt chính nằm ở việc loại bỏ các trường liên quan đến clip region. Hàm trộn màu chỉ nhận mask làm tham số điều khiển, giúp giảm độ phức tạp tính toán trong các trường hợp không yêu cầu cắt xén hình học phức tạp.

Quy trình Thực thi

  1. Xác định tọa độ dòng scanline cần vẽ (y) và đoạn ngang (x_start đến x_end).
  2. Gọi shader để điền dữ liệu màu thô vào bộ đệm tạm pixel_cache.
  3. Thực thi hàm compose_func để kết hợp màu nguồn, giá trị mask và màu hiện có trong target_buffer.

Ví dụ về Hàm Trộn Màu

Một hàm trộn alpha cơ bản có thể được triển khai như sau, sử dụng vòng lặp để tính toán tỷ lệ hòa trộn dựa trên giá trị mask:

fn standard_alpha_blend(source: &[u32], alpha_map: &[u8], output: &mut [u32]) {
    for index in 0..source.len() {
        // Chuẩn hóa giá trị alpha từ 0-255 về 0.0-1.0
        let factor = alpha_map[index] as f32 * (1.0 / 255.0);
        // Thực hiện pha trộn màu giữa nguồn và đích
        output[index] = composite_pixels(source[index], output[index], factor);
    }
}

Cách tiếp cận này phù hợp cho các hệ thống yêu cầu khả năng tùy biến cao trong quá trình render, ví dụ như giao diện người động hoặc các hiệu ứng hạt nơi thuật toán blending cần thay đổi theo ngữ cảnh.

Thẻ: Raqote Rust 2D Graphics Shader Blending Rasterization

Đăng vào ngày 28 tháng 5 lúc 17:00