
<br/><br/>
# Fundamentos
Primeiro de tudo, você precisa saber que o **OpenGL** trabalha com dois processos, que são os **vértices** e os **pixels**.
- Os ==vértices== servem pra quando você precisa pegar algum dado de um objeto, como: normais, coordenadas de texturas, etc.
- Pra trabalhar com os vértices, escrevemos nosso código dentro do **Vertex Shader**.
- Os ==pixels== servem pra você desenhar na tela, como os **Shaders**.
- Pra trabalhar com os pixels, escrevemos nosso código dentro do **Fragment Shader**.
```mermaid
graph LR
Vertex-Shader --> Fragment-Shader
```
É basicamente duas seções dentro do código, ficou claro? Dito isto, vamos prosseguir...
> [!Important]
> Não se preocupe com items novos que forem surgindo, tudo será explicado à você no tempo certo, e por favor, não pule etapas se você é um iniciante. O conhecimento te leva longe e aprender exige paciência!
<br/><br/>
## Tipos Vetoriais
No **GLSL** (a linguagem de shaders do OpenGL), a maior parte dos cálculos é feita com vetores.
Esses vetores representam desde **posições no espaço 3D**, até **cores**, **coordenadas de textura** e muito mais, veja abaixo.
<br/><br/>
==float==
É o tipo mais simples: um número decimal.
**Exemplo:**
```c++
float intensidade = 0.8;
```
Pode representar brilho, intensidade de luz, opacidade, etc.
<br/><br/>
##
==vec2==
Um vetor com **2 componentes**:
```opengl
vec2 coordenada = vec2(0.5, 0.8);
```
**Usos comuns:**
- Coordenadas de textura (UV).
- Posições em uma tela 2D.
- Valores que precisam de dois números relacionados (ex: direção X e Y).
<br/><br/>
##
==vec3==
Um vetor com **3 componentes**:
```opengl
vec3 cor = vec3(1.0, 0.0, 0.0); // vermelho
```
**Usos comuns:**
- Cores RGB (vermelho, verde, azul).
- Posições no espaço 3D (x, y, z).
- Vetores de direção (ex: direção da luz).
<br/><br/>
##
==vec4==
Um vetor com **4 componentes**:
```opengl++
vec4 corComAlpha = vec4(1.0, 0.0, 0.0, 0.5); // vermelho 50% transparente
```
**Usos comuns:**
- Cores RGBA (RGB + Alpha/Transparência).
- Coordenadas homogêneas (x, y, z, w) para matrizes.
- Passar dados extras (às vezes usamos o quarto valor só pra carregar informação). <br/><br/>
> Depois entraremos mais a fundo em **matrizes**, não se preocupe.
<br/><br/>
## Tipos de Qualificadores <br/><br/>
No **GLSL**, as variáveis podem ter papéis diferentes dependendo de **onde vêm os dados** e **quando podem ser alterados**.
Os dois mais comuns (e mais assustadores pra iniciantes) são ==uniform== e ==const==. <br/><br/>
==**const** (constante)==
- Uma variável que **não pode mudar** durante a execução do shader.
- O valor é fixo e já conhecido **quando você escreve o shader**.
- Geralmente usado pra números mágicos ou valores que não vão ser alterados. <br/><br/>
**Exemplo em GLSL:**
```opengl
const float PI = 3.14159;
void main() {
float resultado = cos(PI);
}
```
> [!Note]Resumo
> **const** = imutável, fixo no código.
<br/><br/>
##
==uniform==
- É uma variável que vem **de fora do shader** (do programa OpenGL / UPBGE).
- É a ponte entre o **Python** e o **shader GLSL**.
- O valor é o mesmo para **todos os vértices ou fragmentos** de uma vez (não muda a cada pixel).
**Exemplo em GLSL:**
```c+
uniform vec3 lightColor;
void main() {
gl_FragColor = vec4(lightColor, 1.0);
}
```
E no Python (**UPBGE**), você poderia alterar esse valor:
```c
shader.setUniformVec3("lightColor", [1.0, 0.8, 0.6])
```
> [!Note]Resumo
**uniform** = dado vindo de fora, igual pra todo mundo no shader.
<br/><br/>
##
**Outros que confundem.**
Além de `uniform` e `const`, você ainda vai topar com: <br/><br/>
==attribute== (usado em **Vertex Shader**, até OpenGL 3.3)
- Dados que mudam por vértice (posição, normal, UV).
- No UPBGE moderno já virou `in`. <br/><br/>
==varying==
- Variáveis que são interpoladas entre o **Vertex Shader** e o **Fragment Shader**.
- Hoje em dia também virou `out` (no **vertex**) e `in` (no **fragment**) <br/><br/>
> [color=#04B848]😮 Mas fica calmo que vou te explicar melhor, entendo que pode ser confuso pra entender de primeira, mas pense da seguinte forma: <br/><br/> Digamos que você precise de alguma informação de um **cubo**, talvez a posição dos vértices, as normais ou as coordenadas de textura. Somente quem possui essas informações pra lhe dar é o **Vertex Shader**, certo? Então pra obter esses atributos você pode chamar eles através do ==attribute==. <br/><br/> E se precisar desses dados mais tarde pra usar no **Fragment Shader**, use o ==varying== pra enviar esses dados de forma que ele entenda. <br/><br/> Agora ficou claro?! 😜
Veja os exemplos abaixo:
<br/><br/> **Exemplo prático:**
```opengl
// Vertex Shader
attribute vec2 TexCoord;
varying vec2 UV;
void main() {
UV = TexCoord.st;
}
```
> [!Note] Comentando o código
> No código acima estamos pegando o atributo de coordenada de textura com o ==attribute==. Em seguida enviamos uma variável chamada **UV** para o **Fragment Shader** através do ==varying==, mas essa variável ainda está vazia, então logo abaixo informamos dentro da função **main** que o valor dela será a coordenada da textura, legal né?
```opengl
// Fragment Shader
varying vec2 UV;
void main() {
vec4 texColor = texture2D(myTexture, UV);
}
```
> [!Note] Comentando o código
No código acima estamos trabalhando dentro do **Fragment Shader**, basicamente recebemos a variável **UV** que trabalhamos anteriormente com o ==varying== no **Vextex Shader** e utilizamos ela dentro de uma função qualquer, seja criativo ein!
<br/><br/>
✅ **Comparando**
| Tipo | Quem define? | Pode mudar? | Escopo |
| :-------: | :--------: | :--------: | :--------: |
| ==const== | O shader | ❌ Não | Fixo e imutável |
| ==uniform== | Python/UPBGE | ✅ Sim | Igual pra todos os pixels |
| ==attribute== | Blender/Engine | ✅ Sim | Muda a cada vértice |
| ==varying== | Vertex Shader | ✅ Sim | Passa valores pros fragmentos (interpolados) |
> Não tem problema se não entendeu a tabelinha acima, basta ter compreendido os exemplos nos códigos acima. Avante!
<br/><br/>
## Acessando componentes
Você pode acessar os valores do vetor de diferentes formas:
```c++
// Por posição
vec3 pos = vec3(1.0, 2.0, 3.0);
float x = pos.x; // 1.0
float y = pos.y; // 2.0
float z = pos.z; // 3.0
// Por cor
vec3 cor = vec3(0.2, 0.5, 0.8);
float r = cor.r; // 0.2
float g = cor.g; // 0.5
float b = cor.b; // 0.8
// Por textura
vec2 uv = vec2(0.3, 0.7);
float s = uv.s; // 0.3
float t = uv.t; // 0.7
```
<br/><br/> **💡 Por que ==.st== ao invés de ==.xy== ?** <br/><br/>
É somente para boas práticas:
- ==.xyzw== → geralmente usado pra posições e vetores.
- ==.rgba== → geralmente usado pra cores.
- ==.stpq== → geralmente usado pra coordenadas de textura.
Todos funcionam iguais, mas usar o sufixo certo deixa o código mais legível.
**Exemplo:**
```opengl
// Mais fácil de entender:
vec2 uv = TexCoord.st;
// Do que isso:
vec2 uv = TexCoord.xy;
```
##
✅ Resumindo
> [!Note]
**`float`** → número único.
**`vec2`** → dois valores (UV, 2D).
**`vec3`** → três valores (RGB, XYZ).
**`vec4`** → quatro valores (RGBA, XYZ + W).
==.xy==, ==.rgb==, ==.st== são formas diferentes de acessar os mesmos dados, mas cada um tem um uso recomendado. <br/><br/>
# Funções
Pode parecer que não, mas diferente do python, toda a estrutura de um shader fica dentro de uma função principal que nomeamos por padrão de **main**. _Não tenha receio, não tem o que temer aqui_.
Quando criamos essa função, declaramos ela como ==void== por que ela é a **função principal do shader**, `void` significa que ela não retorna nada, ou seja, não precisar usar `return` como é feito no python.<br/><br/>
**Exemplo prático:**
```opengl
//Fragment Shader
float vec3 color = vec3(1.0, 0.0, 0.0); // Vermelho
void main() {
gl_FragColor = vec4(color, 1.0) // Tela vermelha
}
```
Note que dentro do **Fragment Shader** toda saída final que deseja renderizar na tela deve utilizar variáveis de 4 componentes (**vec4**). <br/><br/>
> [!Tip] Parabéns!!
> Pode parecer que não, mas essa é a **base** e **estrutura** de todo o código em OpenGL, se você entendeu isso, já consegue ao menos entender 50% dos shaders.
>
> Mais pra frente você aprenderá mais sobre matrizes básicas (**mat2**, **mat3** e **mat4**) e modulos de equações básicos como: **dot**, **cross**, **lenght**, **normalize**, **distance**, **reflect**, **refract**.
>
> E também sobre funções de interpolação, como: **mix**, **step**, **smothstep**.
<br/><br/>
# Loop
É muito importante sabermos trabalhar com **loops**, então vamos partir do pressuposto que você já conheça o loop utilizado no python. No OpenGL não tem segredo, veja:
```c
//No python
for i in range(8):
print(i) // Ele imprime de 0 ao 8
//No OpenGL
for (int i = 0; i < 9; i++) {
// faz algo
}
```
`int i = 0` significa que é um número inteiro e inicia em zero (0).
`i < 9` significa que o loop vai acontecer enquanto o valor (i) for menor que 9;
`i++` significa que a cada loop, será adicionado mais 1 no `i`;
<br/><br/>
# Integração
## Texturas Integradas
O Blender/UPBGE nos fornece algumas texturas prontas para trabalhar em nossos shaders, são elas:
### bgl_RenderedTexture
Nos fornece a imagem renderizada pelo Blender (framebuffer principal).
> É a base pra quase todos os efeitos (correção de cor, distorções, pós-processamento em geral).
```opengl
uniform sampler2D bgl_RenderedTexture;
```
<br/><br/>
### bgl_DepthTexture
Nos fornece o mapa de profundidade da renderização.
> Serve para efeitos que dependem da profundidade, tipo Depth of Field, Fog, SSAO.
```opengl
uniform sampler2D bgl_DepthTexture;
```
<br/><br/>
## Funções Integradas
Além das texturas, também tem funções específicas prontas para trabalhar com os shaders, são elas: <br/><br/>
### bgl_TextureCoordinateOffset
Nos fornece um array com offsets prontos pros 9 pixels vizinhos (kernel 3x3) da coordenada atual.
> Serve para convoluções e filtros como blur, sharpen, edge detection.
```opengl
uniform vec2 bgl_TextureCoordinateOffset;
```
<br/><br/>
### bgl_RenderedTextureWidth
Largura da tela (framebuffer).
```opengl
uniform float bgl_RenderedTextureWidth;
```
<br/><br/>
### bgl_RenderedTextureHeight
Altura da tela (framebuffer).
```opengl
uniform float bgl_RenderedTextureHeight;
```
<br/><br/>
## Integrando variáveis
Se quisermos transportar informações para o shader, podemos utilizar o python para esta tarefa, mas antes precisamos coletar o shader, veja:
```python
python
from bge import logic as g
scene = g.getCurrentScene()
variavel = 1.0
filter = scene.filterManager.getFilter(0)
filter.setUniform1f("nome_da_variável_no_shader", variavel)
```
```opengl
glsl
uniform float variavel;
uniform vec2 variavel;
uniform vec3 variavel;
uniform vec4 variavel;
uniform mat4 variavel;
```
O código acima basicamente importa os modulos do **blender engine**(bge), após isso setamos a cena e uma variável desejada. Com o básico feito, criamos uma variável ==filtro== que coletará os dados do nosso shader, para isso usamos o `filterManager.getFilter()` e passaremos o indice ==0== para informar que queremos o filtro do indice zero, cada shader ativo tem o seu indice.
Agora podemos utilizar alguns parâmetros para escolher que tipo de informação queremos enviar, veja os demais logo abaixo:
```opengl
setUniform1f("nome", variavel) //Envia um valor float
setUniform2f("nome", variavel[0], variavel[1]) //Envia dois valores float(vec2)
setUniform3f("nome", variavel[0], variavel[1], variavel[2]) //Envia três valores float(vec3)
setUniformMatrix4("nome", matriz) //Envia uma Matriz(mat4)
```
Pode alterar o número que será enviado. <br/><br/>
<br/><br/>
## Campo de Profundidade
No **UPBGE** podemos obter o campo de profundidade atráves do ==bgl_DepthTexture==. <br/><br/>
Mas pra que precisamos de uma **campo de profundidade**?
É ele quem diz qual a distância cada pixel está em relação a camêra, mas de forma **comprimida** e não **linear**. Com ele você consegue fazer várias coisas em shaders e gráficos 3D, como:
- Profundidade para efeitos de **pós-processamento** → desfoque de profundidade (Depth of Field), volumetria, neblina, etc.
- **Mapa de profundidade visual** → debug, ver o que está perto e longe.
- Calcular **normais** aproximadas → pegando vizinhos e fazendo cross(dx, dy).
- **SSAO** (Ambient Occlusion) → sombrear cantos e buracos automaticamente, porque a distância entre pixels indica o quanto eles se escondem.
- **SSR** (Screen Space Reflections) → reflexos na tela usando a posição real dos pixels.
- Colisão ou efeitos físicos em shaders, tipo simular que objetos colidem com a cena sem precisar do mesh completo. <br/><br/>
Então basicamente tudo que depende de “quanto cada pixel está distante da câmera”, passa pelo campo de produndidade (depth buffer).
Se você utilizar o código abaixo no **UPBGE** através de um ==**filtro 2D**==, já consegue ter alguns resultados.
```opengl
uniform sampler2D bgl_DepthTexture;
void main() {
float zdepth = texture2D(bgl_DepthTexture, gl_TexCoord[0].st).x;
zdepth = pow(zdepth, 100.0);
gl_FragColor = vec4(vec3(zdepth), 1.0); // mostra profundidade como cinza
}
```
**Saída:**

> [!Note] Comentando o código linha por linha
>
```opengl
uniform sampler2D bgl_DepthTexture;
```
A linha acima é utilizada para importar a textura de profundidade, para isso utilizamos o ==bgl_DepthTexture==, e junto dele informamos ao OpenGL que é um ==uniform==, ou seja, que o sistema(**UPBGE**) quem vai nos fornecer a textura. **Mas o que é este** ==sampler2D== **?**
**sampler2D** é um tipo de variável que representa uma textura 2D, como se fosse um “controle remoto” para uma textura. Ele não guarda a imagem em si, mas sabe onde a textura está na memória e permite que você pegue as cores dela.
- `sampler` quer dizer que é uma variável usada para amostrar dados.
- `2D` significa que essa textura tem duas dimensões (largura e altura).
Pra usar um `sampler2D`, você chama a função texture2D() passando ele e as coordenadas UV, como faremos nas próximas linhas. <br/><br/>
```opengl
void main() {
...
}
```
Como já comentamos anteriormente, `void main()` é a nossa função principal que irá retornar nosso resultado. <br/><br/>
```c++
float zdepth = texture2D(bgl_DepthTexture, gl_TexCoord[0].st).x;
```
Acima, nesta linha criamos uma variável chamada **zdepth**. Dentro dela iremos utilizar o `texture2D()` para acessa a textura de profundidade, vamos passar **dois** argumentos:
- **1° argumento:** A textura, no caso, ==bgl_DepthTexture== .
- **2° argumento:** O mapeamento **UV**, para isso utilizaremos uma função do OpenGL chamada `gl_TexCoord` que é uma array com várias coordenadas de textura, pra usar a primeira coordenada informaremos que será `[0]` e também usaremos `.st` para coletar o X e Y.
Ao final do `texture2D()` utilizaremos o ==.x==, ele é o responsável por coletar o canal `vermelho` da textura, ou pode usar ==.r==, dá na mesma. **Mas por que usar o canal VERMELHO?**
Segundo as documentações da OpenGL, as informações de profundidade ficam hospedadas no canal vermelho da textura.
**Resultado:** zdepth vai de 0 (perto) a 1 (longe), mas não é linear com a distância real da câmera. <br/><br/>
```opengl
zdepth = pow(zdepth, 100.0);
```
A equação **pow** eleva o valor a uma potência, no caso utilizei `100`, pois ela “contrasta” a profundidade, deixando objetos distantes mais escuros ou mais visíveis, dependendo do que você quer mostrar.
Sem isso, o Z pode parecer quase tudo branco ou escuro, dependendo do range da cena. <br/><br/>
```opengl
gl_FragColor = vec4(vec3(zdepth), 1.0);
```
==gl_FragColor== é a variável universal de renderização do OpenGL em todos os shaders, tudo o que passarmos dentro dela será renderizado pixel por pixel.
:::danger
**Atenção**
O valor do `gl_FragColor` sempre deverá ser entregue em `vec4`, que significa os canais `vermelho`, `verde`, `azul` e `alpha`.
:::
Então informamos o `vec4` que necessita de **4 componentes**, e, dentro dele informaremos a nossa variável `zdepth` para os **três** primeiros canais RGB, para isso utilizamos um `vec3()`. Ao final informamos o valor `1.0` para suprir o valor do `alpha`. <br/><br/>
**Resultado final:** você vê a profundidade da cena como uma escala de preto e branco como visto na imagem acima. <br/><br/>
**Como essa mágica acontece? Como o sistema entende o que deve ficar branco e o que deve ficar preto?** <br/><br/>
Simples, o shader percorre em todos os pixels, cada pixel possuí 3 cores (RGB) e 1 alpha.
Quando você coloca o mesmo valor no RGB, você está juntando todas as cores, então ficará branco ou preto, o que vai definir a intensidade será os diferentes valores entre 0 e 1, veja:
(0.0, 0.0, 0.0) → Preto, pois está sem cor.
(1.0, 1.0, 1.0) → Branco, pois juntou todas as cores.
(0.5 0.5, 0.5) → Cinza.
Então digamos que no pixel 1 a profudidade dele seja `0.4`, então esse valor será utilizado em todos os 3 canais, e isso percorre com valores diferentes do `zdepth` para todos os pixels, formando o mapa de profundidade que vocês aprenderam. <br/><br/>
<br/><br/>
:::info
**Observação**
Apesar de termos agora o valor da profundidade de um pixel, não significa que temos a **distância** do pixel até o objeto. Isso impede que façamos outros shaders mais complexos, para pular este obstáculo devemos implementar o código, ou seja, simular a visão da câmera e calcular a distância.
**A continuação do código será adicionada aqui no site tão logo, obrigado por chegar até aqui.**
:::