Programação com OpenGL/Modern OpenGL Tutorial 06: diferenças entre revisões

Origem: Wikilivros, livros abertos por um mundo aberto.
[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
Linha 135: Linha 135:
</source>
</source>


[[File:OpenGL_Tutorial_Texture_Flipped.png|thumb|Something is wrong...]]
[[File:OpenGL_Tutorial_Texture_Flipped.png|thumb|Alguma coisa está errada...]]
Mas o que houve? Nossa textura está de cabeça para baixo!
Mas o que houve? Nossa textura está de cabeça para baixo!



Revisão das 20h53min de 17 de maio de 2013

Carregando uma textura

Nossa textura, em 2D

Para carregar uma textura, nós precisamos de um decoficador para carregar uma imagens, em particular as do tipo JPEG ou PNG. Normalmente, seu programa final usara uma biblioteca generica como o SDL_Image, SFML ou Irrlich, que suportam varios formatos de imagem, assim você não precisa escrever o código da imagem para carrega-lo. Bibliotecas especializadas como o SOIL(veja abaixo) pode ser interessante.

Para o primeiro passo, nós precisamos manipular uma imagem em nível-baixo para entender o basic, assim faremos um truque: O GIMP pode exportar uma imagem para um código fonte C. que poderá estar em nosso programa! Eu usarei a opção de salvamento igual a captura de tela.

Exportando uma imagem como C pelo GIMP

Se tiver demanda, nós podemos providenciar um tutorial especial para leitura de formato simples como o PNM, ou do tipo como BMP ou TGA (este dois também são simples, mas suportam compressão e vários formatos por isto é um pouco difícil suportar todas as suas opções).

Nota: Agregar imagem em código C é muito eficiente em questão de memória, por isto não vamos usar sempre. Tecnicamente: ela é armazenada em um segmento BBS do programa, em vez do heap, por isto não pode ser liberado.

Nota 2: você pode encontrar a fonte do GIMP em res_texture.xcf no repositório de códigos.

Ta compilar automaticamente o aplicativo quando você modificar o res_texture.c, coloque ele no Makefile:

cube.o: res_texture.c

Criando uma textura para o OpenGL buffer

Um buffer é basicamente um espaço na memória da placa de vídeo, assim o OpenGL pode acesa-lo rapidamente.

/* Globais */
GLuint texture_id;
GLint uniform_mytexture;
/* init_resources */
  glGenTextures(1, &texture_id);
  glBindTexture(GL_TEXTURE_2D, texture_id);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, // target
	       0,  // level, 0 = base, sem minimap,
	       GL_RGB, // internalformat (formato interno)
	       res_texture.width,  // largura
	       res_texture.height,  // altura
	       0,  // borda, sempre em 0 no OpenGL ES
	       GL_RGB,  // formato
	       GL_UNSIGNED_BYTE, // tipo
	       res_texture.pixel_data);
/* render */
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texture_id);
  glUniform1i(uniform_mytexture, /*GL_TEXTURE*/0);
/* free_resources */
  glDeleteTextures(1, &texture_id);

Coordenadas da textura

Agora nós precisamos dizer onde cada vertex será localizada em nossa textura. Para isto, vamos substituir o atributo v_color pelo vertex shader com um texcoord:

GLint attribute_coord3d, attribute_v_color, attribute_texcoord;
/* init_resources */
  attribute_name = "texcoord";
  attribute_texcoord = glGetAttribLocation(program, attribute_name);
  if (attribute_texcoord == -1) {
    fprintf(stderr, "Could not bind attribute %s\n", attribute_name);
    return 0;
  }

Agora, que parte da textura nós mapearemos, para dizer, no canto superior esquerdo da face da frente? Bem isto depende:

  • Para a face frontal: o canto superior esquerdo da nossa textura
  • Para o topo da face: o canto inferior-esquerdo da nossa textura

Nós vemos que múltiplos pontos da textura será anexada em algumas vértices, O vertex shader não será capaz de decidir qual deve ser escolhido.

Assim nós precisamos reescrever o cubo usando 4 vértices por face, sem reutilizar as vértices.

Para começar enfim, vamos apenas trabalhar com a face frontal. Facil! nós apenas teremos na tela os 2 primeiros triângulos (as 6 primeiras vértices):

  glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);

Assim, as coordenadas da nossa textura estarão entre [0, 1], com o eixo x da esquerda para direita, e o eixo y de baixo para cima:

  /* init_resources */
  GLfloat cube_texcoords[] = {
    // front
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
  };
  glGenBuffers(1, &vbo_cube_texcoords);
  glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords);
  glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords), cube_texcoords, GL_STATIC_DRAW);
  /* onDisplay */
  glEnableVertexAttribArray(attribute_texcoord);
  glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_texcoords);
  glVertexAttribPointer(
    attribute_texcoord, // atributos
    2,                  // numero de elementos por vértices, que é (x,y)
    GL_FLOAT,           // o tipo de cada elemento
    GL_FALSE,           // como o valor está
    0,                  // sem dados extras em cada posição
    0                   // deslocamento do primeiro elemento
  );

Vertex shader:

attribute vec3 coord3d;
attribute vec2 texcoord;
varying vec2 f_texcoord;
uniform mat4 mvp;

void main(void) {
  gl_Position = mvp * vec4(coord3d, 1.0);
  f_texcoord = texcoord;
}

Fragment shader:

varying vec2 f_texcoord;
uniform sampler2D mytexture;

void main(void) {
  gl_FragColor = texture2D(mytexture, f_texcoord);
}
Alguma coisa está errada...

Mas o que houve? Nossa textura está de cabeça para baixo!

Na convenção do OpenGL (começando no canto inferior esquerdo) é diferente das aplicações em 2D (começo no canto superior esquerdo). Para corrigir isto podemos:

  • Ler as linhas do pixel de baixo para cima
  • Trocar as linhas do pixel
  • Trocar as coordenadas Y da textura

A maior parte das bibliotecas gráficas retornam os array dos pixel como na convenção 2D. Porem o DevIL é uma opção que mantém a origem como conhecemos. Como alternativa, temos alguns formatos como BMP e TGA que guardar as linhas do pixel de baixo para cima nativamente (o que pode explicar a certa popularidade que pesa para o TGA entre os desenvolvedores 3D), muito util quando se escrever um carregador próprio para ele.

Trocando as linhas do pixel podemos terminar o código C que rodaremos, se o seu programa está em linguagem de nível alto como o Python isto pode ser feito apenas em uma linha. A desvantagem é que o carregamento da textura será mais lento por causa dos passos extras.

Revertendo a coordenada da textura é um meio mais fácil para nós, podemos fazer isto no fragment shader:

void main(void) {
  vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);
  gl_FragColor = texture2D(mytexture, flipped_texcoord);
}

Certo, tecnicamente nós poderíamos escrever as coordenadas da textura em outra direçõe logo de cara - mas outros aplicativos 3D costumam trabalhar como nós descrevemos.

Um cubo completo