Você está na página 1de 25

OPENGL — UTILIZANDO SHADING LANGUAGE, GLFW E GLEW

PROCESSAMENTO GRÁFICO

M
OUAT
R/ 2 0 1
272
Leandro Tonietto
INTRODUÇÃO

• Podemos fazer uso do hardware gráfico para melhorar perfomance


de visualização gráfica.

• Paralelismo de dados == uma instrução para "todos" os dados,


ao mesmo tempo!

• Sistemas gráficos modernos já estão preparados para funcionar


com versões mais recentes da OpenGL.

• Síntese = dados geométricos e materiais + pipeline renderização.

Mar-2022 Prof. Leandro Tonietto !2


CONCEITOS PROGRAMAÇÃO GPU

• SHADERS
• A programação com GLSL requer utilização de shaders.
• O shaders são "pequenos" programas escritos em GLSL para definir
estilo de desenho.
• São definidos em forma de texto, compilados e enviados para
processamento na GPU (Graphics Processing Unit).
• A GPU possui diversos/muitos núcleos de processamento, o que
permite computação de partes de software em paralelo, portanto,
muito mais eficiente do implementação tradicional de software,
que executa na CPU (geralmente, limitada entre 1 — 8 núcleos).
• Um programa de shader requer pelo menos 2 partes: vertex shader
e fragment shader.

Mar-2022 Prof. Leandro Tonietto !3


CONCEITOS PROGRAMAÇÃO GPU
Pipeline Gráfico
[Anton’s 2014]
FIGURA 2 COPIADA DE [1]

Mar-2022 Prof. Leandro Tonietto !4


CONCEITOS PROGRAMAÇÃO GPU

• Pipeline gráfico:
• [programmable] Hardware pipeline (pipeline em
hardware, programável)
• Shaders podem ser construídos para sobrepor diversas
etapas/ações do pipeline.
• Fixed-function pipeline (pipeline em funções
predefinidas ou não programáveis)
• Ações executadas no pipeline já estão definidas e não
podem ser substituídas.
• A GPU está preparada para suportar hardware pipeline!

Mar-2022 Prof. Leandro Tonietto !5


CONCEITOS - PIPELINE
G R Á F I C O P R O G R A M ÁV E L
OpenGL 2 OpenGL 3 e 4
• Vertex shader: executado por vértice.
Informação de entrada mínima é um
vec3 representando um vértice.
• Tessellation control shader: executado
por patch de vértices. Define
parâmetros para estágio fixo tessellator
(subdivide malha poligonal)
• Tessellation evaluation shader: recebe
vértices de resultado do tessellator,
aplica transformações e posiciona no
universo.
• geometry shader: permite a criação de
primitiva a partir de pontos.
• Fragment shader: permite a operação
sobre fragmentos. Por exemplo,
texturização, modelos de iluminação, …

Mar-2022 fonte: Livro GERDELAN, Anton. Anton's OpenGL 4.


Prof. Leandro Tonietto !6
SHADER LANGUAGE

• A linguagem de shader é a GLSL


• É uma linguagem muito parecida/baseada na
linguagem C.
• Cada pequeno programa em GLSL é vinculado com
vértice “atual” para processamento do mesmo.
• Um programa GLSL é uma combinação de shaders.

const char* vertex_shader =


"#version 410\n"
"in vec3 vp;"
"void main () {"
" gl_Position = vec4 (vp, 1.0);"
“}";

Mar-2022 Prof. Leandro Tonietto !7


SHADER LANGUAGE
• Vertex shader
• Responsável por posicionar os vértices nas coordenadas finais.

#version 410 in — significa input, ou seja, vértice de parâmetro


do estágio anterior. Uma variável out será a saída
in vec3 vp;
para um próximo estágio.
void main () {
gl_Position = vec4 (vp, 1.0);
};
gl_Position é uma variável padrão para definir
posição final do vértice dentro área definida

vec3 — para definir posição, direção ou cor


