Ponto V!

Home OpenGL Primitivas OpenGL
Vinícius Godoy de Mendonça
Primitivas OpenGLImprimir
Escrito por Vinícius Godoy de Mendonça

No artigo anterior, vimos como obter e alterar informações da máquina de estados da OpenGL. Vimos também como ler informações sobre a OpenGL instalado na máquina e como verificar os códigos de erro. Neste, iremos ver quais são as primitivas da OpenGL e como desenhá-las.

Formas básicas: Pontos, linhas e polígonos

Todas as formas básicas estão em apenas 3 categoriais: pontos, linhas e polígonos. Você já deve ter uma boa idéia do que cada figura dessas representa matematicamente. Entretanto, esses conceitos não são idênticos na matemática e no computador. Primeiro, devido a imprecisão dos números de ponto flutuante e os conseqüentes erros de arredondamento, o que afeta todo o sistema de coordenadas da OpenGL. Segundo, devido a limitação física do monitor, que tem como sua menor unidade o pixel.

Vamos, então, as definições dos conceitos de ponto, linha e polígono adaptadas a essa realidade:

Um ponto é constituído por três variáveis de ponto flutuante. A OpenGL sempre trabalha com coordenadas tridimensionais e comandos onde apenas duas dimensões aparecem assumem os valores de Z como sendo zero. Um ponto é também chamado de vértice, quando representa uma extremidade de qualquer outra primitiva.

Quando falamos em linhas nos referimos à segmentos de retas, isso é, um traço delimitado em suas duas extremidades por dois vértices.

Finalmente, quando falamos em polígonos, estamos falando de um conjunto fechado de segmentos de reta (a OpenGL chama esse conjunto fechado de loop). Por ser uma figura fechada, a OpenGL pode preencher o interior de polígonos com cores e texturas. Quando falamos em formas básicas, não estamos falando de qualquer polígono. Os polígonos se restringem apenas a polígonos convexos simples. Ou seja, um polígono em que nenhum dos lados ficará no interior da figura original e onde dois de seus vértices nunca se encontram. Assim, todos os seguimentos de reta que sejam possíveis traçar com dois pontos quaisquer dentro do polígono terão, necessariamente, todos os seus pontos integralmente dentro do polígono. Abaixo, alguns exemplos de polígonos que podem e que não podem ser desenhados:

Polígonos na OpenGL

Essa restrição tem uma razão técnica. É mais fácil construir hardware que desenha esse tipo de polígono de forma acelerada. E o que acontece se você não cumprir essas restrições? O comportamento é indefinido, ou seja, vai variar de hardware para hardware. Exploraremos algumas formas sutis de quebrar essa regra no artigo sobre quadrados e polígonos.

Primitivas da OpenGL

O que são primitivas? Primitivas são comandos que desenham as formas básicas da OpenGL. Essas formas servem como peças, para montar qualquer desenho desejado. O comando básico para desenho de primitivas é:

void glBegin(GLenum modo)

Ele permite à você dizer à OpenGL que você está pronto para começar um desenho e o que vai ser desenhado. Você pode usar um dos 10 modos abaixo para desenhar qualquer uma das três formas básicas. Observe que a OpenGL trata de maneira especial dois tipos de polígonos: os triângulos e os quadriláteros.

  • GL_POINTS: Representando pontos individuais;
  • GL_LINES: Linhas composta por pares de vértices;
  • GL_LINE_STRIP: Uma série de linhas conectadas;
  • GL_LINE_LOOP: Uma série de linhas conectadas. Também conecta o primeiro e o último vértice;
  • GL_TRIANGLES: Desenha triângulos a cada 3 vértices fornecidos;
  • GL_TRIANGLE_STRIP: Uma série de triângulos conectados. Após o desenho do primeiro triângulo, cada vértice adicional forma um novo triângulo com dois últimos pontos fornecidos;
  • GL_TRIANGLE_FAN: Uma série de triângulos com um único vértice em comum. O vértice comum é o primeiro vértice fornecido;
  • GL_QUADS: Desenha um quadrilátero a cada 4 vértices fornecidos;
  • GL_QUAD_STRIP: Desenha uma série de quadriláteros. Após o primeiro, apenas mais 2 vértices precisam ser fornecidos para o desenho do segundo quadrilátero;
  • GL_POLYGON: Desenha polígonos convexos simples com um número arbitrário de vértices.

