Shader (shader) é um programa executado na GPU para processar dados gráficos: vértices, fragmentos ou tarefas computacionais. Ao contrário do código comum na CPU, o shader funciona em paralelo em centenas de núcleos, processando cada elemento independentemente. Segundo a Khronos Group (2025), shaders são usados em 100% dos aplicativos 3D modernos — desde jogos mobile até efeitos de UI em aplicativos do sistema. Desenvolvedores escrevem shaders em linguagens especializadas: GLSL, HLSL, Metal Shading Language ou SPIR-V.
Principais pontos
Shader é um pequeno programa escrito em uma linguagem especializada e executado na GPU. Cada shader processa um elemento de dados — vértice, fragmento (pixel) ou elemento de trabalho — independentemente dos outros. O paralelismo da GPU permite executar milhares de instâncias de shader simultaneamente.
O termo foi introduzido pela Pixar nos anos 1980 para descrever programas que controlam a aparência de superfícies no RenderMan. Em tempo real, shaders surgiram com a NVIDIA GeForce 3 (2001) — a primeira placa de vídeo com pipeline programável. Antes dos shaders, os gráficos eram configurados por parâmetros fixos: cor, textura, modo de mesclagem.
Segundo a Unity Technologies (2025), um jogo mobile médio usa 50–200 shaders. Cada shader possui variantes (variants) para diferentes plataformas, níveis de qualidade e configurações de iluminação. O número de variantes pode chegar a 10.000 por projeto.
A arquitetura de um shader divide-se em dados de entrada (atributos, variáveis uniform, texturas), código do programa e dados de saída (cor, posição). Variáveis uniform são definidas pela CPU antes da execução do shader e permanecem inalteradas para todos os elementos do quadro. Atributos são dados por elemento (coordenadas do vértice, normal, coordenadas UV).
SIMT (Single Instruction, Multiple Threads) é o modelo de execução de shaders na GPU. Uma instrução é carregada uma vez e executada por centenas de threads sobre dados diferentes. Por exemplo, um Fragment Shader para 1000 pixels executa 1000 threads com o mesmo código, mas cada thread recebe suas próprias coordenadas UV e atributos interpolados.
Uma consequência importante do SIMT: ramificações em shaders são custosas. Se dentro de um bloco if metade dos threads segue um caminho e a outra metade outro, a GPU executa ambos os caminhos sequencialmente para todos os threads. O desempenho cai para 50%. Evite ramificações dinâmicas em shaders ou minimize-as por meio de cálculos prévios na CPU.
GPUs modernas suportam vários tipos de shaders, cada um para seu estágio do pipeline gráfico ou computacional. Vamos considerar os principais tipos: vertex, fragment, compute e os modernos mesh shaders.
| Tipo de shader | Estágio do pipeline | Processa | Linguagem |
|---|---|---|---|
| Vertex Shader | Vertex | Cada vértice | GLSL, HLSL |
| Fragment Shader | Fragmento | Cada pixel | GLSL, HLSL |
| Compute Shader | Computação | Dados arbitrários | GLSL, HLSL |
| Geometry Shader | Geometria | Primitivas | GLSL, HLSL |
| Tessellation Shader | Tesselação | Patches | GLSL, HLSL |
| Mesh Shader | Pipeline mesh | Task + Mesh | HLSL, MSL |
Vertex Shader processa cada vértice da geometria: transforma coordenadas, calcula iluminação, passa dados para o fragment shader. É um estágio obrigatório do pipeline gráfico — sem ele, os vértices não chegam à tela. O Vertex Shader é chamado para cada vértice de cada quadro.
Fragment Shader (ou Pixel Shader) é o estágio que mais consome recursos. Ele calcula a cor final de cada fragmento usando texturas, iluminação e material. O Fragment Shader é executado para cada pixel coberto pela geometria, considerando todas as texturas e efeitos aplicados. A otimização deste shader proporciona o maior ganho de desempenho.
Compute Shader é um shader universal para cálculos arbitrários na GPU. Ao contrário dos shaders gráficos, ele não está vinculado à geometria ou pixels. O Compute Shader trabalha com buffers de dados arbitrários por meio de grupos de trabalho. É usado para física, simulação de partículas, pós-efeitos e inferência de redes neurais.
Mesh Shader é o tipo mais recente de shader, surgido na NVIDIA Turing (2018) e Metal 3. O Mesh Shader substitui os shaders vertex, geometry e tessellation por um único pipeline mesh. Funciona em conjunto com o Task Shader: o Task Shader decide quais grupos mesh renderizar, e o Mesh Shader gera a geometria em tempo real.
Shaders são escritos em linguagens especializadas que são compiladas para código de máquina da GPU. A escolha da linguagem depende da plataforma e da API. Vamos considerar as principais linguagens: GLSL, HLSL, Metal Shading Language e o formato intermediário SPIR-V.
GLSL é a linguagem de shaders para OpenGL, OpenGL ES e WebGL. A sintaxe é baseada em C com adição de tipos vetoriais (vec2, vec4, mat4) e funções embutidas (texture, normalize, reflect). GLSL ES é a versão para dispositivos mobile com precisão limitada (lowp, mediump, highp). Segundo a Khronos (2025), GLSL continua sendo a linguagem de shaders mais difundida devido à sua natureza multiplataforma.
#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);
}
Um fragment shader simples lê a cor da textura pelas coordenadas UV. precision mediump instrui a GPU a usar meia precisão para float — acelera a execução em dispositivos mobile em 25–40%.
HLSL é a linguagem de shaders da Microsoft para DirectX. A sintaxe é mais próxima de C++ com suporte a classes, estruturas e templates. HLSL é usado em aplicativos Windows e, através do Shader Model 6.7+, suporta Ray Tracing, Mesh Shaders e Sampler Feedback. Para desenvolvimento mobile, HLSL não é usado diretamente, mas é compilado para SPIR-V para Vulkan.
MSL (Metal Shading Language) é a linguagem de shaders da Apple, baseada em C++14. Ao contrário de GLSL e HLSL, o MSL é compilado junto com o aplicativo, eliminando a compilação JIT no dispositivo. MSL suporta ponteiros, templates e a biblioteca padrão C++. É usado em todos os dispositivos Apple: iPhone, iPad, Mac, Apple TV.
#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 usa atributos [[position]], [[stage_in]] e [[texture(N)]] para vinculação de recursos. O compilador da Apple gera código otimizado para a GPU atual, considerando o número de registradores e cache.
SPIR-V é um formato binário intermediário de shaders, padrão para Vulkan. Shaders são escritos em GLSL ou HLSL, compilados para SPIR-V e carregados no aplicativo Vulkan. SPIR-V não está vinculado a uma linguagem específica — existem compiladores de Rust, Python, OpenCL C para SPIR-V.
Shaders em plataformas mobile têm limitações em comparação com desktop: menos registradores, precisão limitada e falta de suporte para algumas instruções. Vamos considerar as especificidades dos shaders no Android (Vulkan/OpenGL ES) e iOS (Metal).
Android usa GLSL ES para OpenGL e SPIR-V para Vulkan. OpenGL ES 3.2 suporta shaders com mediump por padrão. Vulkan requer compilação explícita de GLSL para SPIR-V via glslangValidator. No Android, shaders são carregados de recursos de string ou de arquivos SPIR-V compilados.
Qualcomm Adreno otimiza shaders no nível do driver. Recomendações: use mediump para cor e UV, highp apenas para posições; evite amostragens de textura em vertex shaders; agrupe cálculos em vetores (vec4 em vez de 4x float). Segundo a Qualcomm (2025), essas otimizações proporcionam ganho de 30–50%.
iOS usa exclusivamente Metal Shading Language. Os shaders são compilados para código de máquina junto com o aplicativo via Xcode. Metal fornece Shader Debugger e GPU Capture para perfilamento de shaders. A GPU da Apple (TBDR) tem otimizações específicas: early fragment test, render targets sem memória e blending programável.
Segundo a Apple WWDC 2024, para shaders iOS é crítico usar half em vez de float, limitar a pressão de registradores (máx 64 registradores) e evitar amostragens de textura dependentes. Shaders iOS devem ser compactos — tamanho ideal de 50–100 instruções ALU.
Os motores de jogo Unity e Unreal Engine fornecem editores visuais de shaders (Shader Graph, Material Editor) e linguagens abstratas (ShaderLab, USF). O desenvolvedor escreve o shader em uma linguagem de alto nível, e o motor compila para a plataforma alvo. Unity usa HLSL como linguagem intermediária, Unreal usa USF (Unreal Shader Format).
Otimização de shaders é uma etapa fundamental no desenvolvimento de gráficos para dispositivos mobile. Um shader mal escrito pode reduzir o FPS de 60 para 20. Vamos considerar as principais regras e técnicas de otimização.
Use a precisão mínima suficiente: lowp para cor e UV, mediump para normais e iluminação, highp apenas para posições e matrizes. Segundo a ARM (2025), operações mediump são 2 vezes mais rápidas que highp em GPUs Mali. No GLSL ES, isso é definido por qualificadores de precisão.
Amostragens de textura são a operação mais cara em um shader (10–30 ciclos contra 1–2 para uma instrução ALU). Reduza o número de amostragens: combine texturas em atlas, use armazenamento de dados em arrays uniform em vez de texturas, cacheie resultados de pixels vizinhos por meio de instruções de derivada.
Ramificações dinâmicas (if com variável uniform) reduzem o desempenho do pipeline SIMT. Substitua ramificações por funções matemáticas: mix(), step(), smoothstep() e clamp(). Essas funções são executadas em 1–2 instruções, enquanto uma ramificação pode custar 8–16 instruções devido à divergência de threads.
// Ruim: desvio dinâmico no shader
if (u_enableLight) {
color *= computeLighting(normal);
}
// Bom: substituição matemática de desvio
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() realiza interpolação linear entre dois valores. Quando u_enableLight=0, retorna 1.0 — a cor não muda. Quando u_enableLight=1, retorna o resultado da iluminação. Sem ramificação — todos os threads executam o mesmo código.
Vamos considerar exemplos práticos de shaders para desenvolvimento mobile — desde sombreamento simples até geração procedural de texturas. Cada exemplo demonstra uma técnica específica.
Modelo Phong é o modelo clássico de iluminação com componentes difusa e especular. O Vertex Shader calcula a iluminação para cada vértice, e o Fragment Shader apenas interpola o resultado (Gouraud shading). Adequado para dispositivos mobile devido ao baixo custo.
#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;
}
O shader calcula a iluminação difusa: produto escalar da normal com a direção da luz. O resultado é misturado com as componentes ambiente (0.3) e difusa (0.7). Executa 10–15 instruções ALU por vértice — aceitável para 100.000 polígonos a 60 FPS em uma GPU mobile.
Texturas procedurais são geradas no Fragment Shader sem carregamento de arquivo. Economiza memória de vídeo e simplifica a animação. Exemplo — tabuleiro de xadrez com tamanho de célula ajustável. Apenas algumas instruções ALU — carga mínima na GPU.
#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 instruções ALU — shader ideal para dispositivos mobile. u_cells define o número de células por eixo. floor e mod são operações baratas, executadas em 1 ciclo em todas as GPUs mobile.
Perguntas frequentes
Shader é executado na GPU em modo SIMT — uma instrução para centenas de threads. Um programa comum em C++ é executado na CPU em modo MIMD. O shader não tem acesso ao sistema de arquivos, E/S ou memória dinâmica. Ele recebe dados por meio de buffers e texturas e retorna uma posição ou cor.
GLSL — para OpenGL ES e WebGL, multiplataforma. Metal Shading Language — para iOS. HLSL — base para Unity e Unreal Engine. Comece com GLSL — a compreensão dos fundamentos é transferível para qualquer linguagem de shaders.
Principais causas: alta precisão (highp em vez de mediump), amostragens excessivas de textura, ramificações dinâmicas e excedência do limite de registradores. Use RenderDoc ou Xcode GPU Frame Debugger para análise de shaders.
Compute Shader é um shader para cálculos arbitrários na GPU: física de partículas, processamento de imagem, simulação de tecidos. Ao contrário do Fragment Shader, ele não está vinculado a pixels e pode escrever em buffers arbitrários.
Sim, por meio de editores visuais: Shader Graph no Unity, Material Editor no Unreal Engine ou Metal Shader Converter. Eles geram código de shader com base em um esquema de nós. Para otimização profunda, é necessário entender a linguagem de shaders.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também