vec4 — idem ao vec3 mais indicador de perspectiva. Onde 1.0
significa não calcular perspectiva

A S V A R I Á V E I S D E E N T R A D A S Ã O U T I L I Z A D A S “ P O R - V É R T I C E ” . P O R TA N T O , O S H A D E R É A S S O C I A D O C O M
C A D A V É R T I C E P R E S E N T E N O V E R T E X B U F F E R ( Á R E A D E M E M Ó R I A PA R A V É R T I C E S N O H A R D W A R E G R Á F I C O ) .
Mar-2022 Prof. Leandro Tonietto !8
SHADER LANGUAGE

• Fragment shader
• Após execução de vertex shader para cada vértice, começa a
etapa de processamento de fragmentos, ou seja, pedaço de
polígono do tamanho de um pixel executa um fragment
shader.
• Também um miniprograma em C com características próprias.

Mar-2022 Prof. Leandro Tonietto !9


SHADER LANGUAGE

• Fragment shader

#version 410
uniform — indica variável global (acessível
uniform vec4 inColor; em todo programa), proveniente da CPU.
out vec4 fragColor;
out — primeira variável do tipo out é
void main () { interpretada como a variável a ser utilizada
fragColor = inColor; para receber uma cor RGBA (4 floats com
}; valore entre 0 e 1)

inColor está vindo da CPU como parâmetro e passada para o


fragmento atual através da variável fragColor.

C O R E S R G B A VA R I A M D E 0 . 0 A 1 . 0 ( N Ã O D E 0 A 2 5 5 )

Mar-2022 Prof. Leandro Tonietto !10


BIBLIOTECAS AUXILIARES
• É importante fazer uso de bibliotecas auxiliares (extensões) que não
interfiram muito na programação. Ou seja, quanto mais simples melhor
para aprender.
• GLEW:
• Extensões e utilitários para OpenGL. Também com include apropriado
para versões mais recentes da OpenGL.
• http://glew.sourceforge.net
• GLFW:
• Biblioteca que realiza a interface com SO
• http://www.glfw.org

// incluir GLEW antes da GLFW para usar versões mais recentes da OpenGL
#include <GL/glew.h> /* include GLEW and new version of GL on Windows */
#include <GLFW/glfw3.h>

Mar-2022 Prof. Leandro Tonietto !11


VERSÃO OPENGL
• Teste de versão / suporte da placa de vídeo:
int main () {
if (!glfwInit ()) {
fprintf (stderr, "ERROR: could not start GLFW3\n");
return 1;
}
FORÇA A VERSÃO MÍNIMA
/* Caso necessário, definições específicas para SOs, p. e. Apple OSX *
/* Definir como 3.2 para Apple OS X */ D E S U P O R T E PA R A 3 . 2 E
/*glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3); MANTÉM
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 2); C O M PAT I B I L I D A D E
glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); APENAS COM VERSÕES
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);*/
MAIS RECENTES E NÃO
GLFWwindow *window = glfwCreateWindow ( CONSIDERA
640, 480, "Teste de versão OpenGL", NULL, NULL); FUNCIONALIDADES
if (!window) { D E P R E C AT E D
fprintf (stderr, "ERROR: could not open window with GLFW3\n");
glfwTerminate();
return 1;
}
glfwMakeContextCurrent (window);
// inicia manipulador da extensão GLEW
glewExperimental = GL_TRUE;
glewInit ();

// obtenção de versão suportada da OpenGL e renderizador


const GLubyte* renderer = glGetString (GL_RENDERER);
const GLubyte* version = glGetString (GL_VERSION);
printf ("Renderer: %s\n", renderer);
printf ("OpenGL (versão suportada) %s\n", version);

// encerra contexto GL e outros recursos da GLFW


glfwTerminate(); COPIE DO PLANO DE ENSINO
return 0;
} O CÓDIGO-FONTE DE TESTE
DE VERSÃO
Mar-2022 Prof. Leandro Tonietto !12
COMPILAÇÃO MANUAL
• Podemos fazer instalação das libs e includes em ambientes de desenvolvimento, como
Visual Studio, por exemplo. Ou
• Fazer compilação manual, linkando as libs dependentes:

• Windows (com MinGWI):


• g++ arq.exe main.cpp libglew32.dll.a glfw3dll.a -I include -
lOpenGL32 -L ./ -lglew32 -lglfw3 -lm
• Linux
• g++ -o arq main.cpp libGLEW.a libglfw3.a -I include -lGL -X11
-lXxf86vm -lXrandr -lpthread -lXi -lm
• Apple OS X
• g++ -framework Cocoa -framework OpenGL -framework IOKit -o arq
main.c -I include -I/sw/include -I/usr/local/include -I
include libGLEW.a libglfw3.a

// incluir GLEW antes da GLFW para usar versões mais recentes da OpenGL
#include <GL/glew.h> /* include GLEW and new version of GL on Windows */
#include <GLFW/glfw3.h>
É INTERESSANTE DEIXAR LIBS NUM
Prof. Leandro Tonietto DIRETÓRIO COMUM DE BIBLIOTECAS !13
Mar-2022
PROGRAMAÇÃO OPENGLSL

• VBO - VERTEX BUFFER OBJECT


• Representação dos dados em memória é através de
VBO.
• Mais eficiente do que passar um vértice por vez é
passar todos de uma só vez! Um vértice a cada 3
floats.

GLfloat points[] = {0.0f,0.5f,0.0f,0.5f,-0.5f,0.0f,-0.5f,-0.5f,0.0f};


GLuint vbo = 0;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

A O P E N G L T R ATA V B O C O M O U M O B J E T O O G A R G A L O D O D E S E N H O E S TÁ N A C O M U N I C A Ç Ã O
IDENTIFICADO NA MEMÓRIA. TEMOS, ENTRE CPU E GPU. LOGO, O IDEAL É MANTER E
P O R TA N T O , U M A I D E N T I F I C A Ç Ã O Q U E V I N C U L A PA S S A R A M A I O R Q U A N T I D A D E P O S S Í V E L D E
C O M T O D O S O S V É R T I C E S D O A R R AY. TA M B É M É D A D O S PA R A A G P U
Mar-2022 U T I L I Z A D O PA R A N O R M A I S E T E X T U R A . Prof. Leandro Tonietto !14
PROGRAMAÇÃO OPENGLSL

• VBO - VERTEX BUFFER OBJECT


• Além de coordenadas, também é possível representar diversas
informações para vértices, como: normais e mapeamento de
texturas.
• No entanto, é necessário ter um relacionamento 1:1 entre os tipos
de informação: para cada vértice, uma normal e um mapeamento
de textura tem que ser passado.

GLfloat colors[]={1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
GLuint colorsVBO = 0;
glGenBuffers (1, &colorsVBO);
glBindBuffer (GL_ARRAY_BUFFER, colorsVBO);
glBufferData (GL_ARRAY_BUFFER, 9*sizeof (GLfloat), colors, GL_STATIC_DRAW);

O S S H A D E R S S Ã O E X E C U TA D O S E M PA R A L E L O , P O R TA N T O P O R T I P O
DE INFORMAÇÃO DE CADA VÉRTICE, POR ISTO É NECESSÁRIO QUE SE
T E N H A M E S M A C O N F I G U R A Ç Ã O PA R A T O D O S V É R T I C E S .
Mar-2022 Prof. Leandro Tonietto !15
PROGRAMAÇÃO OPENGLSL
• VAO - VERTEX ARRAY OBJECT
• Representa um conjunto de definições de atributos para vértice.
Funciona como uma memória (buffer) para mantermos
localização e demais atributos dos vértices.

PA R TA O U M O B J E T O O U G R U P O D E F A C E S , P O D E M O S T E R U M V B O PA R A
V É R T I C E S , O U T R O PA R A C O R E S ( O U T E X T U R A ) E O U T R O PA R A N O R M A I S .
Mar-2022 Prof. Leandro Tonietto !16
PROGRAMAÇÃO OPENGLSL
• VAO - VERTEX ARRAY OBJECT
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); // identifica vbo atual
// habilitado primeiro atributo do vbo (bind atual)
glEnableVertexAttribArray(0);
// associação do vbo atual com primeiro atributo
// 0 identifica que o primeiro atributo está sendo definido
// 3, GL_FLOAT identifica que dados são vec3 e estão a cada 3 float.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

