Ponto V!

Home WebGL Um quadrado com Index Buffer
Vinícius Godoy de Mendonça
Um quadrado com Index BufferImprimir
Escrito por Vinícius Godoy de Mendonça

No artigo anterior, vimos como desenhar um triângulo colorido. Nesse artigo, vamos transformar esse triângulo num quadrado, e mostrar como evitar a duplicação desnecessária de vértices através de um segundo array, chamado de Index Buffer.

Do triângulo para o quadrado

Com o conhecimento que temos até agora, se quiséssemos transformar o triângulo num quadrado seria muito simples. Bastaria alterar a função initBuffers para especificar 3 vértices e 3 cores a mais:

var positions = [
    -0.8,  0.8, 0.0,   //Vertice 0
    -0.8, -0.8, 0.0,   //Vertice 1
     0.8, -0.8, 0.0,   //Vertice 2
     
     0.8,  0.8, 0.0,   //Vertice 3
    -0.8,  0.8, 0.0,   //Vertice 4
     0.8, -0.8, 0.0,   //Vertice 5      
];

var vertexColors = [
    1.00, 0.00, 0.00, 1.0, //Vertice 0
    0.00, 1.00, 0.00, 1.0, //Vertice 1
    0.00, 0.00, 1.00, 1.0, //Vertice 2
    
    1.00, 1.00, 1.00, 1.0, //Vertice 3
    1.00, 0.00, 0.00, 1.0, //Vertice 4
    0.00, 0.00, 1.00, 1.0  //Vertice 5      
];

E isso já resultaria na seguinte imagem:

Quadrado 3D

Entretanto, observe os dados referentes aos vértices 0 e 4 e os dados referentes aos vértices 3 e 5. Reparou algo curioso?
Cada um desses pares contém vértices idênticos, com exatamente as mesmas posições e cores! Isso porque eles se referem a área coincidente, na diagonal do quadrado.

Reduzindo duplicidades

Já foi mencionado anteriormente que um dos grandes gargalos das aplicações gráficas é justamente a comunicação entre o processador e a placa de vídeo. Logicamente, graças a isso, projetistas dos pipelines gráficos criaram mecanismos para evitar essa duplicação.

No passado, o mecanismo recomendado para eliminar essa duplicação era o uso de diferentes primitivas gráficas. Algumas delas eram as primitivas TRIANGLE_STRIP e TRIANGLE_FAN. O triangle strip desenha um triângulo para os primeiros três vértices e, a cada novo vértice adicionado, desenha um novo triângulo ligando os dois últimos vértices desenhados ao vértice fornecido. O TRIANGLE_FAN opera de modo similar, mas ao invés de ligar o novo ponto aos dois últimos, liga ao primeiro e o último vértices.

Observe a diferença das três abordagens:

Primitivas de triângulos

Com isso, essas duas novas primitivas poderíamos desenhar o quadrado fornecendo apenas mais um único vértice. Uma ótima saída, certo? Errado.

Essa abordagem acabou se mostrando complicada por diversas razões:

  • Ao desenhar uma malha, é comum que um vértice se repita diversas vezes. Por exemplo, considere uma matriz de 10x10 quadrados. Cada vértice central dessa matriz se repete 4 vezes, uma para cada quadrado vizinho. O triangle strip era capaz de remover apenas 2 dessas duplicações;
  • Era difícil fazer algoritmos que gerassem bons strips e fans. Isso era especialmente problemático para desenvolvedores de programas de modelagem 3D;
  • As demais primitivas não eram tão otimizadas quanto o desenho de triângulos individuais.

Para resolver esse problema, uma abordagem diferente tornou-se popular durante os anos. Podemos especificar os vértices uma única vez, e colocá-los num array. Em seguida, podemos criar um segundo array, explicando apenas quais vértices serão usados no desenho de cada triângulo. Esse array secundário ficou conhecido como index buffer ou element array buffer. Veja como organizaríamos a malha (o eixo z foi omitido por simplicidade):


Vertices e índices

