This programming guide is only available in Vietnamese. Switch the language toggle to Vietnamese to read the full article.

Khi chơi game 3D, chúng ta thường thấy các hiệu ứng hình ảnh điện ảnh ấn tượng như: làm mờ chuyển động (motion blur), viền tối điện ảnh (vignette), chuyển màu nghệ thuật, hay phát sáng (bloom). Những hiệu ứng này không được vẽ trực tiếp từ mô hình 3D thô mà được xử lý ở giai đoạn sau cùng gọi là Hậu kỳ (Post-processing) nhờ kỹ thuật kết xuất gián tiếp qua Framebuffer Object (FBO).

1. Khái niệm Render to Texture & Off-screen Framebuffer

Mặc định, mọi lệnh vẽ của WebGL sẽ kết xuất điểm ảnh trực tiếp lên màn hình của trình duyệt (được gọi là Default Framebuffer). Tuy nhiên, WebGL cho phép chúng ta tạo ra các bộ đệm ảo tùy ý gọi là Framebuffer Object (FBO).

Thay vì vẽ lên màn hình, GPU vẽ toàn bộ khung cảnh 3D vào một Texture được gắn kết (attached) làm bộ nhớ màu sắc của FBO. Kỹ thuật này được gọi là Render to Texture.

2. Các bước khởi tạo Framebuffer Object (FBO)

Quy trình thiết lập một FBO off-screen hoàn chỉnh:

  1. Tạo FBO phụ:
    const fbo = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
  2. Tạo và gắn kết Texture làm kênh màu (Color Attachment):
    const targetTexture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, targetTexture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
  3. Tạo và gắn kết Depth Buffer để hỗ trợ tính toán ẩn/hiện 3D (Depth Testing) bên trong FBO:
    const depthBuffer = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
  4. Xác minh tính hoàn tất của FBO trước khi vẽ:
    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
        console.error("Lỗi cấu hình FBO!");
    }

3. Bộ lọc Hậu kỳ trên màn phẳng (Screen-space Quad)

Sau khi đã render khung cảnh 3D vào FBO Texture, quá trình hậu kỳ diễn ra qua 2 bước:

  1. Hủy kích hoạt FBO để quay lại vẽ lên màn hình chính: `gl.bindFramebuffer(gl.FRAMEBUFFER, null);`
  2. Vẽ một hình chữ nhật phẳng 2D phủ kín toàn bộ màn hình (Screen-space Quad) dùng FBO Texture làm đầu vào.
  3. Trong Fragment Shader của hình phẳng này, chúng ta áp dụng các công thức toán học để xử lý điểm ảnh (pixel manipulation):
    • Grayscale (Ảnh xám): Lấy trung bình trọng số độ sáng của mắt người đối với 3 kênh đỏ, xanh lá, xanh dương:
      gray = 0.2126*R + 0.7152*G + 0.0722*B
    • Vignette (Tối góc): Giảm độ sáng của pixel tỷ lệ thuận với bình phương khoảng cách từ tâm màn hình:
      vignette = 1.0 - dot(uv - 0.5, uv - 0.5) * 1.5
    • Kernel Filters (Lọc hạt nhân): GPU đọc 9 pixel lân cận xung quanh pixel hiện tại và nhân chập với ma trận hệ số để tạo các hiệu ứng mờ (Gaussian Blur) hoặc lọc biên cạnh sắc nét (Sobel Edge).
🎮 Demo tương tác: 3D Offscreen Rendering & Post-processing Kernels

4. Câu hỏi trắc nghiệm ôn tập

Trắc nghiệm 1: Gắn kết DEPTH_ATTACHMENT cho FBO

Tại sao khi cấu hình FBO để vẽ cảnh 3D có kiểm tra chiều sâu (Depth testing), ta bắt buộc phải gắn kết Depth buffer (Depth attachment) bên cạnh Color texture?

Trắc nghiệm 2: Luma Grayscale formula

Tại sao trong công thức đổi sang ảnh xám Grayscale (Luma), hệ số nhân cho màu xanh lá (Green) lại lớn vượt trội so với Đỏ (Red) và Xanh dương (Blue)?

Trắc nghiệm 3: Vì sao cần Render-to-Texture (FBO) cho Post-processing

Tại sao kỹ thuật hậu xử lý (post-processing) bắt buộc phải vẽ cảnh ra một Framebuffer Object (texture) trước, thay vì xử lý trực tiếp lên màn hình?

Mã nguồn thiết lập FBO offscreen rendering và bộ lọc pixel:

Tải mã nguồn FBO & Post-processing

Related Articles

Bài viết liên quan trong series

Lesson 12: Real-time Shadow Mapping Bài 12: Đổ Bóng Real-time (Shadow Mapping) Lesson 10: Transparency, Blending & Depth Sorting Bài 10: Độ Trong Suốt, Alpha Blending & Sắp Xếp Độ Sâu Back to WebGL Course Overview Quay lại Lộ trình WebGL Series