Skip to content
This repository has been archived by the owner on May 25, 2024. It is now read-only.

Latest commit

 

History

History
261 lines (200 loc) · 8.76 KB

README.md

File metadata and controls

261 lines (200 loc) · 8.76 KB

Amogpu

The reason to this readme is written in another language and not english is because there is not good materials to learn OpenGL in portuguese (Brasil).

--

Entenda, a GPU é muito importante para nós programdores e programadoras, sem ela aplicações que nós usamos não teria o minimo de performance e design bonito! infelizmente poucas pessoas se interessam por este incrível lado do hardware! Essa biblioteca mostra como podemos utilizar corretamente funções do OpenGL moderno.

Tessellator, batch. shape builder e outros conceitos estão inseridos na amogpu, aqui está um simples resumo do que são:

  • O que é tessellator? Do mesmo modo que traçamos linhas para formar um tecido, em computação trassamos linhas por vértices, quando tratamos de elementos UI que elaboram uma GUI, é preciso manipular cada evento de cada elemento UI sincronizado com o desenho, para isso preciamos passar para a GPU vértices e as posições na tela, entretanto não dá pra só atualizar a todo tick e passar vértices a todo tick e a todo momento.

  • O que é batch? Batch é salvar em forma de lote e utilizar depois, diferente de você enviar vértices todo instante (tick), podemos armazenar as vértices em lote e depois renderizar, e mais, salvar as posições, cor ou textura para renderizar sem a necessidade de mandar todas as vértices denovo para a GPU, com isso é possível ter uma performance muito maior se comparada com outros métodos.

  • O que é shape builder? Se você quiser renderizar formas geométricas rapidamente, mas ela não é eficiênte para UI complexas, o nome builder por que ela constrói um shape e destrói logo em seguida.


Get Start

Essa biblioteca foi compilado pra Windows-x86_x64, se você quiser contribuir com uma versão compilada em outro sistema fique a vontade. Primeiramente baixe o repositório e copie a pasta include/ & lib/ dentro do MinGW, pronto! Agora você deve linkar:

target_link_libraries(<projectname> amogpu) g++ ... -lamogpu

Inicialmente inclua amogpu/amogpu e chame a função amogpu::init, deste jeitinho ó:

#include <amogpu/amogpu.hpp>

// ...
amogpu::init(); // Não é pra ocorrer nenhum erro, caso sim reporte.

/**
 * Mainloop.
 **/
while (true) {
  // ...
  // Você deve chamar essa função apenas 1 vez antes de desenhar qualquer coisa.
  // Ela serve pra atualizar as matrizes de posição da camêra.
  amogpu::matrix();
  
  // ...

Shape Builder

Shape builder é uma feature para desenhar circulos e retangulos de forma rápida e eficiente, mas ela não serve pra contextos aonde você precisa de performance e controle.

shape_builder shape;

/*
 * Main loop.
 */
while (true) {
   // Diferente de dynamic batching, você não precisa controlar invoke e revoke calls.
   shape.invoke();
   
   // Se você quiser criar um circulo use amogpu::shape::CIRCLE, se não use amogpu::shape::RECT
   shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 0.5f), 0); // o ultimo argumento é a texture id.
   shape.modal(0.0f, 0.0f, 1.0f, 1.0f); // se não tiver uma textura embutida no shape não é preciso chamar esse metódo!
   shape.draw(20, 20, 200, 200); // (x, y, width, height), se você estiver renderizando um circle então deixe width e height iguais.
   
   // Pronto é só isso.
   // Você pode chamar quantas vezes quiser.
   
   shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 1.0f)); 
   shape.draw(20, 60, 200, 200);
   
   shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 1.0f), texture_cool_cat_id);
   shape.modal(0.0f, 0.0f, 1.0f, 1.0f);
   shape.draw(0, 60, 200, 200);
   
   // Prontinho.
   shape.revoke();
}

A imagem a baixo é ilustrativa, se trata de outro código usando a feature shape_builder. Alt text

Dynamic Batching

O funcionamento é simples:

/**
 * Mainloop.
 **/
while (true) {  
  // ...
  // Qualquer lugar após glClear & glClearColor.
  batch1.draw();
  batch3.draw();
  batch2.draw();
  
  // ...
  // Final do loop.
}

Detalhes:

#include <amogpu/gpu_handler.hpp> // ou só #include <amogpu/amogpu.hpp>

// ...
dynamic_batching batch;

// Em 1 tick ou em poucos ticks aonde queremos mudar alguma coisa.
batch.invoke(); // Chamamos a GPU.
// Enviamos dados para a GPU.
batch.revoke(); // Finalizamos o segmento de desenhar da GPU.

// Em um loop com contexto OpenGL.
while (true) {
   batch.draw();
}

Dentro do segmento de desenhar podemos renderizar muitos rects (dá pra facilmente renderizar circulos mas nesse caso é feito no fragment shader):

batch.invoke(); // Iniciamos esse segmento.

x = 0;
y = 0;
w = 1.0f;
h = 1.0f;

batch.instance(20, 50); // Instancia contém 3 argumentos, 2 primeiros são [x, y] e o ultimo é o factor, mas não precisa especificar agora.
batch.fill(1.0f, 1.0f, 1.0f, 1.0f); // definimos a cor em RGBA normalisados (1.0 - 0.0).
batch.bind(f_renderer.texture_bitmap); // texture é um uint que guarda dados de uma textura.

// Como isso é um shape simples e não um grande desenho (e.g tile map, wireframe) você não precisa especificar um fator unico.
batch.modal(300, 300); // isso serve para definir o tamanho, mas isso só funciona pra shape simples.

// Mesh de vértices.
batch.vertex(x, y);
batch.vertex(x, y + h);
batch.vertex(x + w, y + h);
batch.vertex(x + w, y + h);
batch.vertex(x + w, y);
batch.vertex(x, y);

// Vamos desenhar a letra V.
x = 0.922495f;
w = 0.008192f;
y = 0.000000f;
h = 0.678571f;

// Se temos 6 vértices então devemos aplicar 6 vezes coordenadas uv.
// Como é texturizado devemos pasar as coordenadas normalisadas, se não tiver textura inclusa é só passar 0.0f 6 vezes.
batch.coords(x, y);
batch.coords(x, y + h);
batch.coords(x + w, y + h);
batch.coords(x + w, y + h);
batch.coords(x + w, y);
batch.coords(x, y);

batch.next(); // Se você quiser desenhar 30 rects é só pegar esse sub-segmento de (instance() - next()) calls e replicar.

// e.g
// você colocou todo o código acima dentro de uma função ou metódo com parametros para apenas a posição.
// então você pode invocar muitas vezes.
push_rect(20, 50);
push_rect(90, 80);
push_rect(700, 250);

batch.revoke(); // Finalizamos esse segmento.

Alt text

Se você quiser ver um exemplo real recomendo olhar a pasta test/ do projeto, no main.cpp você pode ver como usar as features dynamic_batching e font_renderer de forma otimizada.

Aqui irei explicar como usar o dynamic_batching com multiplas instâncias. Observação: Multiplas instâncias não tem nada haver com instanced rendering, amogpu ainda não tem essa feature.

// ...
// Entretanto se você querer desenhar multiplos shapes na tela em uma unica instância, você tem que especificar o tamanho na hora de enviar as vértices.
// e.g você dá 18 vértices e 18 coordenadas em uma unica instância, mas o tamanho de cada shape é unico, então você altera a posição
// dá vértice e o tamanho de cada mesh, então ai que entra o factor, você tem que colocar no paramêtro alguma coisa que vá fazer sentido
// para o dynamic batch alterar o buffer.
// ...

batch.instance(20, 20);
batch.fill(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); // white;

float x = 0;
float y = 0;

float w = 30;
float h = 30;

for (uint8_t i = 0; i < 5; i++) {
  batch.vertex(x, y);
  batch.vertex(x, y + h);
  batch.vertex(x + w, y + h);
  batch.vertex(x + w, y + h);
  batch.vertex(x + w, y);
  batch.vertex(x, y);
  
  batch.coords(0.0f, 0.0f);
  batch.coords(0.0f, 0.0f);
  batch.coords(0.0f, 0.0f);
  batch.coords(0.0f, 0.0f);
  batch.coords(0.0f, 0.0f);
  batch.coords(0.0f, 0.0f);
  
  x += w + 5;
}

batch.factor(x / 5); // Por que x / 5? Se o tamanho de algum shape mudar, então o buffer precisa mudar.
batch.next();

Alt text


Font Renderer

#include <amogpu/amogpu.hpp>

dynamic_batching batch;
font_renderer f_renderer;

// Se você quiser alterar o tamanho ou mudar de fonte é só rodar esse método.
f_renderer.load("path/to/font.ttf", 18);
f_renderer.from(&batch); // isso vai dizer pro font renderer qual dynamic batch usar.

// Se você quiser deixar automatico, é só fazer.
f_renderer.from(amogpu::invoked);

batch.invoke();
f_renderer.render("hi sou linwda", 10, 10, amogpu::vec4(1.0f, 1.0f, 1.0f, 1.0f));
batch.revoke();

// Ai no loop você dá draw.
while (true) {
    batch.draw();
}

// Você também pode fazer combinações.
// e.g
dynamic_batch batch2;
f_renderer.from(&batch);

// Você pode chamar o batch aplicado pelo método batch.
f_renderer.batch()->invoke();
f_renderer.render("vwc é linda(o)", 10, 10 + 1 + f_renderer.get_text_height(), amogpu::vec4(1.0f, 1.0f, 1.0f, 1.0f));
f_renderer.batch()->revoke();

// Ai no loop você faz.
while (true) {
     f_renderer.batch()->draw();
}

Alt text