Shader là gì - các loại shader và cách chúng hoạt động

Tác giả: IT Sectr Đã đăng: 2026-06-11 Thời gian đọc: 8 phút

Shader là chương trình chạy trên GPU để xử lý dữ liệu đồ họa: đỉnh, mảnh hoặc các tác vụ tính toán. Không giống như mã thông thường trên CPU, shader hoạt động song song trên hàng trăm lõi, xử lý từng phần tử một cách độc lập. Theo Khronos Group (2025), shader được sử dụng trong 100% ứng dụng 3D hiện đại — từ game di động đến hiệu ứng UI trong ứng dụng hệ thống. Nhà phát triển viết shader bằng các ngôn ngữ chuyên dụng: GLSL, HLSL, Metal Shading Language hoặc SPIR-V.

Các điểm chính

  • Shader là chương trình cho GPU chạy song song trên hàng trăm lõi cho mỗi phần tử của cảnh.
  • Các loại shader: vertex, fragment, compute, geometry, tessellation và mesh shader.
  • Ngôn ngữ: GLSL (OpenGL), HLSL (DirectX), MSL (Metal), SPIR-V (Vulkan) — tất cả được biên dịch thành mã máy GPU.
  • Shader trong phát triển di động được sử dụng cho hiệu ứng, hậu xử lý, chiếu sáng và tính toán.
  • Tối ưu hóa shader: độ chính xác mediump, giảm thiểu rẽ nhánh và ATF (Adaptive Texture Fetch) cho GPU di động.

Shader là gì?

Shader là một chương trình nhỏ được viết bằng ngôn ngữ chuyên dụng và chạy trên GPU. Mỗi shader xử lý một phần tử dữ liệu — đỉnh, mảnh (pixel) hoặc phần tử công việc — độc lập với các phần tử khác. Tính song song của GPU cho phép chạy hàng nghìn phiên bản shader đồng thời.

Thuật ngữ này được Pixar giới thiệu vào những năm 1980 để mô tả các chương trình điều khiển bề ngoài của bề mặt trong RenderMan. Shader thời gian thực xuất hiện với NVIDIA GeForce 3 (2001) — card đồ họa đầu tiên có pipeline lập trình được. Trước shader, đồ họa được cấu hình bằng các tham số cố định: màu sắc, kết cấu, chế độ hòa trộn.

Theo Unity Technologies (2025), một game di động trung bình sử dụng 50–200 shader. Mỗi shader có các biến thể cho các nền tảng, mức chất lượng và cấu hình chiếu sáng khác nhau. Số lượng biến thể có thể lên tới 10.000 mỗi dự án.

Kiến trúc shader được chia thành dữ liệu đầu vào (thuộc tính, biến uniform, kết cấu), mã chương trìnhdữ liệu đầu ra (màu sắc, vị trí). Biến uniform được CPU đặt trước khi thực thi shader và không thay đổi cho tất cả các phần tử của khung hình. Thuộc tính là dữ liệu trên mỗi phần tử (tọa độ đỉnh, pháp tuyến, tọa độ UV).

Mô hình thực thi SIMT

SIMT (Single Instruction, Multiple Threads) là mô hình thực thi shader trên GPU. Một lệnh được tải một lần và được thực thi bởi hàng trăm luồng trên các dữ liệu khác nhau. Ví dụ, Fragment Shader cho 1000 pixel chạy 1000 luồng với cùng mã, nhưng mỗi luồng nhận tọa độ UV và thuộc tính nội suy riêng.

Một hệ quả quan trọng của SIMT: rẽ nhánh trong shader rất tốn kém. Nếu trong khối if, một nửa số luồng đi theo một nhánh và nửa còn lại đi theo nhánh khác, GPU thực thi cả hai nhánh tuần tự cho tất cả các luồng. Hiệu suất giảm xuống 50%. Tránh rẽ nhánh động trong shader hoặc giảm thiểu chúng thông qua tính toán trước trên CPU.

Các loại shader

GPU hiện đại hỗ trợ nhiều loại shader, mỗi loại cho một giai đoạn của pipeline đồ họa hoặc tính toán. Hãy xem xét các loại chính: vertex, fragment, compute và mesh shader hiện đại.

Loại shaderGiai đoạn pipelineXử lýNgôn ngữ
Vertex ShaderĐỉnhMỗi đỉnhGLSL, HLSL
Fragment ShaderMảnhMỗi pixelGLSL, HLSL
Compute ShaderTính toánDữ liệu tùy ýGLSL, HLSL
Geometry ShaderHình họcNguyên thủyGLSL, HLSL
Tessellation ShaderTessellationPatchGLSL, HLSL
Mesh ShaderPipeline meshTask + MeshHLSL, MSL

Vertex Shader

Vertex Shader xử lý mỗi đỉnh của hình học: biến đổi tọa độ, tính toán chiếu sáng, truyền dữ liệu cho fragment shader. Đây là giai đoạn bắt buộc của pipeline đồ họa — nếu không có nó, các đỉnh sẽ không xuất hiện trên màn hình. Vertex Shader được gọi cho mỗi đỉnh của mỗi khung hình.

Fragment Shader

Fragment Shader (hoặc Pixel Shader) là giai đoạn tốn nhiều tài nguyên nhất. Nó tính toán màu sắc cuối cùng của mỗi mảnh bằng cách sử dụng kết cấu, chiếu sáng và vật liệu. Fragment Shader được thực thi cho mỗi pixel được phủ bởi hình học, xét đến tất cả các kết cấu và hiệu ứng đã áp dụng. Tối ưu hóa shader này mang lại cải thiện hiệu suất lớn nhất.

Compute Shader

Compute Shader là shader đa năng cho các tính toán tùy ý trên GPU. Không giống như shader đồ họa, nó không bị ràng buộc với hình học hay pixel. Compute Shader làm việc với các bộ đệm dữ liệu tùy ý thông qua các nhóm công việc. Nó được sử dụng cho vật lý, mô phỏng hạt, hậu xử lý và suy luận mạng nơ-ron.

Mesh Shader

Mesh Shader là loại shader mới nhất, xuất hiện với NVIDIA Turing (2018) và Metal 3. Mesh Shader thay thế các shader vertex, geometry và tessellation bằng một pipeline mesh duy nhất. Nó hoạt động cùng với Task Shader: Task Shader quyết định nhóm mesh nào cần render, Mesh Shader tạo hình học ngay lập tức.

Ngôn ngữ viết shader

Shader được viết bằng các ngôn ngữ chuyên dụng được biên dịch thành mã máy GPU. Việc chọn ngôn ngữ phụ thuộc vào nền tảng và API. Hãy xem xét các ngôn ngữ chính: GLSL, HLSL, Metal Shading Language và định dạng trung gian SPIR-V.

GLSL (OpenGL Shading Language)

GLSL là ngôn ngữ shader cho OpenGL, OpenGL ES và WebGL. Cú pháp dựa trên C với việc bổ sung các kiểu vector (vec2, vec4, mat4) và các hàm tích hợp (texture, normalize, reflect). GLSL ES là phiên bản cho thiết bị di động với độ chính xác hạn chế (lowp, mediump, highp). Theo Khronos (2025), GLSL vẫn là ngôn ngữ shader phổ biến nhất nhờ tính đa nền tảng.

glsl
#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 fragColor;

void main() {
    fragColor = texture(u_texture, v_texCoord);
}

Fragment shader đơn giản đọc màu từ kết cấu theo tọa độ UV. precision mediump yêu cầu GPU sử dụng nửa độ chính xác cho float — tăng tốc độ thực thi trên thiết bị di động lên 25–40%.

HLSL (High-Level Shader Language)