A figura abaixo ilustra um exemplo para cada um dos modos de desenho. Observe atentamente o número de cada vértice e como a OpenGL conectou os seguimentos de reta para formar cada figura:

primitivasVocê diz à OpenGL que terminou de desenhar através da função glEnd(). A função é void e não aceita qualquer tipo de parâmetro. Geralmente, faz-se a endentação do que aparece dentro dos blocos glBegin() e glEnd().

Cadê as curvas?

Você notou que todas as primitivas só desenham formas retas? A OpenGL não fornece uma forma primitiva de traçar curvas. Entretanto, isso não significa que essa seja uma tarefa impossível. Qualquer figura subdividida nos pontos certos e ligadas à seguimentos de retas parece curva. Quanto maior o número de pontos, menor o serrilhado e mais suave a curva parecerá. Veja um exemplo:

curvasMesmo as curvas não sendo primitivas, a OpenGL também fornece alguns comandos para dar suporte ao desenho de curvas ou superfícies curvas usando a técnica acima, através dos comandos avaliadores (evaluator commands) e dos NURBS, fornecidos pela biblioteca Glut. Esse tópico está muito adiante dos artigos de primitivas e será tratado numa ocasião futura.

Desenhando vértices

Vértices são um conceito particularmente importante na OpenGL, afinal, é através deles descrevemos absolutamente todas as formas primitivas. Mais do que isso, a OpenGL também fará seu mapeamento de texturas, luzes, neblina e cores baseado nos vértices. Para especificar um vértice, recorremos à uma família de funções, chamadas glVertex. Infelizmente, os projetistas da OpenGL optaram por não usar sobrecarga de nomes de função, e, portanto, as diferentes funções da família seguem a seguinte convenção:

  1. Inicia-se o nome com o prefixo gl;
  2. Coloca-se o nome da família (nesse caso Vertex);
  3. Indica-se o número de coordenadas a serem usadas;
  4. Indica-se o tipo de dados das coordenadas, através da primeira letra de seu tipo de dado. Usa-se também a letra v para vetores;

No caso de glVertex:

void glVertex(234)(dfis)[v](...)

Essa convenção de nomes de função é usada em todo lugar, não só na função glVertex, portanto, acostume-se com ela.

Uma das assinaturas mais usadas da família glVertex é certamente void glVertex3f(float, float, float), para o desenho de pontos em três dimensões, com cada float representando a coordenada do vértice nos eixos x, y e z.

Revendo o desenho do triângulo

Agora que já entendemos as primitivas e os vértices, que tal revisitarmos o bloco glBegin() e glEnd() da função dos artigos anteriores?

glBegin(GL_TRIANGLES); //desenho de triângulos
   glColor3f(1,0,0);
   glVertex2f(0.0f,0.5f); //Vértice 1
   glColor3f(0,1,0);
   glVertex2f(-0.5f, -0.5f); //Vértice 2
   glColor3f(0,0,1);
   glVertex2f(0.5f, -0.5f); //Vértice 3
glEnd();

Como você pode notar, usamos o modo de desenho GL_TRIANGLES para desenhar o triângulo. Então, dentro da função, desenhamos cada um dos três vértices, usando o sistema de coordenadas padrão (que varia de -1 até 1 em cada eixo). Lembre-se que a OpenGL trabalha com o sistema de coordenadas igual ao que vemos nos livros de matemática, então, (-1, -1) em y significa o ponto inferior esquerdo da tela, enquanto (1,1) o superior direito.

Associado a cada vértice, existe uma informação de cor, descrita pela função glColor. Cores não serão explicadas nesse momento, mas as mantive no exemplo para demonstrar na prática que os vértices podem carregar mais informações do que simplesmente a sua coordenada. Isso reforça o quão importante é o conceito de vértice para a OpenGL.

