# WebGL - Pipeline Grapfic

Xin chào các bạn lập trình viên đang học hỏi! Hôm nay, chúng ta sẽ bắt đầu một chuyến hành trình thú vị qua Pipeline Grapfic của WebGL. Đừng lo lắng nếu bạn mới bắt đầu học lập trình - tôi sẽ là người hướng dẫn thân thiện của bạn, và chúng ta sẽ cùng nhau đi từng bước. Cuối cùng của bài hướng dẫn này, bạn sẽ có một sự hiểu biết vững chắc về cách WebGL chuyển đổi mã của bạn thành những hình ảnh ấn tượng trên màn hình của bạn.

WebGL - Graphics Pipeline

JavaScript: Điểm Khởi Đầu

Trước khi chúng ta lặn sâu vào WebGL, hãy bắt đầu với điều gì đó quen thuộc - JavaScript. WebGL được truy cập thông qua JavaScript, làm cho nó trở thành điểm vào hoàn hảo cho cuộc phiêu lưu của chúng ta.

Chương Trình WebGL Đầu Tiên

Hãy bắt đầu với một ví dụ đơn giản:

// Lấy phần tử canvas
const canvas = document.getElementById('myCanvas');

// Lấy ngữ cảnh WebGL
const gl = canvas.getContext('webgl');

// Đặt màu xóa (màu nền)
gl.clearColor(0.0, 0.0, 0.0, 1.0);

// Xóa canvas
gl.clear(gl.COLOR_BUFFER_BIT);

Trong đoạn mã này, chúng ta đã thực hiện một số việc:

  1. Chúng ta lấy tham chiếu đến phần tử canvas HTML.
  2. Chúng ta lấy ngữ cảnh渲染 WebGL.
  3. Chúng ta đặt màu xóa (trong trường hợp này là đen).
  4. Chúng ta xóa canvas với màu chúng ta đã chỉ định.

Điều này có thể không看起来 nhiều, nhưng chúc mừng! Bạn vừa tạo ra chương trình WebGL đầu tiên của mình. Nó giống như chuẩn bị một canvas trống cho một kiệt tác.

Vertex Shader: Định Hình Thế Giới Của Bạn

Bây giờ chúng ta đã sẵn sàng canvas, hãy nói về vertex shaders. Hãy tưởng tượng vertex shaders là những người điêu khắc trong thế giới 3D của bạn. Chúng làm việc với dữ liệu thô của các đối tượng của bạn - các đỉnh.

Vertex Shader Đơn Giản

Dưới đây là ví dụ về một vertex shader cơ bản:

attribute vec4 a_position;

void main() {
  gl_Position = a_position;
}

Shader này làm một điều đơn giản nhưng quan trọng - nó lấy vị trí của mỗi đỉnh và gán nó cho gl_Position. Nó giống như đang nói với mỗi điểm của đối tượng 3D của bạn, "Bạn đi đây!"

Primitive Assembly: Kết Nối Các Điểm

Sau khi vertex shader hoàn thành công việc của mình, WebGL tiếp tục sang giai đoạn primitive assembly. Giai đoạn này giống như nối các điểm - nó lấy các đỉnh riêng lẻ và xác định cách chúng nên được nối với nhau để tạo thành các hình dạng.

Ví dụ, nếu bạn đang vẽ một tam giác, primitive assembly sẽ lấy ba đỉnh và hiểu rằng chúng tạo thành một tam giác duy nhất.

Rasterization: Pixels khắp nơi

Bây giờ đến phần kỳ diệu của rasterization. Giai đoạn này chuyển đổi các hình dạng 3D của chúng ta thành các pixel 2D mà bạn thấy trên màn hình. Nó giống như lấy một bức tượng 3D và tạo ra một bức ảnh chi tiết của nó.

Fragment Shader: Sắc Màu Thế Giới Của Bạn

Fragment shader là nơi xảy ra nghệ thuật thực sự. Trong khi vertex shader xử lý cấu trúc của các đối tượng của bạn, fragment shader lại màu sắc chúng.

Fragment Shader Đơn Giản

Dưới đây là một fragment shader cơ bản màu đỏ:

precision mediump float;

void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Shader này đặt gl_FragColor thành một vector đại diện cho màu đỏ (đỏ tối đa, không xanh lá, không xanh dương, độ trong suốt tối đa). Nó giống như nhúng toàn bộ thế giới 3D của bạn vào sơn đỏ!

Fragment Operations: Những Chỉnh Sửa Cuối Cùng