Mas você pode estar se perguntando: Eu reduzi a quantidade de vértices, mas ao custo de um array inteiro! Isso é vantajoso? A resposta é sim!

  • Cada índice ocupa apenas 2 bytes. Os vértices são formados por 3 posições e 4 componentes de cor, cada um float de 4 bytes, ou seja (3+4)*4=28 bytes! Em nosso exemplo, se somarmos todos os 6 índices criados teremos um gasto de 12 bytes. Os dois vértices poupados nos economizaram 56 bytes, gerando um resultado final de 44 bytes a menos. Numa malha mais complexa, o vértice ainda teria informação adicional de normais de luz e coordenadas de textura.
  • Um mesmo vértice pode ser repetido diversas vezes. Em malhas mais complexas, essa economia pode representar menos espaço do que o uso de fans e strips.

Além da economia, esse array tem outras vantagens:

  • É mais fácil gerar index arrays proceduralmente.
  • Os index arrays também contribuem para outras técnicas, como a identificação de vértices adjacentes, geração de normais de luz suavizadas, etc.

Index buffer em código

Vamos começar alterando o nosso método initBuffers para incluir também o index buffer:

function initBuffers() {
    //Criação do buffer de vértices
    var positions = [
        -0.8,  0.8, 0.0,   //Vertice 0
         0.8,  0.8, 0.0,   //Vertice 1
        -0.8, -0.8, 0.0,   //Vertice 2
         0.8, -0.8, 0.0,   //Vertice 3                  
    ];
    
    vertices = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    vertices.itemSize = 3;
    vertices.numItems = 4;    
    
    //Criação do buffer de cores
    var vertexColors = [
        1.00, 0.00, 0.00, 1.0,
        1.00, 1.00, 1.00, 1.0,
        0.00, 1.00, 0.00, 1.0,
        0.00, 0.00, 1.00, 1.0,
    ];
    
    colors = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colors);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexColors), gl.STATIC_DRAW);
    colors.itemSize = 4;
    colors.numItems = 4;   

    //Criação do Index Buffer
    var indexBuffer = [
       0, 2, 3,
       0, 3, 1
    ];
    
    indices = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexBuffer), gl.STATIC_DRAW);
    indices.itemSize = 1;
    indices.numItems = 6;
}

Agora, basta alterar o método de desenho para considerar a existência dos índices. Para isso, precisamos:

  • Associar todos os arrays, inclusive o de índices;
  • Usar o comando drawElements no lugar do comando drawArrays para fazer o desenho.
//Associa os buffers que serao usados no desenho    
gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
gl.vertexAttribPointer(shaderProgram.aVertexPosition, vertices.itemSize, gl.FLOAT, false, 0, 0);
    
gl.bindBuffer(gl.ARRAY_BUFFER, colors);
gl.vertexAttribPointer(shaderProgram.aVertexColor, colors.itemSize, gl.FLOAT, false, 0, 0);    

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);    

//Comanda o desenho
gl.drawElements(gl.TRIANGLES, indices.numItems, gl.UNSIGNED_SHORT, 0);

Desafio

Você conseguiria alterar essa quadrado para desenhar um chão, formado por diversos quadrados? Vamos as regras:

  • Todos os quadrados do chão estarão no plano (x, z);
  • O chão deve ser centralizado no ponto (0,0) – respeitando as coordenadas do modelo
  • A função que cria a malha do chão deve receber como parâmetro o número de vértices nas linhas e colunas;
  • Cada quadrado terá o tamanho de 1x1
  • Não preocupe com cores, os quadrados podem ser todos brancos.

Essa malha é muito importante. Sobre ela, podem ser feitos diversos efeitos como água ou terrenos. Nós veremos a resposta desse desafio no futuro, quando terminarmos algumas refatorações nesse código.

Download

Nesse artigo você aprendeu como desenhar um quadrado e otimizá-lo através da técnica de Index Buffers. No próximo artigo, veremos como organizar um pouco o código.

Você pode fazer o download do código completo deste artigo clicando no link abaixo:

Download do exemplo

Até a próxima!


Comentários (0)
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