Outro detalhe interessante: Note que os vértices foram fornecidos no sentido anti-horário. Eles não podem ser fornecidos de qualquer jeito, pois é através do sentido que a OpenGL sabe não só como ligar as retas mas qual é o lado que a figura está virada. Algumas otimizações envolvem não desenhar um dos lados da figura, como veremos em artigos futuros.

Faça você mesmo:

Experimente alterar o código do triângulo girando criado nos artigos anteriores para usar as outras primitivas da OpenGL. Restrinja-se ao que está entre ao bloco glBegin() e glEnd(). Tente usar cada um dos 10 modos da enumeração do comando, como descrito acima;

2. Tente desenhar um círculo usando diversos seguimentos de reta. Você pode usar os comandos for e variáveis dentro dos blocos glBegin e glEnd normalmente. Você precisará usar a função as funções seno e cosseno (sin e cos);

3. Crie mais 2 blocos glBegin e glEnd embaixo do primeiro para criar a bandeira do Brasil girando (não precisa desenhar as estrelinhas e nem a faixa, a menos que você esteja MUITO determinado). As funções abaixo deixam os vértices nas cores verde, amarela e azul. Ela afeta todos os vértices declarados após a função:

glColor3f(0.0f, 0.7f, 0.0f); //Verde
glColor3f(1.0f, 1.0f, 0.0f); //Amarelo
glColor3f(0.0f, 0.0f, 1.0f); //Azul