Sau fragment shader, WebGL thực hiện các thao tác khác nhau trên các mảnh. Điều này bao gồm kiểm tra độ sâu (xác định đối tượng nào ở trước đối tượng nào), trộn lẫn (cách các đối tượng trong suốt tương tác với nhau) và hơn thế nữa.

Frame Buffer: Bài Hát Cuối Cùng

Cuối cùng, chúng ta đến frame buffer. Đây là nơi hình ảnh đã render của bạn được lưu trữ trước khi hiển thị trên màn hình. Nó giống như khu vực hậu trường nơi những chỉnh sửa cuối cùng được thực hiện trước khi màn rèm nâng lên.

Kết Hợp Tất Cả

Bây giờ chúng ta đã đi qua từng giai đoạn, hãy xem cách chúng hoạt động cùng nhau trong một chương trình WebGL hoàn chỉnh:

// Mã nguồn vertex shader
const vsSource = `
    attribute vec4 aVertexPosition;

    void main() {
      gl_Position = aVertexPosition;
    }
`;

// Mã nguồn fragment shader
const fsSource = `
    precision mediump float;

    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
`;

// Khởi tạo shaders
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);

// Tạo chương trình shader
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

// Sử dụng chương trình
gl.useProgram(shaderProgram);

// Tạo bộ đệm và gửi dữ liệu
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
   1.0,  1.0,
  -1.0,  1.0,
   1.0, -1.0,
  -1.0, -1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// Báo cho WebGL cách lấy vị trí từ bộ đệm vào thuộc tính vertexPosition
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(
    gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
    numComponents,
    type,
    normalize,
    stride,
    offset);
gl.enableVertexAttribArray(gl.getAttribLocation(shaderProgram, 'aVertexPosition'));

// Vẽ cảnh
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

Chương trình này tạo một hình vuông đỏ đơn giản trên nền đen. Hãy phân tích nó:

  1. Chúng ta định nghĩa mã nguồn của vertex và fragment shaders.
  2. Chúng ta biên dịch các shaders và liên kết chúng thành một chương trình.
  3. Chúng ta tạo một bộ đệm với vị trí của các đỉnh của hình vuông của chúng ta.
  4. Chúng ta chỉ định cách WebGL hiểu dữ liệu bộ đệm.
  5. Cuối cùng, chúng ta xóa màn hình và vẽ hình vuông của chúng ta.

Mỗi bước tương ứng với một giai đoạn trong pipeline graphich mà chúng ta đã thảo luận. Nó giống như nhìn thấy một dây chuyền, nơi nguyên liệu thô (dữ liệu đỉnh) được chuyển đổi thành sản phẩm hoàn chỉnh (pixel trên màn hình).

Bảng Phương Pháp

Dưới đây là bảng của các phương thức WebGL chính mà chúng ta đã sử dụng:

Phương thức Mô tả
gl.createShader() Tạo một đối tượng shader
gl.shaderSource() Đặt mã nguồn của một shader
gl.compileShader() Biên dịch một shader
gl.createProgram() Tạo một đối tượng chương trình
gl.attachShader() Gắn một shader vào một chương trình
gl.linkProgram() Liên kết một đối tượng chương trình
gl.useProgram() Đặt chương trình chỉ định làm phần của trạng thái render hiện tại
gl.createBuffer() Tạo một đối tượng bộ đệm
gl.bindBuffer() Gắn một đối tượng bộ đệm vào một mục tiêu
gl.bufferData() Tạo và�始化 dữ liệu lưu trữ của một đối tượng bộ đệm
gl.vertexAttribPointer() Chỉ định cách lấy dữ liệu vị trí từ bộ đệm vào thuộc tính vertexPosition
gl.enableVertexAttribArray() Bật một thuộc tính vertex array
gl.clearColor() Đặt màu để sử dụng khi xóa các bộ đệm màu
gl.clear() Xóa các bộ đệm để đặt giá trị mặc định
gl.drawArrays() Render các hình học từ dữ liệu mảng

Và thế là xong! Chúng ta đã đi qua Pipeline Grapfic của WebGL, từ JavaScript đến frame buffer cuối cùng. Nhớ rằng, như bất kỳ kỹ năng nào khác, việc thành thạo WebGL đòi hỏi sự luyện tập. Nhưng với mỗi dòng mã bạn viết, bạn lại gần hơn một bước đến việc tạo ra các hình ảnh 3D ấn tượng trong trình duyệt web của bạn. Hãy tiếp tục thử nghiệm, học hỏi và quan trọng nhất, hãy vui vẻ!

Credits: Image by storyset