---
# System prepended metadata

title: Fundamentos

---

![Captura de tela 2025-08-25 153241 (1)](https://hackmd.io/_uploads/Sk68WEiKxe.png)
<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:**
![image](https://hackmd.io/_uploads/HyAig1Wcxl.png)


> [!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/>

![giphy](https://hackmd.io/_uploads/Sy6F2lbcxl.gif =30%x)<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.**
:::



