Prof. Rodrigo Luis de Souza da Silva, D.Sc.
Programação com Shaders
Introdução
Introdução
● Core-profile vs Immediate Mode
● O que são Shaders
● GLSL
● Tipos de Shaders
● GPU
● Compilação dos Shaders
● Utilizando Shaders
● Bibliotecas Auxiliares
Core-profile vs Immediate Mode
● Ao programarmos em OpenGL podemos utilizar
dois modos: Modo Imediato ou Core-Profile.
● O modo imediato (immediate mode), também é
conhecido como pipeline de funções fixas (fixed
function pipeline).
● Vantagens do modo imediato:
○ Mais fácil de usar e entender (mais didático);
○ Funciona em virtualmente qualquer placa de vídeo (inclusive as integradas deste laboratório)
● Desvantagens:
○ Pouco flexibilidade para desenvolvedores;
Core-profile vs Immediate Mode
● A versão "moderna" do OpenGL é chamada de
Core-profile e a partir da versão 3.2 foi
formalmente dividida da versão de compatibilidade (compatibility-profile).
● Vários tutoriais utilizam a versão 3.3, pois as
versões mais modernas basicamente acrescentam novas funcionalidades utilizando o mesmo
Core-profile vs Immediate Mode
● Core-profile - Vantagens:
○ Mais flexível e eficiente;
○ Propicia um melhor entendimento da programação gráfica.
● Desvantagens:
○ Mais difícil de aprender;
Pipelines
OpenGL 1.x Fixed-Function Pipeline - Nós em azul representam estágios ainda usados nas versões mais novas do OpenGL. Nós em verde representam estágios que foram
Pipelines
OpenGL 2.0 - Adição dos dois primeiros e mais importantes estágios programáveis - Processador de Vértices (Vertex Shader) e processador de fragmentos (Fragment Shader).
Pipelines
O que são Shaders?
● Shaders são programas que originalmente foram
usados para fazer sombreamentos (shading, daí o
nome) em imagens, mas que atualmente são utilizados para fazer praticamente de tudo em Computação Gráfica (e em alguns casos em computação geral).
● São processados nas GPUs (Graphics Processing Unit).
● São desenvolvidos com linguagens especiais
chamadas Shadings Languages.
GLSL
● Para OpenGL, a linguagem desenvolvida pelos
próprios criadores chama-se GLSL (OpenGL
Shading Language).
● É uma linguagem de alto nível com sintaxe
baseada em C.
● Sua principal função é fornecer acesso direto à
GLSL - Versões
● Foi introduzido como uma extensão no OpenGL 1.4.
● Sua versão inicial, a GLSL 1.10, é de 2004, fazendo
parte da especificação do OpenGL 2.0.
● A partir de 2010, as versões do OpenGL e da GLSL
passaram a ter a mesma numeração (versão 3.3).
Tipos de Shaders
Shaders 2D
● Fragment Shaders
Shaders 3D
● Vertex Shaders
● Geometry Shaders
● Tessellation Shaders
Tipos de Shaders
Shaders 2D
● Fragment Shaders
Shaders 3D
● Vertex Shaders
● Geometry Shaders
● Tessellation Shaders
● Compute Shaders
Tipos de Shaders
Shaders 2D
● Fragment Shaders → também conhecidos como
Pixel Shaders, são responsáveis pela forma como a cena será apresentada, calculando cor, luz,
texturas etc em cada fragmento. Não leva em consideração informações geométricas na cena.
Shaders 3D
● Vertex Shaders → o mais conhecido tipo de
GPU - Características
● As GPUs (Graphics Processing Unit) são usadas
para o processamento dos shaders
● Possuem normalmente mais núcleos de
processamento que as CPUs, porém são núcleos mais especializados.
● Possuem arquitetura massivamente paralela e
poder cálculo muito superior à de um processador central, mas focado em problemas paralelizáveis a nível de dados, como multiplicação de matrizes.
GPU - Características
● Os núcleos de processamento (processing cores),
também conhecidos de forma geral como Shader
Processing Unit (SPU), Stream Processors (AMD) ou CUDA* Cores (Nvidia), são a parte mais
importante das GPUs.
● Placas modernas podem conter milhares de núcleos
de processamento (veja link abaixo) que podem ser utilizados para processamento gráfico (principal uso)
ou para computação de propósito geral (GPGPU -
General-Purpose Graphics Processing Unit).
Compilação dos Shaders (GLSL)
● Shaders precisam ser compilados para gerar
programas processados e executáveis (program
objects).
● Processo de utilização dos shaders é semelhante
ao processamento de códigos em C/C++, isto é:
○ Inicialmente o código fonte dos shaders são
processados, gerando arquivos-objeto (shaders objects)
○ Para gerar códigos executáveis, um ou mais
Co
mp
ilação
d
os
Sh
ad
ers
- P
ip
el
in
e
Compilação dos Shaders - Funções
● Shaders em OpenGL são compilados através do uso de um conjunto de funções.
● glCreateShader - inicialmente devemos criar um shader object vazio com a função abaixo:
GLuint glCreateShader(GLenum shaderType);
● O shader deve ser de um dos tipos abaixo:
○ GL_VERTEX_SHADER;
○ GL_FRAGMENT_SHADER;
○ GL_GEOMETRY_SHADER;
○ GL_COMPUTE_SHADER;
○ GL_TESS_CONTROL_SHADER;
Compilação dos Shaders - Funções
● glShaderSource - função onde passaremos a string
que representa o código do shader
Gvoid glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
● glCompileShader - uma vez preenchido, o shader object está pronto para ser compilado
void glCompileShader(GLuint shader);
● Pode-se, opcionalmente, utilizar os comandos
glGetShaderiv e glGetShaderInfoLog para recuperar
Compilação dos Shaders - Funções
● Uma vez compilado os shaders objects de
interesse, eles estarão prontos para serem vinculados ao programa.
● glCreateProgram - Cria um programa retornando
seu id.
GLuint glCreateProgram();
● glAttachShader - Função usada para incluir os
shaders objects compilados ao programa criado.
Compilação dos Shaders - Funções
● glLinkProgram - vincula o programa criado.
Esta é a última etapa na preparação dos shaders.
○ Se algum vertex shader tiver sido anexado ao programa, ele será utilizado para criar um executável responsável pelo processador de vértices programável;
○ Se algum fragment shader tiver sido anexado ao
programa, ele será utilizado para criar um executável responsável pelo processador de fragmentos
programável.
○ O mesmo comportamento ocorre com os demais tipos de
Variáveis e
Shaders
● Shaders são compilados e executados de forma independente.
● Existem mecanismos para passar variáveis para
os shaders a partir do programa principal.
● Existem outros mecanismos para que uma mesma
variável possa ser acessada por shaders
diferentes
Removendo Shaders - Funções
● Após a criação do programa usando os shader
objects, os mesmos podem ser desvinculados e, se não forem utilizados posteriormente por outro
programa, removidos.
● glDetachShader - Desvincula um shader object de
um programa
Gvoid glDetachShader(GLuint program, GLuint shader);
● glDeleteShader - Remove um shader object
Utilizando Shaders - Funções
● Após criação, compilação e vinculação dos
shaders na pipeline gráfica, usa-se o comando
glUseProgram para ativar ou desativar o shader.
● glUseProgram - Instala o programa vinculado
através de seu identificador como parte do estado de renderização corrente.
void glUseProgram(GLuint program);
● Para remover o programa do estado corrente,
basta passar o argumento 0 (zero) como identicador:
Utilizando Shaders
- Passo-a-passo
● Normalmente os passos necessários para
compilarmos, vincularmos e utilizarmos shaders podem ser complicados.
● Em muitos sistemas e tutoriais, pequenas classes
são criadas para auxiliar esse tipo de tarefa.
● Vamos ver nos próximos slides um exemplo
simplificado (sem controle de erros e limpeza dos
Utilizando Shaders
-
glcShader.h
class glcShader
{
public:
~glcShader();
glcShader();
glcShader(string vs, string fs);
void Use(int use = 1);
int GetProgram();
private:
GLuint programHandler;
char* ReadShaderFile(string);
void InstallShaders(string vs, string fs);
Utilizando Shaders
-
glcShader.cpp
void glcShader::InstallShaders(string vs_in, string fs_in)
{
const char *VertexCodeString = ReadShaderFile(vs_in); const char *FragmentCodeString = ReadShaderFile(fs_in);
GLuint VS, FS; // handles to objects
// Create a vertex shader object and a fragment shader object
VS = glCreateShader(GL_VERTEX_SHADER);
FS = glCreateShader(GL_FRAGMENT_SHADER);
// Load source code strings into shaders
glShaderSource(VS, 1, &VertexCodeString, NULL);
glShaderSource(FS, 1, &FragmentCodeString, NULL);
// Compile shaders
glCompileShader(VS);
glCompileShader(FS);
this->programHandler = glCreateProgram();
glAttachShader(this->programHandler, VS);
glAttachShader(this->programHandler, FS);
glLinkProgram(this->programHandler);
Utilizando Shaders
-
glcShader.cpp
void glcShader::Use(int use)
{
if(use)
glUseProgram(this->programHandler);
else
Utilizando Shaders
-
main.cpp
// No seu código, você deve criar um objeto da // classe 'shader', passar os códigos fontes // dos shaders e aplicá-lo
// Shader object pointer glcShader *shader = NULL;
...
// Create shader object
shader = new glcShader("../shaders/nomeShader.vert", "../shaders/nomeShader.frag");
...
// Use Shader object shader->Use();
Bibliotecas auxiliares
● Algumas bibliotecas auxiliares são comumente utilizadas para programação com shaders.
● Principais:
○ GLEW (libglm-dev) → OpenGL Extension Wrangler Library,
possui mecanismos para verificar quais extensões e
funcionalidades do OpenGL são suportadas na plataforma que está sendo executada.
○ GLM (libglm-dev) → OpenGL Mathematics fornece inúmeras classes e funções projetadas com a mesma nomenclatura da GLSL. É especialmente útil na manipulação de matrizes,
quaternions, número randômicos etc.
Bibliotecas auxiliares
● Outras:
○ SOIL (libsoil-dev) → Simple OpenGL Image Library é uma biblioteca útil para gerenciar o carregamento de texturas em OpenGL. Permite a leitura de arquivos com extensão BMP, PNG, JPG, HDR entre outros.
Tutoriais disponíveis
● Existem inúmeros tutoriais disponíveis na internet para
auxiliar no estudo de OpenGL com shaders. Dois que destaco:
● OpenGL Tutorial (GLSL Version 3.30)
Livros
Learn OpenGL
Prática
● Execute o projeto "Shader Basic Example" e veja a saída;
● Analise o código e:
○ Identifique a criação e alocação do objeto da classe
glcShader;
○ Abra os arquivos simple.vert e simple.frag e
faça uma breve análise