HLSL là ngôn ngữ shader của Microsoft cho DirectX. Cú pháp gần với C++ hơn, hỗ trợ lớp, cấu trúc và template. HLSL được sử dụng trong các ứng dụng Windows và thông qua Shader Model 6.7+ hỗ trợ Ray Tracing, Mesh Shaders và Sampler Feedback. Đối với phát triển di động, HLSL không được sử dụng trực tiếp nhưng được biên dịch sang SPIR-V cho Vulkan.

Metal Shading Language

MSL (Metal Shading Language) là ngôn ngữ shader của Apple, dựa trên C++14. Không giống GLSL và HLSL, MSL được biên dịch cùng với ứng dụng, loại bỏ quá trình biên dịch JIT trên thiết bị. MSL hỗ trợ con trỏ, template và thư viện chuẩn C++. Nó được sử dụng trên tất cả thiết bị Apple: iPhone, iPad, Mac, Apple TV.

cpp
#include <metal_stdlib>
using namespace metal;
struct VertexOut {
    float4 position [[position]];
    float2 texCoord;
};

fragment float4
myFragment(VertexOut in [[stage_in]],
            texture2d<float> tex [[texture(0)]]) {
    constexpr sampler s = sampler(filter::linear);
    return tex.sample(s, in.texCoord);
}

MSL sử dụng các thuộc tính [[position]], [[stage_in]] và [[texture(N)]] để liên kết tài nguyên. Trình biên dịch Apple tạo mã tối ưu cho GPU hiện tại, xét đến số lượng thanh ghi và bộ nhớ đệm.

SPIR-V

SPIR-V là định dạng nhị phân trung gian cho shader, tiêu chuẩn cho Vulkan. Shader được viết bằng GLSL hoặc HLSL, biên dịch thành SPIR-V và nạp vào ứng dụng Vulkan. SPIR-V không bị ràng buộc với ngôn ngữ cụ thể — có trình biên dịch từ Rust, Python, OpenCL C sang SPIR-V.

Shader trong phát triển di động

Shader trên nền tảng di động có hạn chế so với desktop: ít thanh ghi hơn, độ chính xác hạn chế và thiếu hỗ trợ một số lệnh. Hãy xem xét đặc thù của shader trên Android (Vulkan/OpenGL ES) và iOS (Metal).

Shader trên Android

Android sử dụng GLSL ES cho OpenGL và SPIR-V cho Vulkan. OpenGL ES 3.2 hỗ trợ shader với mediump mặc định. Vulkan yêu cầu biên dịch tường minh GLSL sang SPIR-V qua glslangValidator. Trên Android, shader được tải từ tài nguyên chuỗi hoặc từ tệp SPIR-V đã biên dịch.

Qualcomm Adreno GPU tối ưu hóa shader ở cấp driver. Khuyến nghị: sử dụng mediump cho màu sắc và UV, highp chỉ cho vị trí; tránh lấy mẫu kết cấu trong vertex shader; nhóm các tính toán thành vector (vec4 thay vì 4x float). Theo Qualcomm (2025), các tối ưu này mang lại cải thiện 30–50%.

Shader trên iOS

iOS sử dụng duy nhất Metal Shading Language. Shader được biên dịch thành mã máy cùng với ứng dụng qua Xcode. Metal cung cấp Shader DebuggerGPU Capture để phân tích shader. GPU Apple (TBDR) có các tối ưu đặc thù: early fragment test, memoryless render targets và blending lập trình được.

Theo Apple WWDC 2024, đối với shader iOS, điều quan trọng là sử dụng half thay vì float, giới hạn áp lực thanh ghi (tối đa 64 thanh ghi) và tránh lấy mẫu kết cấu phụ thuộc. Shader iOS phải nhỏ gọn — kích thước lý tưởng 50–100 lệnh ALU.

Shader trong Unity và Unreal Engine

Các engine game Unity và Unreal Engine cung cấp trình chỉnh sửa shader trực quan (Shader Graph, Material Editor) và ngôn ngữ trừu tượng (ShaderLab, USF). Nhà phát triển viết shader bằng ngôn ngữ cấp cao và engine biên dịch cho nền tảng đích. Unity sử dụng HLSL làm ngôn ngữ trung gian, Unreal sử dụng USF (Unreal Shader Format).

Tối ưu hóa shader

Tối ưu hóa shader là bước quan trọng trong phát triển đồ họa cho thiết bị di động. Một shader viết kém có thể giảm FPS từ 60 xuống 20. Hãy xem xét các quy tắc và kỹ thuật tối ưu chính.

Độ chính xác tính toán

Sử dụng độ chính xác tối thiểu đủ dùng: lowp cho màu sắc và UV, mediump cho pháp tuyến và chiếu sáng, highp chỉ cho vị trí và ma trận. Theo ARM (2025), phép toán mediump nhanh gấp 2 lần highp trên GPU Mali. Trong GLSL ES, điều này được thiết lập qua bộ định tính precision.

Giảm thiểu lấy mẫu kết cấu

Lấy mẫu kết cấu là thao tác tốn kém nhất trong shader (10–30 chu kỳ so với 1–2 cho lệnh ALU). Giảm số lần lấy mẫu: kết hợp kết cấu thành atlas, lưu dữ liệu trong mảng uniform thay vì kết cấu, lưu đệm kết quả của pixel lân cận qua lệnh đạo hàm.

Tránh rẽ nhánh

Rẽ nhánh động (if với biến uniform) làm giảm hiệu suất pipeline SIMT. Thay thế rẽ nhánh bằng các hàm toán học: mix(), step(), smoothstep()clamp(). Các hàm này thực thi trong 1–2 lệnh, trong khi rẽ nhánh có thể tốn 8–16 lệnh do phân kỳ luồng.

glsl
// Xấu: rẽ nhánh động trong shader
if (u_enableLight) {
    color *= computeLighting(normal);
}

// Tốt: thay thế rẽ nhánh bằng toán học
color *= mix(1.0, computeLighting(normal),
              float(u_enableLight));

mix() thực hiện nội suy tuyến tính giữa hai giá trị. Khi u_enableLight=0, nó trả về 1.0 — màu sắc không thay đổi. Khi u_enableLight=1, nó trả về kết quả chiếu sáng. Không có rẽ nhánh — tất cả luồng thực thi cùng một mã.

Ví dụ về shader

Hãy xem xét các ví dụ thực tế về shader cho phát triển di động — từ tô bóng đơn giản đến tạo kết cấu thủ tục. Mỗi ví dụ minh họa một kỹ thuật cụ thể.

Tô bóng Phong (vertex shader)

Mô hình Phong là mô hình chiếu sáng cổ điển với các thành phần khuếch tán và phản chiếu. Vertex Shader tính toán chiếu sáng cho mỗi đỉnh, Fragment Shader chỉ nội suy kết quả (Gouraud shading). Phù hợp cho thiết bị di động nhờ chi phí thấp.

glsl
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_normal;
uniform mat4 u_mvp;
uniform mat4 u_modelView;
out vec3 v_color;

void main() {
    vec3 normal = normalize(mat3(u_modelView) * a_normal);
    vec3 lightDir = normalize(vec3(0.0, 1.0, 1.0));
    float diff = max(dot(normal, lightDir), 0.0);
    v_color = vec3(0.8, 0.2, 0.2) * (0.3 + diff * 0.7);
    gl_Position = u_mvp * a_position;
}

Shader tính toán chiếu sáng khuếch tán: tích vô hướng của pháp tuyến và hướng ánh sáng. Kết quả được trộn với các thành phần ambient (0.3) và diffuse (0.7). Thực thi 10–15 lệnh ALU trên mỗi đỉnh — chấp nhận được cho 100.000 đa giác ở 60 FPS trên GPU di động.

Kết cấu thủ tục (fragment shader)

Kết cấu thủ tục được tạo trong Fragment Shader mà không cần tải từ tệp. Tiết kiệm bộ nhớ video và đơn giản hóa hoạt ảnh. Ví dụ — bàn cờ với kích thước ô có thể điều chỉnh. Chỉ vài lệnh ALU — tải tối thiểu lên GPU.

glsl
#version 300 es
precision mediump float;
in vec2 v_uv;
uniform float u_cells;
out vec4 fragColor;

void main() {
    vec2 cell = floor(v_uv * u_cells);
    float isWhite = mod(cell.x + cell.y, 2.0);
    fragColor = mix(vec4(0.1, 0.1, 0.1, 1.0),
                     vec4(0.9, 0.9, 0.9, 1.0),
                     isWhite);
}

8 lệnh ALU — shader lý tưởng cho thiết bị di động. u_cells xác định số lượng ô trên mỗi trục. floormod là các thao tác rẻ, thực thi trong 1 chu kỳ trên tất cả GPU di động.

Câu hỏi thường gặp

Shader khác gì so với chương trình C++ thông thường?

Shader chạy trên GPU ở chế độ SIMT — một lệnh cho hàng trăm luồng. Chương trình C++ thông thường chạy trên CPU ở chế độ MIMD. Shader không có quyền truy cập hệ thống tệp, I/O hoặc bộ nhớ động. Nó nhận dữ liệu qua bộ đệm và kết cấu và trả về vị trí hoặc màu sắc.

Nên học ngôn ngữ shader nào cho phát triển di động?

GLSL — cho OpenGL ES và WebGL, đa nền tảng. Metal Shading Language — cho iOS. HLSL — nền tảng cho Unity và Unreal Engine. Bắt đầu với GLSL — hiểu biết nền tảng có thể áp dụng cho bất kỳ ngôn ngữ shader nào.

Tại sao shader chậm trên thiết bị di động?

Nguyên nhân chính: độ chính xác cao (highp thay vì mediump), lấy mẫu kết cấu quá mức, rẽ nhánh động và vượt quá giới hạn thanh ghi. Sử dụng RenderDoc hoặc Xcode GPU Frame Debugger để phân tích shader.

Compute shader là gì và tại sao cần nó?

Compute Shader là shader cho các tính toán tùy ý trên GPU: vật lý hạt, xử lý ảnh, mô phỏng vải. Không giống Fragment Shader, nó không bị ràng buộc với pixel và có thể ghi vào bộ đệm tùy ý.

Có thể viết shader mà không biết ngôn ngữ lập trình không?

Có, thông qua trình chỉnh sửa trực quan: Shader Graph trong Unity, Material Editor trong Unreal Engine hoặc Metal Shader Converter. Chúng tạo mã shader dựa trên sơ đồ nút. Để tối ưu sâu, cần hiểu ngôn ngữ shader.

Tổng kết

  • Shader là chương trình song song cho GPU xử lý hàng loạt đỉnh, mảnh hoặc dữ liệu tùy ý.
  • Các loại shader: Vertex, Fragment, Compute, Geometry, Tessellation và Mesh — mỗi loại chịu trách nhiệm cho giai đoạn pipeline của nó.
  • Ngôn ngữ: GLSL (OpenGL), HLSL (DirectX), MSL (Metal), SPIR-V (Vulkan) — biên dịch thành mã máy GPU.
  • Đặc thù di động: độ chính xác mediump, lấy mẫu kết cấu tối thiểu, không rẽ nhánh động.
  • Tối ưu hóa: mix() thay vì if, độ chính xác half, ATF, kết hợp kết cấu thành atlas và tính toán trước trên CPU.
  • Ứng dụng: chiếu sáng Phong, kết cấu thủ tục, hậu xử lý, vật lý hạt và hiệu ứng UI.
  • Công cụ: RenderDoc, Xcode GPU Debugger, Qualcomm Adreno Profiler và Mali Offline Compiler để phân tích shader.

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.

Thảo luận dự án

Đọc thêm