Comentários (14)
  • Sora  - Ótimo
    avatar

    Ótimo artigo! Completo e explica super bem o uso de primitivas da OpenGL com imagens que ajudam a entender melhor ainda.

  • Vinícius Godoy de Mendonça
    avatar

    Obrigado. :)

  • Koolen  - Bacana o exemplo...
    avatar

    Consegui fazer a bandeira, mas tive algumas dificuldades para usar o
    GL_TRIANGLE_FAN e o GL_POLYGON, bom mais eu saquei como é o esquema, se tiver um folha quadrigulada (folha em grade) em mãos da pra fazer, o problema e que tentativa e erro fica muito dificil.

    Concordo que se pesquisar talvez encontre alguma for que facilite este processo.

    Mas vou continuar com o estudo. Pois o entendimento ficou bem aplicavel..

    Parabens mais uma vez.

  • edna ferreira  - perfeito
    avatar

    ótimo conteúdo ! me ajudou bastante estou usando a maioria de seus artigos para estudos de complementação do meu primeiro game!!


    Edna Ferreira (estudante de programação)

  • Vinícius Godoy de Mendonça
    avatar

    Obrigado. :)

  • Tales  - Muito Bom
    avatar

    Cara eu conhecia a antiga versão do site e era boa, mas essa novo é bem mais fácil de se localizar. Também gostei do fato de vocês usarem a SDL, pois eu já tenho domínio sobre esta. Os artigos são tão bem explicados que eu resolvi ler eles todos antes de partir pro redbook. Continuem assim!

  • Vinícius Godoy de Mendonça
    avatar

    Valeu. :)

  • Luiz Paulo
    avatar

    Eeee! Aprendendo OpenGL :D
    Ótimo artigo / Tutorial.

    A question, if I may :P
    Estava fazendo uns testes, tentando fazer um sistema de Tiles . O máximo de Quads que consigo desenhar sem diminuir o FPS é 600 ~ 700.

    Uma versão resumida do código seria
    for(int i = 0; i < 700; i++)
    glBegin ...

    Só que o que eu tenho em mente, quando a pessoa desse um zoom out, teriam mais ou menos 1000 tiles na tela.

    Existe algum jeito mais otimizado do que desenhar Quad por Quad?

    Tentei assim também, mas deu na mesma.

    glBegin();
    for (int i = 0; i < 700; i++)...
    glEnd();

  • Bruno Crivelari Sanches
    avatar

    Tenho tido conversas com um amigo exatamente sobre isso, o que tenho sugerido é:

    Crie um único VBO para um quad (seriam apenas dois triângulos), faça um bind desse VBO e na hora de desenhar, va apenas modificando a posição dele usando gltranslate ou se precisar, glrotate, além de mudar a textura. Esse único VBO você vai usar para todos os tiles.

    Além disso, no renderizador, agrupar os tiles por texturas na hora de desenhar de forma a minimizar as trocas de textura durante o desenho geralmente costuma ajudar, isso eu também implementaria.

    Se nada disso ajudar ou se não chegar num desempenho satisfatório, eu pensaria em fazer um pré processador do nível de tiles e agruparia os tiles que usam a mesma textura ou shader em uma única malha, ao menos os adjacentes, de forma a maximizar as malhas enviadas a GPU e minimizar as drawcalls, mesmo que isso gere um pouco de desenho inutil quando um agrupamento de tiles estiver apenas parcialmente visivel.

    T+

  • Luiz Paulo
    avatar

    Obrigado pela resposta rápida!

    Testei com VBO e DisplayLists. Os dois rodam na mesma velocidade e, novamente, não passam de 700 FPS.
    (Somentes 1 Quad, sem textura, um do lado do outro)
    E também fiz com um em cima do outro, sem dar translate. Mesmo desempenho.

    Quanto a fazer um pre-processamento, não sei se daria certo, pois o terreno seria interativo (mover). Só se eu repetisse a otimização toda vez que um tile fosse empurrado.

    Uma outra coisa, conhece o jogo Minecraft? Os blocos são em quantidade enorme, mas também dinâmicos, o que me faz pensar que são renderizados um por um. Se tiver mais alguma dica, agradeceria :cheer:

  • Bruno Crivelari Sanches
    avatar

    Renderizar dois quads não é uma boa forma de medir desempenho.

    O ideal é ter um nível de testes, que realmente exija do seu HW um pouco mais de trabalho e derrube esse FPS para algo abaixo de 60, a partir desse ponto, otimizações vão começar a mostrar a diferença.

    Eu duvido que o minecraft desenhe cubo a cubo, seriam milhares de drawcall e isso impactaria o desempenho, principalmente com faces ocultas e overdraw.

    No caso do minecraft é voxel, se você quer fazer algo que suporte isso vai ter que pesquisar sobre eles.

    Mas de novo, se você montar um cenário com meia duzia de voxels, desempenho nunca vai se problema, então primeiro, construa algo para testes que realmente exija do seu HW.

    Para minecraft, uma das técnicas que já vi é gerar uma malha a partir da face vísivel dos cubos, dessa forma se otimiza toda a renderização minimizando as drawcalls.

    O problema é até quanto agrupar? No artigo que eu vi (http://www.sauropodstudio.com/how-to-make-a-voxel-base-game-in-unity/ ) eles tem o problema de decidir qual o tamanho de blocos deve-se considerar para agrupar as coisas e isso depende do tamanho do seu cenário e do HW alvo e sim, é feito em tempo real, tem que se encontrar apenas o ponto onde o custo e os ganhos desse processamento superam o custo de enviar tudo para a GPU e deixar ela se virar.

    T+

  • Luiz Paulo
    avatar

    Entendi. Voxel já parece mais complicado.

    E não foi só 1 Quad renderizado. O que eu quis dizer foi o que você sugeriu: Criar apenas 1 Quad e renderizar várias vezes. Foi assim que eu fiz. 700 drawcalls para o mesmo quad.

    Vou ver se pesquiso sobre Voxels e se dá pra tirar alguma coisa para aplicar nos Tiles :)

  • Luiz Paulo
    avatar

    Achei um video no youtube que pode ser uma alternativa pro que eu quero.

    http://www.youtube.com/watch?v=7AL7HtjnibQ&feature=mfu_in_order&lis t=UL

    (Aos 40 segundos mostra o Wireframe)

    No blog do projeto, o autor postou sobre como ele fez o terreno:

    It’s only a simple polygon (in this case a edge loop) subtraction algorithm + box2d fixtures made from those triangulated polygons.

    Pesquisei e não achei nada prático sobre Polygon subtraction Algorithm. Alguém tem algum link sobre esse tópico?
    Obrigado!

  • Gregório Magno Diogo  - OLá
    avatar

    :)
    O tutorial é muito bom :) :) :)

    gostaria de uma ajudinha....
    quero criar um carro!

    Obrigado!

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter