Vertex Shader là một giai đoạn có thể lập trình của pipeline đồ họa (graphics pipeline) xử lý từng đỉnh của mô hình 3D một cách độc lập. Theo Khronos Group, 2025, vertex shader nhận đầu vào là các thuộc tính đỉnh — tọa độ, pháp tuyến, màu sắc và tọa độ kết cấu — và biến đổi chúng theo logic đã định. Vertex Shader được thực thi cho mỗi đỉnh một cách độc lập, cho phép song song hóa tính toán hiệu quả trên hàng nghìn lõi GPU.
Những điểm chính
Vertex Shader là một giai đoạn có thể lập trình của pipeline đồ họa xử lý từng đỉnh của mô hình 3D một cách riêng lẻ. Đỉnh (vertex) là một điểm trong không gian ba chiều chứa các thuộc tính: tọa độ xyz, pháp tuyến, màu sắc và tọa độ kết cấu UV.
Không giống như pipeline chức năng cố định (fixed-function pipeline) nơi các phép biến đổi được thực hiện theo thuật toán cứng nhắc, Vertex Shader cho phép nhà phát triển kiểm soát hoàn toàn việc biến đổi đỉnh. Điều này cho phép triển khai hoạt ảnh thủ tục, biến dạng mô hình, skinning xương và bất kỳ phép biến đổi toán học nào ở phía GPU.
Pipeline đồ họa OpenGL ES bao gồm một chuỗi các giai đoạn từ dữ liệu đầu vào đến hình ảnh cuối cùng. Vertex Shader nằm sau giai đoạn lắp ráp đỉnh (vertex assembly) và trước giai đoạn rasterization. Mỗi đỉnh được gửi từ CPU qua bộ đệm đỉnh đi qua Vertex Shader đúng một lần và kết quả được chuyển đến giai đoạn tiếp theo để lắp ráp nguyên thủy.
| Giai đoạn pipeline | Mục đích |
|---|---|
| Vertex Specification | Truyền dữ liệu đỉnh và thuộc tính từ VBO |
| Vertex Shader | Xử lý từng đỉnh, biến đổi tọa độ |
| Tessellation | Phân chia tùy chọn hình học thành các nguyên thủy con |
| Geometry Shader | Tạo hoặc loại bỏ tùy chọn các nguyên thủy |
| Rasterization | Chuyển đổi nguyên thủy thành mảnh (fragment) để tô màu |
Vertex Shader nhận các thuộc tính đỉnh từ bộ đệm đỉnh (VBO) thông qua các con trỏ thuộc tính đỉnh được cấu hình trong mã CPU. Nhà phát triển viết mã shader bằng GLSL (OpenGL Shading Language) hoặc ngôn ngữ tương đương, trình điều khiển GPU sẽ biên dịch thành các chỉ thị máy cho chip cụ thể.
Đối với mỗi đỉnh, Vertex Shader thực hiện tuần tự ba thao tác: đọc thuộc tính đầu vào — áp dụng các phép biến đổi do nhà phát triển định nghĩa — ghi kết quả vào biến đầu ra. Đầu ra tối thiểu bắt buộc là vị trí đỉnh trong không gian cắt (clip space) (gl_Position). Tất cả dữ liệu đầu ra khác (màu sắc, pháp tuyến, tọa độ kết cấu) được nội suy và chuyển đến giai đoạn tiếp theo — Fragment Shader.
#version 300 es
in vec3 aPosition;
in vec2 aTexCoord;
uniform mat4 uMVP;
out vec2 vTexCoord;
void main() {
gl_Position = uMVP * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
Vertex Shader giải quyết một loạt các nhiệm vụ của pipeline đồ họa liên quan đến biến đổi hình học. Mỗi nhiệm vụ được thực hiện thông qua các phép toán trên vector và ma trận, được thực thi song song cho hàng nghìn đỉnh cùng một lúc.
Nhiệm vụ cơ bản và phổ biến nhất là nhân tọa độ đỉnh với các ma trận model, view và projection (MVP). Ma trận MVP biến đổi một đỉnh từ không gian mô hình cục bộ thành không gian cắt (clip space), sau đó được chuẩn hóa và chuyển đổi thành tọa độ màn hình trong các giai đoạn tự động của pipeline.
Đối với các nhân vật hoạt hình, Vertex Shader thực hiện hoạt ảnh xương (skinning). Mỗi đỉnh được gắn vào nhiều xương với các trọng số cụ thể. Vertex blending — tổng trọng số của các phép biến đổi từ các xương được gắn — cho phép biến dạng da nhân vật một cách mượt mà khi bộ xương di chuyển.
Vertex Shader có thể sửa đổi hình học dựa trên thời gian hoặc các tham số uniform khác mà không cần CPU tham gia. Wave deformation, dịch chuyển nhiễu Perlin và hiệu ứng gợn sóng nước — tất cả các hiệu ứng này được thực hiện trong vertex shader mà không cần tải lại hình học từ RAM.
Mặc dù cả hai loại shader đều là các giai đoạn có thể lập trình của pipeline đồ họa, Vertex Shader và Fragment Shader giải quyết các nhiệm vụ khác nhau về cơ bản và hoạt động ở các giai đoạn khác nhau của xử lý hình ảnh.
| Đặc điểm | Vertex Shader | Fragment Shader |
|---|---|---|
| Dữ liệu đầu vào | Thuộc tính đỉnh từ VBO | Dữ liệu nội suy từ VS |
| Số lần gọi | Số lượng đỉnh | Số lượng mảnh |
| Nhiệm vụ chính | Biến đổi hình học | Tính màu pixel |
| Tải tính toán | Độ phức tạp hình học | Độ phân giải màn hình |
| Truy cập kết cấu | Hạn chế (qua texture lookup) | Truy cập đầy đủ |
Sự khác biệt chính là tần suất gọi. Vertex Shader được gọi một lần cho mỗi đỉnh, trong khi Fragment Shader được gọi cho mỗi mảnh (pixel). Trong một cảnh có độ phân giải cao, số lần gọi Fragment Shader vượt quá số lần gọi Vertex Shader hàng nghìn lần, khiến việc tối ưu hóa pixel shader càng trở nên quan trọng hơn.
Hiệu suất của Vertex Shader cực kỳ quan trọng đối với các ứng dụng thời gian thực, đặc biệt là trong phát triển di động với mức tiêu thụ điện năng hạn chế. Các yếu tố hiệu suất chính là số lượng đỉnh, độ phức tạp của mã shader và băng thông bộ nhớ GPU.
Để giảm tải cho Vertex Shader, các phương pháp sau được sử dụng. Level of Detail (LOD) — chuyển sang các phiên bản mô hình đơn giản hóa khi camera di chuyển ra xa đối tượng. Instancing — vẽ cùng một hình geometry nhiều lần với các ma trận biến đổi khác nhau trong một lệnh gọi vẽ duy nhất (draw call).
// Vertex Shader hỗ trợ instancing
in vec3 aPosition;
in mat4 aInstanceMatrix;
uniform mat4 uViewProjection;
void main() {
vec4 worldPos = aInstanceMatrix * vec4(aPosition, 1.0);
gl_Position = uViewProjection * worldPos;
}
Vertex Shader trên các API đồ họa khác nhau có logic tương tự nhưng cú pháp và quy ước đặt tên khác nhau. Hãy xem cách triển khai vertex shader cơ bản bằng GLSL cho OpenGL ES 3.0 và bằng Metal Shading Language cho iOS và macOS.
#version 300 es
in vec4 position;
in vec3 normal;
in vec2 texcoord;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
out vec3 vNormal;
out vec2 vTexCoord;
out vec3 vPosition;
void main() {
vec4 mvPosition = modelViewMatrix * position;
vPosition = mvPosition.xyz;
vNormal = normalMatrix * normal;
vTexCoord = texcoord;
gl_Position = projectionMatrix * mvPosition;
}
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
};
struct VertexOut {
float4 position [[position]];
float3 worldNormal;
};
vertex VertexOut
vertexMain(VertexIn in [[stage_in]],
constant float4x4& mvp [[buffer(1)]]) {
VertexOut out;
out.position = mvp * float4(in.position, 1.0);
out.worldNormal = in.normal;
return out;
}
Câu hỏi thường gặp
Vertex Shader là một chương trình trên GPU xử lý từng đỉnh của mô hình 3D và biến đổi tọa độ của nó thành tọa độ màn hình. Hãy tưởng tượng mô hình như một lưới các điểm; vertex shader di chuyển các điểm này theo các quy tắc toán học đã định.
Vertex Shader xử lý các đỉnh và chịu trách nhiệm về hình học, trong khi Fragment Shader xác định màu sắc của mỗi pixel. Cái đầu được gọi cho mỗi đỉnh (hàng nghìn), cái sau được gọi cho mỗi pixel (hàng triệu).
Vertex Shader được viết bằng ngôn ngữ tương ứng với API đồ họa: GLSL cho OpenGL ES, Metal Shading Language cho Metal API trên iOS/macOS, HLSL cho Direct3D. Về mặt khái niệm, tất cả đều thực hiện các thao tác giống nhau trên các đỉnh.
Độ phức tạp của vertex shader ảnh hưởng trực tiếp đến FPS: nhiều chỉ thị hơn trên mỗi đỉnh = thời gian xử lý lâu hơn. Tối ưu hóa bao gồm giảm số lượng đỉnh qua LOD, sử dụng instancing và giảm thiểu rẽ nhánh có điều kiện trong mã shader.
Các nhiệm vụ chính trong game: biến đổi tọa độ qua ma trận MVP, skinning nhân vật hoạt hình, biến dạng thủ tục của quần áo và tóc, biến dạng giữa các biểu cảm khuôn mặt và hiệu ứng hạt với hoạt ảnh trên GPU.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm