Shader: co to jest, rodzaje shaderów i jak działają

Autor: IT Sectr Opublikowano: 2026-06-11 Czas czytania: 8 min

Shader (shader) — to program wykonywany na GPU do przetwarzania danych graficznych: wierzchołków, fragmentów lub zadań obliczeniowych. W przeciwieństwie do zwykłego kodu na CPU, shader działa równolegle na setkach rdzeni, przetwarzając każdy element niezależnie. Według danych Khronos Group (2025), shadery są używane w 100% nowoczesnych aplikacji 3D — od gier mobilnych po efekty UI w aplikacjach systemowych. Deweloperzy piszą shadery w wyspecjalizowanych językach: GLSL, HLSL, Metal Shading Language lub SPIR-V.

Najważniejsze

  • Shader — program dla GPU, wykonywany równolegle na setkach rdzeni dla każdego elementu sceny.
  • Rodzaje shaderów: wierzchołkowe, fragmentowe, compute, geometry, tessellation i mesh-shadery.
  • Języki: GLSL (OpenGL), HLSL (DirectX), MSL (Metal), SPIR-V (Vulkan) — wszystkie kompilowane do kodu maszynowego GPU.
  • Shadery w programowaniu mobilnym są używane do efektów, post-processingu, oświetlenia i obliczeń.
  • Optymalizacja shaderów: precyzja mediump, minimalizacja rozgałęzień i ATF (Adaptive Texture Fetch) dla mobilnych GPU.

Co to jest Shader?

Shader — to mały program napisany w wyspecjalizowanym języku i wykonywany na GPU. Każdy shader przetwarza jeden element danych — wierzchołek, fragment (piksel) lub element roboczy — niezależnie od innych. Równoległość GPU pozwala uruchamiać tysiące instancji shadera jednocześnie.

Termin został wprowadzony przez Pixar w latach 80. XX wieku do opisania programów sterujących wyglądem powierzchni w RenderMan. W czasie rzeczywistym shadery pojawiły się z NVIDIA GeForce 3 (2001) — pierwszą kartą graficzną z programowalnym potokiem. Przed shaderami grafikę konfigurowano za pomocą stałych parametrów: kolor, tekstura, tryb mieszania.

Według danych Unity Technologies (2025), przeciętna gra mobilna używa 50–200 shaderów. Każdy shader ma warianty (variants) dla różnych platform, poziomów jakości i konfiguracji oświetlenia. Liczba wariantów może osiągnąć 10 000 na projekt.

Architektura shadera dzieli się na dane wejściowe (atrybuty, zmienne uniform, tekstury), kod programu i dane wyjściowe (kolor, pozycja). Zmienne uniform są ustawiane z CPU przed uruchomieniem shadera i są niezmienne dla wszystkich elementów klatki. Atrybuty to dane na element (współrzędne wierzchołka, normalna, współrzędne UV).

Model wykonania SIMT

SIMT (Single Instruction, Multiple Threads) — model wykonania shaderów na GPU. Jedna instrukcja jest ładowana raz, a wykonywana przez setki wątków na różnych danych. Na przykład Fragment Shader dla 1000 pikseli uruchamia 1000 wątków z tym samym kodem, ale każdy wątek otrzymuje własne współrzędne UV i interpolowane atrybuty.

Ważna konsekwencja SIMT: rozgałęzienia w shaderach są kosztowne. Jeśli wewnątrz bloku if połowa wątków idzie jedną ścieżką, a druga połowa drugą, GPU wykonuje obie ścieżki sekwencyjnie dla wszystkich wątków. Wydajność spada do 50%. Unikaj dynamicznych rozgałęzień w shaderach lub minimalizuj je poprzez wstępne obliczenia na CPU.

Rodzaje shaderów

Nowoczesne GPU obsługują kilka typów shaderów, każdy dla swojego etapu potoku graficznego lub obliczeniowego. Omówmy główne rodzaje: wierzchołkowe, fragmentowe, compute i nowoczesne mesh-shadery.

Typ shaderaEtap potokuPrzetwarzaJęzyk
Vertex ShaderWierzchołkowyKażdy wierzchołekGLSL, HLSL
Fragment ShaderFragmentowyKażdy pikselGLSL, HLSL
Compute ShaderObliczeniowyDowolne daneGLSL, HLSL
Geometry ShaderGeometrycznyPrymitywyGLSL, HLSL
Tessellation ShaderMozaikowaniePłatkiGLSL, HLSL
Mesh ShaderPotok MeshTask + MeshHLSL, MSL

Vertex Shader

Vertex Shader przetwarza każdy wierzchołek geometrii: transformuje współrzędne, oblicza oświetlenie, przekazuje dane do fragment shadera. To obowiązkowy etap potoku graficznego — bez niego wierzchołki nie trafią na ekran. Vertex Shader jest wywoływany dla każdego wierzchołka każdej klatki.

Fragment Shader

Fragment Shader (lub Pixel Shader) — najbardziej zasobożerny etap. Oblicza końcowy kolor każdego fragmentu, używając tekstur, oświetlenia i materiału. Fragment Shader jest wykonywany dla każdego piksela pokrytego geometrią, z uwzględnieniem wszystkich nałożonych tekstur i efektów. Optymalizacja tego shadera daje największy wzrost wydajności.

Compute Shader

Compute Shader — uniwersalny shader do dowolnych obliczeń na GPU. W przeciwieństwie do shaderów graficznych, nie jest związany z geometrią ani pikselami. Compute Shader pracuje z dowolnymi buforami danych poprzez grupy robocze. Używany do fizyki, symulacji cząstek, post-efektów i inferencji sieci neuronowych.

Mesh Shader

Mesh Shader — najnowszy typ shaderów, który pojawił się w NVIDIA Turing (2018) i Metal 3. Mesh Shader zastępuje shadery wierzchołkowy, geometryczny i mozaikowania jednym potokiem mesh. Działa w parze z Task Shader: Task Shader decyduje, które grupy mesh renderować, Mesh Shader generuje geometrię w locie.

Języki do pisania shaderów

Shadery są pisane w wyspecjalizowanych językach, które są kompilowane do kodu maszynowego GPU. Wybór języka zależy od platformy i API. Omówmy główne języki: GLSL, HLSL, Metal Shading Language i format pośredni SPIR-V.

GLSL (OpenGL Shading Language)

GLSL — język shaderów dla OpenGL, OpenGL ES i WebGL. Składnia oparta na C z dodatkiem typów wektorowych (vec2, vec4, mat4) i wbudowanych funkcji (texture, normalize, reflect). GLSL ES — wersja dla urządzeń mobilnych z ograniczoną precyzją (lowp, mediump, highp). Według danych Khronos (2025), GLSL pozostaje najpopularniejszym językiem shaderów dzięki cross-platformowości.

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);
}

Prosty fragment shader odczytuje kolor z tekstury po współrzędnych UV. precision mediump nakazuje GPU używać połowicznej precyzji dla float — przyspiesza wykonanie na urządzeniach mobilnych o 25–40%.

HLSL (High-Level Shader Language)

HLSL — język shaderów od Microsoft dla DirectX. Składnia bliższa C++ z obsługą klas, struktur i szablonów. HLSL jest używany w aplikacjach Windows i przez Shader Model 6.7+ obsługuje Ray Tracing, Mesh Shaders i Sampler Feedback. Dla programowania mobilnego HLSL nie jest używany bezpośrednio, ale jest kompilowany do SPIR-V dla Vulkan.

Metal Shading Language

MSL (Metal Shading Language) — język shaderów Apple, oparty na C++14. W przeciwieństwie do GLSL i HLSL, MSL jest kompilowany razem z aplikacją, co eliminuje kompilację JIT na urządzeniu. MSL obsługuje wskaźniki, szablony i standardową bibliotekę C++. Używany na wszystkich urządzeniach 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 używa atrybutów [[position]], [[stage_in]] i [[texture(N)]] do wiązania zasobów. Kompilator Apple generuje zoptymalizowany kod dla bieżącego GPU, uwzględniając liczbę rejestrów i pamięć podręczną.

SPIR-V

SPIR-V — pośredni binarny format shaderów, standard dla Vulkan. Shadery są pisane w GLSL lub HLSL, kompilowane do SPIR-V i ładowane do aplikacji Vulkan. SPIR-V nie jest związany z konkretnym językiem — istnieją kompilatory z Rust, Python, OpenCL C do SPIR-V.

Shadery w programowaniu mobilnym

Shadery na platformach mobilnych mają ograniczenia w porównaniu do desktopowych: mniej rejestrów, ograniczona precyzja i brak obsługi niektórych instrukcji. Omówmy specyfikę shaderów na Android (Vulkan/OpenGL ES) i iOS (Metal).

Shadery na Android

Android używa GLSL ES dla OpenGL i SPIR-V dla Vulkan. OpenGL ES 3.2 obsługuje shadery z domyślną precyzją mediump. Vulkan wymaga jawnej kompilacji GLSL do SPIR-V przez glslangValidator. Na Android shadery są ładowane z zasobów tekstowych lub skompilowanych plików SPIR-V.

Qualcomm Adreno GPU optymalizuje shadery na poziomie sterownika. Zalecenia: używaj mediump dla koloru i UV, highp tylko dla pozycji; unikaj próbkowania tekstur w shaderach wierzchołkowych; grupuj obliczenia w wektory (vec4 zamiast 4x float). Według danych Qualcomm (2025), te optymalizacje dają wzrost 30–50%.

Shadery na iOS

iOS używa wyłącznie Metal Shading Language. Shadery są kompilowane do kodu maszynowego razem z aplikacją przez Xcode. Metal dostarcza Shader Debugger i GPU Capture do profilowania shaderów. Apple GPU (TBDR) ma specyficzne optymalizacje: early fragment test, memoryless render targets i programowalne mieszanie.

Według danych Apple WWDC 2024, dla shaderów iOS krytyczne jest używanie half zamiast float, ograniczanie presji rejestrowej (max 64 rejestry) i unikanie zależnych próbkowań tekstur. Shadery iOS powinny być kompaktowe — optymalny rozmiar 50–100 instrukcji ALU.

Shadery w Unity i Unreal Engine

Silniki gier Unity i Unreal Engine dostarczają wizualne edytory shaderów (Shader Graph, Material Editor) i języki abstrakcyjne (ShaderLab, USF). Deweloper pisze shader w języku wysokiego poziomu, a silnik kompiluje go dla docelowej platformy. Unity używa HLSL jako języka pośredniego, Unreal — USF (Unreal Shader Format).

Optymalizacja shaderów

Optymalizacja shaderów — kluczowy etap tworzenia grafiki dla urządzeń mobilnych. Źle napisany shader może obniżyć FPS z 60 do 20. Omówmy główne zasady i techniki optymalizacji.

Precyzja obliczeń

Używaj minimalnej wystarczającej precyzji: lowp dla koloru i UV, mediump dla normalnych i oświetlenia, highp tylko dla pozycji i macierzy. Według danych ARM (2025), operacje mediump są 2 razy szybsze niż highp na Mali GPU. W GLSL ES określa się to za pomocą kwalifikatorów precision.

Minimalizacja próbkowań tekstur

Próbkowanie tekstur — najdroższa operacja w shaderze (10–30 cykli vs 1–2 dla instrukcji ALU). Zmniejszaj liczbę próbkowań: łącz tekstury w atlasy, używaj przechowywania danych w tablicach uniform zamiast tekstur, buforuj wyniki sąsiednich pikseli przez instrukcje pochodnych.

Unikaj rozgałęzień

Dynamiczne rozgałęzienia (if ze zmienną uniform) obniżają wydajność potoku SIMT. Zastępuj rozgałęzienia funkcjami matematycznymi: mix(), step(), smoothstep() i clamp(). Te funkcje wykonują się w 1–2 instrukcje, podczas gdy rozgałęzienie może kosztować 8–16 instrukcji z powodu dywergencji wątków.

glsl
// Źle: dynamiczne rozgałęzienie w shaderze
if (u_enableLight) {
    color *= computeLighting(normal);
}

// Dobrze: matematyczne zastąpienie rozgałęzienia
color *= mix(1.0, computeLighting(normal),
              float(u_enableLight));

mix() wykonuje liniową interpolację między dwiema wartościami. Gdy u_enableLight=0, zwracane jest 1.0 — kolor się nie zmienia. Gdy u_enableLight=1, zwracany jest wynik oświetlenia. Brak rozgałęzienia — wszystkie wątki wykonują ten sam kod.

Przykłady shaderów

Omówmy praktyczne przykłady shaderów dla programowania mobilnego — od prostego cieniowania po proceduralne generowanie tekstur. Każdy przykład demonstruje konkretną technikę.

Cieniowanie Phonga (vertex shader)

Model Phonga — klasyczny model oświetlenia z komponentami dyfuzyjnym i spekularnym. Vertex Shader oblicza oświetlenie dla każdego wierzchołka, Fragment Shader tylko interpoluje wynik (Gouraud shading). Odpowiedni dla urządzeń mobilnych ze względu na niski koszt.

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 oblicza oświetlenie diffuse: iloczyn skalarny normalnej i kierunku światła. Wynik jest mieszany z komponentami ambient (0.3) i diffuse (0.7). Wykonuje 10–15 instrukcji ALU na wierzchołek — akceptowalne dla 100 000 wielokątów przy 60 FPS na mobilnym GPU.

Tekstura proceduralna (fragment shader)

Tekstury proceduralne są generowane w Fragment Shader bez ładowania z pliku. Oszczędza pamięć wideo i upraszcza animację. Przykład — szachownica ze zmiennym rozmiarem komórek. Zaledwie kilka instrukcji ALU — minimalne obciążenie 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 instrukcji ALU — idealny shader dla urządzeń mobilnych. u_cells określa liczbę komórek na oś. floor i mod — tanie operacje, wykonywane w 1 cyklu na wszystkich mobilnych GPU.

Często zadawane pytania

Czym różni się shader od zwykłego programu w C++?

Shader jest wykonywany na GPU w trybie SIMT — jedna instrukcja dla setek wątków. Zwykły program w C++ — na CPU w trybie MIMD. Shader nie ma dostępu do systemu plików, wejścia/wyjścia i pamięci dynamicznej. Otrzymuje dane przez bufory i tekstury i zwraca pozycję lub kolor.

Jakiego języka shaderów uczyć się do programowania mobilnego?

GLSL — dla OpenGL ES i WebGL, cross-platformowy. Metal Shading Language — dla iOS. HLSL — podstawa dla Unity i Unreal Engine. Zacznij od GLSL — zrozumienie podstaw przenosi się na każdy język shaderów.

Dlaczego shadery działają wolno na urządzeniach mobilnych?

Główne przyczyny: wysoka precyzja (highp zamiast mediump), nadmierne próbkowanie tekstur, dynamiczne rozgałęzienia i przekroczenie limitu rejestrów. Używaj RenderDoc lub Xcode GPU Frame Debugger do analizy shaderów.

Co to jest compute shader i do czego służy?

Compute Shader — shader do dowolnych obliczeń na GPU: fizyka cząstek, przetwarzanie obrazów, symulacja tkanin. W przeciwieństwie do Fragment Shader, nie jest związany z pikselami i może zapisywać do dowolnych buforów.

Czy można pisać shadery bez znajomości języków programowania?

Tak, poprzez edytory wizualne: Shader Graph w Unity, Material Editor w Unreal Engine lub Metal Shader Converter. Generują one kod shadera na podstawie schematu węzłowego. Do głębokiej optymalizacji potrzebne będzie zrozumienie języka shaderów.

Podsumowanie

  • Shader — równoległy program dla GPU, przetwarzający wierzchołki, fragmenty lub dowolne dane masowo.
  • Typy shaderów: Vertex, Fragment, Compute, Geometry, Tessellation i Mesh — każdy odpowiada za swój etap potoku.
  • Języki: GLSL (OpenGL), HLSL (DirectX), MSL (Metal), SPIR-V (Vulkan) — kompilowane do kodu maszynowego GPU.
  • Specyfika mobilna: precyzja mediump, minimum próbkowań tekstur, brak dynamicznych rozgałęzień.
  • Optymalizacja: mix() zamiast if, precyzja half, ATF, łączenie tekstur w atlasy i wstępne obliczenia na CPU.
  • Zastosowanie: oświetlenie Phonga, tekstury proceduralne, post-efekty, fizyka cząstek i efekty UI.
  • Narzędzia: RenderDoc, Xcode GPU Debugger, Qualcomm Adreno Profiler i Mali Offline Compiler do analizy shaderów.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również