// é possível associar outros atributos, como normais, mapeamento e cores


// lembre-se: um por vértice!
glBindBuffer(GL_ARRAY_BUFFER, colorsVBO);
// habilitado segundo atributo do vbo bound atual
glEnableVertexAttribArray(1);
// note que agora o atributo 1 está definido
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);

Mar-2022 Prof. Leandro Tonietto !17


PROGRAMAÇÃO OPENGLSL

• Vertex Shaders
• Dados do programa
const char* vertex_shader =
"#version 410\n"
"layout(location=0) in vec3 vp;" vp: um vértice do VBO de vértices
"layout(location=1) in vec3 vc;" vc : um vértice de cor do VBO de cores
"out vec3 color;"
"void main () {" Associação manual de atributos
" color = vc;" com localizações (layout).
" gl_Position = vec4 (vp, 1.0);" A definição out vec3 color
"}";
indica que este parâmetro será
// in - definição de variável de entrada. passado para o próximo estágio.
// out - definição de variável de saída
// layout - vai associar cada variável com um respectivo atributo do VAO
// vec3 - a cada 3 valores um vértice a partir do VAO…
// gl_Position - variável de saída cujo nome é reservado e espera por vec4.
// vec4(vp, 1.0) - um vértice 4D. O 1.0 indica “não calcular perspectiva"

Mar-2022 Prof. Leandro Tonietto !18


PROGRAMAÇÃO OPENGLSL

• Fragment Shaders
• Definições do programa.

const char* fragment_shader =


"#version 410\n" Variável in recebe parâmetro do
"in vec3 color;" estágio anterior, no caso, do vertex
"out vec4 frag_color;"
"void main () {" shader.
" frag_color = vec4 (color, 1.0);" Cada fragmento receberá uma cor
“}"; e passará esta informação para o
próximo estágio.
// out - variável de saída/escrita
// novamente cuidar a versão mais alta adequada para execução:
// trocar 410 por 330 ou 150, se for o caso.

Mar-2022 Prof. Leandro Tonietto !19


PROGRAMAÇÃO OPENGLSL
• Shader programm
• Para utilização de renderização com shaders, precisamos
montar as partes (shaders) compiladas e ligadas num programa
(shader program) para OpenGL.
// identifica vs e o associa com vertex_shader
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertex_shader, NULL);
glCompileShader(vs);

// identifica fs e o associa com fragment_shader


GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragment_shader, NULL);
glCompileShader(fs);

// identifica do programa, adiciona partes e faz "linkagem"


GLuint shader_programme = glCreateProgram ();
glAttachShader(shader_programme, fs);
glAttachShader(shader_programme, vs);
glLinkProgram(shader_programme);

Mar-2022 Prof. Leandro Tonietto !20


PROGRAMAÇÃO OPENGLSL

• Depois é necessário passar o shader programme ao


OpenGL.
// Define shader_programme como o shader a ser utilizado
glUseProgram (shader_programme);

while (!glfwWindowShouldClose (window)) {


glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Define vao como verte array atual


glBindVertexArray (vao);

// desenha pontos a partir do p0 e 3 no total do VAO atual com o shader


// atualmente em uso
glDrawArrays (GL_TRIANGLES, 0, 3);

glfwSwapBuffers (window);
}

glDrawArrays(<mode>, <first>, <count>)


mode: tipo de primitiva a desenhar: GL_POINTS, GL_LINES, GL_TRIANGLES…
f i r s t : í n d i c e i n i c i a l d o VA O a t i v o
count: número de índices a serem renderizados

Mar-2022 Prof. Leandro Tonietto !21


PROGRAMAÇÃO OPENGLSL

• Analisando o exemplo para entender o processo:


• O vertex shader será associado com cada vértice do objeto 0,0
desenhado, portanto, 3 associações e, consequentemente,
apenas 3 saídas para o próximo estágio.
• O fragment shader será associado com cada pedaço de
desenho de tamanho de um pixel, portanto, muitas
associações.
• Para os fragmentos que não tem associação direta com os
vértices os valores passados são interpolações de cores. Por
exemplo:
• Um fragmento posicionado exatamente no canto superior do
triangulo será completamente vermelho:
(1.0, 0.0, 0.0)
800,600
• Um fragmento posicionado exatamente no meio dos vértices azul e vermelho será roxo (metade azul,
metade vermelho, sem verde): (0.5, 0.0, 0.5)
• Um fragmento posicionado exatamente no centro do triângulo terá uma mistura igual das 3 cores (0.3333,
0.3333, 0.3333)

!22
PROGRAMAÇÃO OPENGLSL

• Aplicar multiplicação de matrizes:


// …matriz translação column-order
float matrix[] = {
1.0f, 0.0f, 0.0f, 0.0f, // 1ª coluna
0.0f, 1.0f, 0.0f, 0.0f, // 2ª coluna
0.0f, 0.0f, 1.0f, 0.0f, // 3ª coluna
0.25f, 0.25f, 0.0f, 1.0f // 4ª coluna
};
// … alteração do shader para receber matriz e
// efetuar cálculo de multiplicação de matrizes
const char* vertex_shader =
"#version 410\n"
"layout(location=0) in vec3 vp;"
"layout(location=1) in vec3 vc;"
"uniform mat4 matrix;" Translação de 0.25 em cada eixo.
"out vec3 color;"
"void main () {" TENTE OUTRAS
" color = vc;" TRANSFORMAÇÕES, COMO
" gl_Position = matrix * vec4 (vp, 1.0);" R O TA Ç Ã O E E S C A L A
“}";
// … mais adiante no código, antes do main-loop
glUseProgram (shader_programme);
int matrixLocation = glGetUniformLocation(shader_programme, "matrix");
glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, matrix);

!23
PROGRAMAÇÃO OPENGLSL

• Exemplo com transformação em loop:


// …alterando o exemplo anterior…
float speed = 1.0f;
float lastPosition = 0.0f;
while (!glfwWindowShouldClose (window)) {

static double previousSeconds = glfwGetTime();


double currentSeconds = glfwGetTime();
double elapsedSeconds =
currentSeconds - previousSeconds;
previousSeconds = currentSeconds;
if(fabs(lastPosition) > 1.0f){
speed = -speed;
}
Movimentação do triângulo da
matrix[12] = elapsedSeconds * speed +
lastPosition; direita para esquerda até chegar a
lastPosition = matrix[12]; borda; inverte o movimento até a
outra borda.
glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, matrix);
// … continuam as definições anteriores…
// glDrawArrays…
}

!24
RECURSOS ADICIONAIS

• Este material utiliza como referência o ebook:


http://antongerdelan.net/opengl/

• http://antongerdelan.net/teaching/3dprog1/maths_cheat_sheet.pdf
• Biblioteca de funções:
• http://antongerdelan.net/opengl/maths_funcs.h
• http://antongerdelan.net/opengl/maths_funcs.cpp
• https://www.opengl.org/sdk/docs/
• PDF com guia de referência para OpenGL 4
• Khronos group:
• https://www.khronos.org/opengl/wiki/
• Learn OpenGL:
• https://learnopengl.com

Mar-2022 Prof. Leandro Tonietto !25

Você também pode gostar