Programação com OpenGL/Modern OpenGL Tutorial 04
Neste tutorial vamos mergulhar no mundo da transformação de matrizes, assim poderemos movimentar, rotacionar e mudar a escala do nosso triângulo.
Configurando as matrizes
editarAlgumas coisas temos que lembrar quando trabalhamos com matrizes:
- Transformações são aplicados pela multiplicação de matrizes 4X4 na ordem inversa. O
M = M_translation * M_rotation
que diz para rodar primeiro, e depois para movimentar. - A Matriz é uma matriz de identidade que não faz nada - absolutamente nenhuma movimentação.
- Para transformar uma vértice, nós multiplicaremos ele pela matriz:
v' = M * v
- As matrizes 4x4 podem ser aplicadas somente para vetores 4X1, que obtemos usando uma das quatro dimensões da vertice: (x, y, z, 1).
Nestas multiplicações, vamos precisar de uma biblioteca aritmética(math). os Shader já possuem uma de fácil suporte para operações de matrizes, mas precisamos manipular-las na linguaguem C. é também mais eficiente, porque os shaders são executados em cada vértice, então é melhor calcularmos as matrizes de antemão.
Neste tutorial nós usaremos OpenGL Mathematics A biblioteca GLM, está escrita em C++. A GLM usa algumas convenções da GLSL, então será mais fácil para começar. Esta documentação também mostrar substitutos para a defassada OpenGL 1.x e funções da GLU, como o glRotate
, glFrustum
ou gluLookAt
, que vem a calhar se você já usou um deles.
Existem alternativas, como libSIMDx86 (que também trabalham em processador que não são da família x86). você pode também escrever seus próprio códigos de matrizes, desde que não seja muito grande, veja o exemplo mesa-demos-8.0.1/src/egl/opengles2/tri.c
no demos que vem no Mesa3D.
Nota: a partir de agora usaremos C++, vamos precisar mudar nosso programa para C++ - desculpe; vamos então editar nosso primeiro tutorial para começar. Na verdade você só precisa mudar o nome triangle.c
para triangle.cpp
O GLM é uma biblioteca header-only, então você não precisa modificar o Makefile, contando que os header esteja configurado corretamente no standard path. Para instalar o GLM no Debian ou no Ubuntu:
apt-get install libglm-dev
Agora nós podemos adicionar os cabeçalhos da GLM:
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
Usando 3D Points
editarNossa matriz de transformação são para vértices em 3D. Mesmo que nós estejamos em 2D, vamos descrever nosso triângulo com um ponto 3D com Z = 0. de qualquer maneiro vamos mudar nosso objeto para 3D no próximo tutorial :)
Vamos definir ele (3 elementos por vértices) para OpenGL no triangle.cpp:
struct attributes {
GLfloat coord3d[3];
GLfloat v_color[3];
};
Agora, no init_resources()
struct attributes triangle_attributes[] = {
{{ 0.0, 0.8, 0.0}, {1.0, 1.0, 0.0}},
{{-0.8, -0.8, 0.0}, {0.0, 0.0, 1.0}},
{{ 0.8, -0.8, 0.0}, {1.0, 0.0, 0.0}}
};
...
attribute_name = "coord3d";
attribute_coord3d = glGetAttribLocation(program, attribute_name);
if (attribute_coord3d == -1) {
fprintf(stderr, "Could not bind attribute %s\n", attribute_name);
return 0;
}
Mudaremos a configuração do array de vértices em onDisplay()
glVertexAttribPointer(
attribute_coord3d, // atributo
3, // numero de elementos por vértice, que é (x,y,z)
GL_FLOAT, // o tipo de elemento
GL_FALSE, // o valor que ele está
sizeof(struct attributes), // o próximo coord3d aparecerá a cada 6 flots.
0 // deslocamento do primeiro elemento.
);
substitua as outras ocorrências de 'attribute_coord2d' correspondente e chame o shader usando a nova coordenada.
attribute vec3 coord3d;
[...]
void main(void) {
gl_Position = vec4(coord3d, 1.0);
Criando uma matriz de transformação
editarNo GLM vem inclusivo funções para calcular rotações, movimentação e escalas das matrizes.
Vamos colocar nossa matriz de transformação em onIdle()
, que calculará uma rotação progressiva combinado com movimentos:
void onIdle() {
float move = sinf(glutGet(GLUT_ELAPSED_TIME) / 1000.0 * (2*3.14) / 5); // -1<->+1 a cada 5 segundos
float angle = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 45; // 45° por segundo
glm::vec3 axis_z(0, 0, 1);
glm::mat4 m_transform = glm::translate(glm::mat4(1.0f), glm::vec3(move, 0.0, 0.0))
* glm::rotate(glm::mat4(1.0f), angle, axis_z);
[...]
Passando para matriz de transformação
editarAssim como vimos no tutorial anterior, vamos adicionar um novo uniform, com glUniformMatrix4fv
:
/* Global */
#include <glm/gtc/type_ptr.hpp>
GLint uniform_m_transform;
/* init_resources() */
uniform_name = "m_transform";
uniform_m_transform = glGetUniformLocation(program, uniform_name);
if (uniform_m_transform == -1) {
fprintf(stderr, "Could not bind uniform %s\n", uniform_name);
return 0;
}
/* onIdle() */
glUniformMatrix4fv(uniform_m_transform, 1, GL_FALSE, glm::value_ptr(m_transform));
Se você não está usando o GLM, é bom passar um ponteiro para uma array GLfloat[16], deste jeito:
GLfloat matrix[16] = {...};
glUniformMatrix4fv(uniform_m_transform, 1, GL_FALSE, matrix);
O vertex shader apenas tem que multiplicar o vertex pela matrix, como mostraremos abaixo:
uniform mat4 m_transform;
void main(void) {
gl_Position = m_transform * vec4(coord3d, 1.0);
[...]
Nós notaremos que ainda que temos o problema em relação ao aspecto, (caso venhamos a ver em um monitor de 16:9) Nós corrigiremos isto no próximo tutorial de matrizes de Model-View-Projection(Modelo, visualização e Projeção)
Experimentando
editarLembre que nós mencionamos sobre como aplicar matrizes na ordem contrária? em nosso exemplo, nos rodamos primeiro e depois movimentamos.
Tente fazer em outra direção: você vai fazer que o triângulo rotacione depois ele vai movimentar, o que significa que ele irá rodar sobre sua origem ao invés de rodar sobre seu próprio centro.