WebGL - Guia rápido

Alguns anos atrás, os aplicativos Java - como uma combinação de miniaplicativos e JOGL - eram usados ​​para processar gráficos 3D na Web, endereçando a GPU (Graphical Processing Unit). Como os miniaplicativos requerem uma JVM para serem executados, tornou-se difícil contar com miniaplicativos Java. Alguns anos depois, as pessoas pararam de usar miniaplicativos Java.

As APIs Stage3D fornecidas pela Adobe (Flash, AIR) ofereciam arquitetura acelerada por hardware de GPU. Usando essas tecnologias, os programadores podem desenvolver aplicativos com recursos 2D e 3D em navegadores da web, bem como em plataformas IOS e Android. Como o Flash era um software proprietário, não era usado como padrão da web.

Em março de 2011, o WebGL foi lançado. É um software aberto que pode ser executado sem JVM. É totalmente controlado pelo navegador da web.

A nova versão do HTML 5 possui vários recursos para oferecer suporte a gráficos 3D, como 2D Canvas, WebGL, SVG, transformações 3D CSS e SMIL. Neste tutorial, cobriremos os fundamentos do WebGL.

O que é OpenGL?

OpenGL (Open Graphics Library) é uma API de linguagem cruzada e plataforma cruzada para gráficos 2D e 3D. É uma coleção de comandos. OpenGL4.5 é a versão mais recente do OpenGL. A tabela a seguir lista um conjunto de tecnologias relacionadas ao OpenGL.

API Tecnologia Usada
OpenGL ES É a biblioteca de gráficos 2D e 3D em sistemas embarcados - incluindo consoles, telefones, aparelhos e veículos. OpenGL ES 3.1 é sua versão mais recente. É mantido pelo Grupo Khronos www.khronos.org
JOGL É a vinculação Java para OpenGL. JOGL 4.5 é sua versão mais recente e é mantida por jogamp.org.
WebGL É a vinculação do JavaScript para OpenGL. WebGL 1.0 é sua versão mais recente e é mantida pelo grupo khronos .
OpenGLSL OpenGL Shading Language. É uma linguagem de programação que acompanha o OpenGL 2.0 e superior. É uma parte da especificação central do OpenGL 4.4. É uma API feita sob medida para sistemas embarcados, como aqueles presentes em telefones celulares e tablets.

Note - No WebGL, usamos GLSL para escrever shaders.

O que é WebGL?

WebGL (Web Graphics Library) é o novo padrão para gráficos 3D na Web. Ele foi projetado com a finalidade de renderizar gráficos 2D e gráficos 3D interativos. É derivado da biblioteca ES 2.0 do OpenGL, que é uma API 3D de baixo nível para telefones e outros dispositivos móveis. O WebGL fornece funcionalidade semelhante ao ES 2.0 (Sistemas Embarcados) e tem um bom desempenho em hardware gráfico 3D moderno.

É uma API JavaScript que pode ser usada com HTML5. O código WebGL é escrito na tag <canvas> do HTML5. É uma especificação que permite que os navegadores da Internet acessem as unidades de processamento gráfico (GPUs) nos computadores em que foram usados.

Quem desenvolveu WebGL

Um engenheiro de software sérvio-americano chamado Vladimir Vukicevic fez o trabalho de base e liderou a criação do WebGL

  • Em 2007, Vladimir começou a trabalhar em um OpenGL protótipo do elemento Canvas do documento HTML.

  • Em março de 2011, o Grupo Kronos criou o WebGL.

Renderização

Renderização é o processo de gerar uma imagem a partir de um modelo usando programas de computador. Em gráficos, uma cena virtual é descrita usando informações como geometria, ponto de vista, textura, iluminação e sombreamento, que são passadas por um programa de renderização. A saída deste programa de renderização será uma imagem digital.

Existem dois tipos de renderização -

  • Software Rendering - Todos os cálculos de renderização são feitos com a ajuda da CPU.

  • Hardware Rendering - Todos os cálculos gráficos são feitos pela GPU (unidade de processamento gráfico).

A renderização pode ser feita local ou remotamente. Se a imagem a ser renderizada for muito complexa, a renderização será feita remotamente em um servidor dedicado com recursos de hardware suficientes para renderizar cenas complexas. Também é chamado deserver-based rendering. A renderização também pode ser feita localmente pela CPU. É chamado declient-based rendering.

WebGL segue uma abordagem de renderização baseada em cliente para renderizar cenas 3D. Todo o processamento necessário para obter uma imagem é executado localmente, usando o hardware gráfico do cliente.

GPU

De acordo com a NVIDIA, uma GPU é "um processador de chip único com transformação integrada, iluminação, configuração / recorte de triângulo e mecanismos de renderização capazes de processar um mínimo de 10 milhões de polígonos por segundo". Ao contrário dos processadores multi-core com alguns núcleos otimizados para processamento sequencial, uma GPU consiste em milhares de núcleos menores que processam cargas de trabalho paralelas com eficiência. Portanto, a GPU acelera a criação de imagens em um buffer de quadros (uma parte da memória RAM que contém os dados de um quadro completo) destinadas a serem enviadas para um display.

GPU Accelerated Computing

Na computação acelerada por GPU, o aplicativo é carregado na CPU. Sempre que encontra umcompute-intensiveparte do código, essa parte do código será carregada e executada na GPU. Ele dá ao sistema a capacidade de processar gráficos de maneira eficiente.

A GPU terá uma memória separada e executa várias cópias de uma pequena parte do código por vez. A GPU processa todos os dados que estão em sua memória local, não a memória central. Portanto, os dados que precisam ser processados ​​pela GPU devem ser carregados / copiados para a memória da GPU e depois processados.

Nos sistemas com a arquitetura acima, a sobrecarga de comunicação entre a CPU e a GPU deve ser reduzida para obter um processamento mais rápido de programas 3D. Para isso, temos que copiar todos os dados e mantê-los na GPU, em vez de nos comunicarmos com a GPU repetidamente.

Navegadores suportados

As tabelas a seguir mostram uma lista de navegadores que suportam WebGL -

Navegadores da web

Nome do navegador Versão Apoio, suporte
IInternet Explorer 11 e acima Suporte completo
Google Chrome 39 e acima Suporte completo
Safári 8 Suporte completo
Raposa de fogo 36 e acima Suporte parcial
Ópera 27 e acima Suporte parcial

Navegadores móveis

Nome do navegador Versão Apoio, suporte
Chrome para Android 42 Suporte parcial
Navegador Android 40 Suporte parcial
IOS Safari 8,3 Suporte completo
Opera Mini 8 Não suporta
Navegador blackberry 10 Suporte completo
IE mobile 10 Suporte parcial

Vantagens do WebGL

Aqui estão as vantagens de usar WebGL -

  • JavaScript programming- Os aplicativos WebGL são escritos em JavaScript. Usando esses aplicativos, você pode interagir diretamente com outros elementos do documento HTML. Você também pode usar outras bibliotecas JavaScript (por exemplo, JQuery) e tecnologias HTML para enriquecer o aplicativo WebGL.

  • Increasing support with mobile browsers - WebGL também oferece suporte a navegadores móveis, como iOS safari, navegador Android e Chrome para Android.

  • Open source- WebGL é um código aberto. Você pode acessar o código-fonte da biblioteca e entender como funciona e como foi desenvolvido.

  • No need for compilation- JavaScript é um componente metade programação e metade HTML. Para executar este script, não há necessidade de compilar o arquivo. Em vez disso, você pode abrir o arquivo diretamente usando qualquer um dos navegadores e verificar o resultado. Como os aplicativos WebGL são desenvolvidos usando JavaScript, não há necessidade de compilar aplicativos WebGL também.

  • Automatic memory management- JavaScript suporta gerenciamento automático de memória. Não há necessidade de alocação manual de memória. O WebGL herda esse recurso do JavaScript.

  • Easy to set up- Como o WebGL é integrado ao HTML 5, não há necessidade de configuração adicional. Para escrever um aplicativo WebGL, tudo o que você precisa é de um editor de texto e um navegador da web.

Configuração de ambiente

Não há necessidade de definir um ambiente diferente para WebGL. Os navegadores que suportam WebGL têm sua própria configuração embutida para WebGL.

Para criar aplicativos gráficos na web, o HTML-5 fornece um rico conjunto de recursos, como 2D Canvas, WebGL, SVG, transformações 3D CSS e SMIL. Para escrever aplicativos WebGL, usamos o elemento canvas existente do HTML-5. Este capítulo fornece uma visão geral do elemento de tela 2D do HTML-5.

HTML5 Canvas

HTML-5 <canvas>fornece uma opção fácil e poderosa para desenhar gráficos usando JavaScript. Ele pode ser usado para desenhar gráficos, fazer composições de fotos ou fazer animações simples (e não tão simples).

Aqui é um simples <canvas> elemento tendo apenas dois atributos específicos width e height além de todos os principais atributos do HTML-5, como id, nome e classe.

Sintaxe

A sintaxe da tag de tela HTML é fornecida abaixo. Você deve mencionar o nome da tela entre aspas duplas (“”).

<canvas id = "mycanvas" width = "100" height = "100"></canvas>

Atributos da tela

A tag canvas tem três atributos, a saber, id, largura e altura.

  • Id- Id representa o identificador do elemento canvas no Document Object Model (DOM) .

  • Width - Largura representa a largura da tela.

  • Height - A altura representa a altura da tela.

Esses atributos determinam o tamanho da tela. Se um programador não os estiver especificando na tag canvas, navegadores como Firefox, Chrome e Web Kit, por padrão, fornecem um elemento canvas de tamanho 300 × 150.

Exemplo - Criar uma tela

O código a seguir mostra como criar uma tela. Usamos CSS para dar uma borda colorida à tela.

<html>
   <head>
      <style>
         #mycanvas{border:1px solid red;}
      </style>
   </head>
   <body>
      <canvas id = "mycanvas" width = "100" height = "100"></canvas>
   </body>
</html>

Ao ser executado, o código acima produzirá a seguinte saída -

O contexto de renderização

O <canvas> está inicialmente em branco. Para exibir algo no elemento canvas, temos que usar uma linguagem de script. Esta linguagem de script deve acessar o contexto de renderização e desenhar nele.

O elemento canvas tem um método DOM chamado getContext(), que é usado para obter o contexto de renderização e suas funções de desenho. Este método usa um parâmetro, o tipo de contexto2d.

O código a seguir deve ser escrito para obter o contexto necessário. Você pode escrever este script dentro da tag body conforme mostrado abaixo.

<!DOCTYPE HTML>
<html>
   <body>
      <canvas id = "mycanvas" width = "600" height = "200"></canvas>

      <script>
         var canvas = document.getElementById('mycanvas');
         var context = canvas.getContext('2d');
			
         context.font = '20pt Calibri';
         context.fillStyle = 'green';
         context.fillText('Welcome to Tutorialspoint', 70, 70);
      </script>
   </body>
</html>

Ao ser executado, o código acima produzirá a seguinte saída -

Para obter mais exemplos sobre HTML-5 2D Canvas, verifique o seguinte link HTML-5 Canvas .

Contexto WebGL

O HTML5 Canvas também é usado para escrever aplicativos WebGL. Para criar um contexto de renderização WebGL no elemento canvas, você deve passar a stringexperimental-webgl, ao invés de 2d ao canvas.getContext()método. Alguns navegadores suportam apenas 'webgl'.

<!DOCTYPE html>
<html>
   <canvas id = 'my_canvas'></canvas>
	
   <script>
      var canvas = document.getElementById('my_canvas');
      var gl = canvas.getContext('experimental-webgl');
      gl.clearColor(0.9,0.9,0.8,1);
      gl.clear(gl.COLOR_BUFFER_BIT);
   </script>
</html>

Ao ser executado, o código acima produzirá a seguinte saída -

WebGL é principalmente uma API de rasterização de baixo nível, em vez de uma API 3D. Para desenhar uma imagem usando WebGL, você deve passar um vetor que representa a imagem. Ele então converte o vetor fornecido em formato de pixel usando OpenGL SL e exibe a imagem na tela. Escrever um aplicativo WebGL envolve um conjunto de etapas que explicaremos neste capítulo.

WebGL - Sistema de Coordenadas

Assim como qualquer outro sistema 3D, você terá os eixos x, y e z no WebGL, onde o z eixo significa depth. As coordenadas em WebGL são restritas a (1, 1, 1) e (-1, -1, - 1). Isso significa que - se você considerar a tela projetando gráficos WebGL como um cubo, então um canto do cubo será (1, 1, 1) e o canto oposto será (-1, -1, -1). O WebGL não exibirá nada desenhado além desses limites.

O diagrama a seguir descreve o sistema de coordenadas WebGL. O eixo z significa profundidade. Um valor positivo de z indica que o objeto está perto da tela / visualizador, enquanto um valor negativo de z indica que o objeto está longe da tela. Da mesma forma, um valor positivo de x indica que o objeto está à direita da tela e um valor negativo indica que o objeto está à esquerda. Da mesma forma, os valores positivos e negativos de y indicam se o objeto está no topo ou na parte inferior da tela.

Gráficos WebGL

Depois de obter o contexto WebGL do objeto de tela, você pode começar a desenhar elementos gráficos usando a API WebGL em JavaScript.

Aqui estão alguns termos fundamentais que você precisa saber antes de começar com WebGL.

Vértices

Geralmente, para desenhar objetos como um polígono, marcamos os pontos no plano e os unimos para formar o polígono desejado. UMAvertexé um ponto que define a conjunção das bordas de um objeto 3D. É representado por três valores de ponto flutuante, cada um representando os eixos x, y, z respectivamente.

Exemplo

No exemplo a seguir, estamos desenhando um triângulo com os seguintes vértices - (0,5, 0,5), (-0,5, 0,5), (-0,5, -0,5).

Note - Temos que armazenar esses vértices manualmente usando matrizes JavaScript e passá-los para o pipeline de renderização WebGL usando o buffer de vértices.

Índices

No WebGL, os valores numéricos são usados ​​para identificar os vértices. Esses valores numéricos são conhecidos como índices. Esses índices são usados ​​para desenhar malhas em WebGL.

Note - Assim como os vértices, armazenamos os índices usando arrays JavaScript e os passamos para o pipeline de renderização WebGL usando o buffer de índice.

Arrays

Ao contrário do OpenGL e JoGL, não existem métodos predefinidos no WebGL para renderizar os vértices diretamente. Temos que armazená-los manualmente usando matrizes JavaScript.

Exemplo

var vertices = [ 0.5, 0.5, 0.1,-0.5, 0.5,-0.5]

Buffers

Buffers são as áreas de memória do WebGL que armazenam os dados. Existem vários buffers, a saber, buffer de desenho, buffer de quadro, buffer vetex e buffer de índice. overtex buffer e index buffer são usados ​​para descrever e processar a geometria do modelo.

Os objetos de buffer de vértices armazenam dados sobre os vértices, enquanto os objetos de buffer de índice armazenam dados sobre os índices. Depois de armazenar os vértices em arrays, nós os passamos para o pipeline gráfico WegGL usando esses objetos Buffer.

Frame bufferé uma parte da memória gráfica que contém os dados da cena. Este buffer contém detalhes como largura e altura da superfície (em pixels), cor de cada pixel, profundidade e buffers de estêncil.

Malha

Para desenhar objetos 2D ou 3D, a API WebGL fornece dois métodos, a saber, drawArrays() e drawElements(). Esses dois métodos aceitam um parâmetro chamadomodeusando o qual você pode selecionar o objeto que deseja desenhar. As opções fornecidas por este campo são restritas a pontos, linhas e triângulos.

Para desenhar um objeto 3D usando esses dois métodos, temos que construir um ou mais polígonos primitivos usando pontos, linhas ou triângulos. Depois disso, usando esses polígonos primitivos, podemos formar uma malha.

Um objeto 3D desenhado usando polígonos primitivos é chamado de mesh. O WebGL oferece várias maneiras de desenhar objetos gráficos 3D, mas os usuários normalmente preferem desenhar uma malha.

Exemplo

No exemplo a seguir, você pode observar que desenhamos um quadrado usando dois triângulos → {1, 2, 3} e {4, 1, 3}.

Programas Shader

Normalmente usamos triângulos para construir malhas. Como o WebGL usa computação acelerada por GPU, as informações sobre esses triângulos devem ser transferidas da CPU para a GPU, o que exige uma grande sobrecarga de comunicação.

O WebGL fornece uma solução para reduzir a sobrecarga de comunicação. Uma vez que usa ES SL (Embedded System Shader Language) que é executado em GPU, escrevemos todos os programas necessários para desenhar elementos gráficos no sistema cliente usandoshader programs (os programas que escrevemos usando OpenGL ES Shading Language / GLSL)

Esses shaders são os programas para GPU e a linguagem usada para escrever programas de shaders é GLSL. Nesses sombreadores, definimos exatamente como vértices, transformações, materiais, luzes e câmera interagem entre si para criar uma imagem específica.

Resumindo, é um snippet que implementa algoritmos para obter pixels para uma malha. Discutiremos mais sobre sombreadores em capítulos posteriores. Existem dois tipos de shaders - Vertex Shader e Fragment Shader.

Vertex Shader

Vertex shader é o código do programa chamado em cada vértice. É usado para transformar (mover) a geometria (ex: triângulo) de um lugar para outro. Ele lida com os dados de cada vértice (dados por vértice), como coordenadas de vértice, normais, cores e coordenadas de textura.

No ES GLcódigo do sombreador de vértice, os programadores têm que definir atributos para lidar com os dados. Esses atributos apontam para umVertex Buffer Object escrito em JavaScript.

As seguintes tarefas podem ser realizadas usando sombreadores de vértice -

  • Transformação de vértice
  • Transformação normal e normalização
  • Geração de coordenadas de textura
  • Transformação de coordenadas de textura
  • Lighting
  • Aplicação de material de cor

Fragment Shader (Pixel Shader)

Uma malha é formada por múltiplos triângulos, e a superfície de cada um dos triângulos é conhecida como fragment. Fragment shader é o código executado em todos os pixels de cada fragmento. Ele foi escrito para calcular e preencher a cor em pixels individuais .

As tarefas a seguir podem ser realizadas usando Fragment shaders -

  • Operações em valores interpolados
  • Acesso à textura
  • Aplicação de textura
  • Fog
  • Soma de cores

Variáveis ​​OpenGL ES SL

A forma completa de OpenGL ES SLé OpenGL Embedded System Shading Language. Para lidar com os dados nos programas de sombreador, o ES SL oferece três tipos de variáveis. Eles são os seguintes -

  • Attributes- Essas variáveis ​​contêm os valores de entrada do programa de sombreador de vértice. Os atributos apontam para os objetos de buffer de vértice que contêm dados por vértice. Cada vez que o sombreador de vértice é chamado, os atributos apontam para VBO de vértices diferentes.

  • Uniforms - Essas variáveis ​​contêm os dados de entrada que são comuns para sombreadores de vértice e fragmento, como posição da luz, coordenadas de textura e cor.

  • Varyings - Essas variáveis ​​são usadas para passar os dados do sombreador de vértice para o sombreador de fragmento.

Com todos esses fundamentos, iremos agora discutir o Pipeline de Gráficos.

Para renderizar gráficos 3D, temos que seguir uma sequência de etapas. Essas etapas são conhecidas comographics pipeline ou rendering pipeline. O diagrama a seguir descreve o pipeline de gráficos WebGL.

Nas seções a seguir, discutiremos uma a uma a função de cada etapa do pipeline.

JavaScript

Ao desenvolver aplicativos WebGL, escrevemos o código da linguagem Shader para nos comunicarmos com a GPU. JavaScript é usado para escrever o código de controle do programa, que inclui as seguintes ações -

  • Initialize WebGL - JavaScript é usado para inicializar o contexto WebGL.

  • Create arrays - Criamos arrays JavaScript para armazenar os dados da geometria.

  • Buffer objects - Criamos objetos de buffer (vértice e índice) passando os arrays como parâmetros.

  • Shaders - Criamos, compilamos e vinculamos os shaders usando JavaScript.

  • Attributes - Podemos criar atributos, ativá-los e associá-los a objetos de buffer usando JavaScript.

  • Uniforms - Também podemos associar os uniformes usando JavaScript.

  • Transformation matrix - Usando JavaScript, podemos criar uma matriz de transformação.

Inicialmente, criamos os dados para a geometria necessária e os passamos para os sombreadores na forma de buffers. A variável de atributo da linguagem do sombreador aponta para os objetos de buffer, que são passados ​​como entradas para o sombreador de vértice.

Vertex Shader

Quando iniciamos o processo de renderização invocando os métodos drawElements() e drawArray(), o sombreador de vértice é executado para cada vértice fornecido no objeto de buffer de vértice. Ele calcula a posição de cada vértice de um polígono primitivo e o armazena em diferentesgl_position. Ele também calcula os outros atributos, comocolor, texture coordinates, e vertices que normalmente estão associados a um vértice.

Conjunto primitivo

Depois de calcular a posição e outros detalhes de cada vértice, a próxima fase é a primitive assembly stage. Aqui, os triângulos são montados e passados ​​para o rasterizador.

Rasterização

Na etapa de rasterização, os pixels da imagem final da primitiva são determinados. Tem duas etapas -

  • Culling- Inicialmente é determinada a orientação (é para frente ou para trás?) Do polígono. Todos aqueles triângulos com orientação inadequada que não são visíveis na área de visualização são descartados. Esse processo é chamado de seleção.

  • Clipping- Se um triângulo estiver parcialmente fora da área de visualização, a parte fora da área de visualização será removida. Esse processo é conhecido como recorte.

Fragment Shader

O sombreador de fragmento obtém

  • dados do sombreador de vértice em variáveis ​​variáveis,
  • primitivas do estágio de rasterização, e então
  • calcula os valores de cor para cada pixel entre os vértices.

O sombreador de fragmento armazena os valores de cor de cada pixel em cada fragmento. Esses valores de cor podem ser acessados ​​durante as operações de fragmento, que discutiremos a seguir.

Operações de Fragmentação

As operações de fragmentação são realizadas após determinar a cor de cada pixel na primitiva. Essas operações de fragmento podem incluir o seguinte -

  • Depth
  • Mistura de tampão de cor
  • Dithering

Uma vez que todos os fragmentos são processados, uma imagem 2D é formada e exibida na tela. oframe buffer é o destino final do pipeline de renderização.

Suavizador de quadros

O buffer de quadros é uma parte da memória gráfica que contém os dados da cena. Esse buffer contém detalhes como largura e altura da superfície (em pixels), cor de cada pixel e buffers de profundidade e estêncil.

Discutimos os fundamentos do WebGL e do pipeline do WebGL (um procedimento seguido para renderizar aplicativos gráficos). Neste capítulo, vamos pegar um aplicativo de amostra para criar um triângulo usando WebGL e observar as etapas seguidas no aplicativo.

Estrutura do aplicativo WebGL

O código do aplicativo WebGL é uma combinação de JavaScript e OpenGL Shader Language.

  • JavaScript é necessário para se comunicar com a CPU
  • O OpenGL Shader Language é necessário para se comunicar com a GPU.

Aplicativo de amostra

Vamos agora dar um exemplo simples para aprender como usar WebGL para desenhar um triângulo simples com coordenadas 2D.

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>
		
      <script>
         /* Step1: Prepare the canvas and get WebGL context */

         var canvas = document.getElementById('my_Canvas');
         var gl = canvas.getContext('experimental-webgl');

         /* Step2: Define the geometry and store it in buffer objects */

         var vertices = [-0.5, 0.5, -0.5, -0.5, 0.0, -0.5,];

         // Create a new buffer object
         var vertex_buffer = gl.createBuffer();

         // Bind an empty array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         
         // Pass the vertices data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         /* Step3: Create and compile Shader programs */

         // Vertex shader source code
         var vertCode =
            'attribute vec2 coordinates;' + 
            'void main(void) {' + ' gl_Position = vec4(coordinates,0.0, 1.0);' + '}';

         //Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         //Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         //Compile the vertex shader
         gl.compileShader(vertShader);

         //Fragment shader source code
         var fragCode = 'void main(void) {' + 'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}';

         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragment shader
         gl.compileShader(fragShader);

         // Create a shader program object to store combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader); 
         
         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /* Step 4: Associate the shader programs to buffer objects */

         //Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         //Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         //point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0);

         //Enable the attribute
         gl.enableVertexAttribArray(coord);

         /* Step5: Drawing the required object (triangle) */

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST); 
         
         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
   </body>
</html>

Isso produzirá o seguinte resultado -

Se você observar o programa acima com atenção, seguimos cinco etapas sequenciais para desenhar um triângulo simples usando WebGL. As etapas são as seguintes -

Step 1 − Prepare the canvas and get WebGL rendering context

Obtemos o objeto de tela HTML atual e seu contexto de renderização WebGL.

Step 2 − Define the geometry and store it in buffer objects

Definimos os atributos da geometria, como vértices, índices, cor, etc., e os armazenamos nos arrays JavaScript. Em seguida, criamos um ou mais objetos de buffer e passamos os arrays contendo os dados para o respectivo objeto de buffer. No exemplo, armazenamos os vértices do triângulo em um array JavaScript e passamos esse array para um objeto buffer de vértices.

Step 3 − Create and compile Shader programs

Nós escrevemos programas shader de vértice e shader de fragmento, os compilamos e criamos um programa combinado ligando esses dois programas.

Step 4 − Associate the shader programs with buffer objects

Associamos os objetos de buffer e o programa de sombreador combinado.

Step 5 − Drawing the required object (triangle)

Esta etapa inclui operações como limpar a cor, limpar o bit de buffer, habilitar o teste de profundidade, definir a porta de visualização, etc. Finalmente, você precisa desenhar os primitivos necessários usando um dos métodos - drawArrays() ou drawElements().

Todas essas etapas são explicadas mais adiante neste tutorial.

Para escrever um aplicativo WebGL, a primeira etapa é obter o objeto de contexto de renderização WebGL. Este objeto interage com o buffer de desenho WebGL e pode chamar todos os métodos WebGL. As seguintes operações são realizadas para obter o contexto WebGL -

  • Crie uma tela HTML-5
  • Obtenha o ID da tela
  • Obtenha WebGL

Criação de elemento HTML-5 Canvas

No Capítulo 5, discutimos como criar um elemento de tela HTML-5. No corpo do documento HTML-5, escreva uma tela, dê um nome a ela e passe-a como um parâmetro para o id do atributo. Você pode definir as dimensões da tela usando os atributos de largura e altura (opcional).

Exemplo

O exemplo a seguir mostra como criar um elemento de tela com as dimensões 500 × 500. Criamos uma borda para a tela usando CSS para visibilidade. Copie e cole o seguinte código em um arquivo com o nomemy_canvas.html.

<!DOCTYPE HTML>
<html>
   <head>
      <style>
         #mycanvas{border:1px solid blue;}
      </style>
   </head>
   <body>
      <canvas id = "mycanvas" width = "300" height = "300"></canvas>
   </body>
</html>

Isso produzirá o seguinte resultado -

Obtenha o ID da tela

Depois de criar a tela, você deve obter o contexto WebGL. A primeira coisa a fazer para obter um contexto de desenho WebGL é obter o id do elemento de tela atual.

O ID da tela é adquirido chamando o método DOM (Document Object Model) getElementById(). Este método aceita um valor de string como parâmetro, então passamos o nome da tela atual para ele.

Por exemplo, se o nome da tela for my_canvas, então o ID da tela é obtido conforme mostrado abaixo−

var canvas = document.getElementById('my_Canvas');

Obtenha o contexto de desenho WebGL

Para obter o objeto WebGLRenderingContext (ou objeto de contexto de desenho WebGL ou simplesmente contexto WebGL), chame o getContext() método da corrente HTMLCanvasElement. A sintaxe de getContext () é a seguinte -

canvas.getContext(contextType, contextAttributes);

Passe as cordas webgl ou experimental-webgl Enquanto o contentType. ocontextAttributesparâmetro é opcional. (Ao prosseguir com esta etapa, certifique-se de que seu navegador implemente WebGL versão 1 (OpenGL ES 2.0)).

O fragmento de código a seguir mostra como obter o contexto de renderização WebGL. Aquigl é a variável de referência para o objeto de contexto obtido.

var canvas = document.getElementById('my_Canvas');
var gl = canvas.getContext('experimental-webgl');

WebGLContextAttributes

O parâmetro WebGLContextAttributesnão é obrigatório. Este parâmetro fornece várias opções que aceitam valores booleanos conforme listado abaixo -

Sr. Não. Atributos e descrição
1

Alpha

Se seu valor for verdadeiro, ele fornece um buffer alfa para a tela.

Por padrão, seu valor é verdadeiro.

2

depth

Se o valor for verdadeiro, você obterá um buffer de desenho que contém um buffer de profundidade de pelo menos 16 bits.

Por padrão, seu valor é verdadeiro.

3

stencil

Se o valor for true, você obterá um buffer de desenho que contém um buffer de estêncil de pelo menos 8 bits.

Por padrão, seu valor é falso.

4

antialias

Se o valor for true, você obterá um buffer de desenho que executa o anti-aliasing.

Por padrão, seu valor é verdadeiro.

5

premultipliedAlpha

Se o valor for verdadeiro, você obterá um buffer de desenho que contém cores com alfa pré-multiplicado.

Por padrão, seu valor é verdadeiro.

6

preserveDrawingBuffer

Se o valor for verdadeiro, os buffers não serão limpos e preservarão seus valores até que sejam limpos ou substituídos pelo autor.

Por padrão, seu valor é falso.

O trecho de código a seguir mostra como criar um contexto WebGL com um buffer de estêncil, o que não funcionará anti-aliasing.

var canvas = document.getElementById('canvas1');
var context = canvas.getContext('webgl', { antialias: false, stencil: true });

No momento da criação do WebGLRenderingContext, um buffer de desenho é criado. O objeto Context gerencia o estado OpenGL e renderiza no buffer de desenho.

WebGLRenderingContext

É a interface principal do WebGL. Ele representa o contexto de desenho do WebGL. Esta interface contém todos os métodos usados ​​para realizar várias tarefas no buffer de desenho. Os atributos desta interface são fornecidos na tabela a seguir.

Sr. Não. Atributos e descrição
1

Canvas

Esta é uma referência ao elemento canvas que criou este contexto.

2

drawingBufferWidth

Este atributo representa a largura real do buffer de desenho. Pode ser diferente do atributo de largura do HTMLCanvasElement.

3

drawingBufferHeight

Este atributo representa a altura real do buffer de desenho. Pode ser diferente do atributo de altura do HTMLCanvasElement.

Depois de obter o contexto WebGL, você deve definir a geometria da primitiva (objeto que deseja desenhar) e armazená-la. No WebGL, definimos os detalhes de uma geometria - por exemplo, vértices, índices, cor da primitiva - usando matrizes JavaScript. Para passar esses detalhes aos programas de sombreador, temos que criar os objetos de buffer e armazenar (anexar) os arrays JavaScript que contêm os dados nos respectivos buffers.

Note: Posteriormente, esses objetos de buffer serão associados aos atributos do programa de sombreador (vertex shader).

Definindo a geometria necessária

Um modelo 2D ou 3D desenhado usando vértices é chamado de mesh. Cada faceta em uma malha é chamada depolygon e um polígono é feito de 3 ou mais vértices.

Para desenhar modelos no contexto de renderização WebGL, você deve definir os vértices e índices usando matrizes JavaScript. Por exemplo, se quisermos criar um triângulo que se encontra nas coordenadas {(5,5), (-5,5), (-5, -5)} conforme mostrado no diagrama, então você pode criar uma matriz para os vértices como -

var vertices = [
   0.5,0.5,    //Vertex 1
   0.5,-0.5,   //Vertex 2
   -0.5,-0.5,  //Vertex 3
];

Da mesma forma, você pode criar uma matriz para os índices. Os índices para os índices triangulares acima serão [0, 1, 2] e podem ser definidos como -

var indices = [ 0,1,2 ]

Para uma melhor compreensão dos índices, considere modelos mais complexos como o quadrado. Podemos representar um quadrado como um conjunto de dois triângulos. Se (0,3,1) e (3,1,2) são os dois triângulos com os quais pretendemos desenhar um quadrado, então os índices serão definidos como -

var indices = [0,3,1,3,1,2];

Note -

Para desenhos primitivos, o WebGL fornece os dois métodos a seguir -

  • drawArrays() - Ao usar este método, passamos os vértices da primitiva usando arrays JavaScript.

  • drawElements() - Ao usar este método, passamos vértices e índices da primitiva usando o array JavaScript.

Objetos Buffer

Um objeto buffer é um mecanismo fornecido pelo WebGL que indica uma área de memória alocada no sistema. Nestes objetos de buffer, você pode armazenar dados do modelo que deseja desenhar, correspondendo a vértices, índices, cor, etc.

Usando esses objetos de buffer, você pode passar vários dados para o programa de sombreador (sombreador de vértice) por meio de uma de suas variáveis ​​de atributo. Como esses objetos de buffer residem na memória da GPU, eles podem ser renderizados diretamente, o que, por sua vez, melhora o desempenho.

Para processar geometria, existem dois tipos de objetos de buffer. Eles são -

  • Vertex buffer object (VBO)- Ele contém os dados por vértice do modelo gráfico que será renderizado. Usamos objetos de buffer de vértice em WebGL para armazenar e processar os dados relativos aos vértices, como coordenadas de vértices, normais, cores e coordenadas de textura.

  • Index buffer objects (IBO) - Contém os índices (dados de índice) do modelo gráfico que será renderizado.

Depois de definir a geometria necessária e armazená-los em matrizes JavaScript, você precisa passar essas matrizes para os objetos de buffer, de onde os dados serão passados ​​para os programas de sombreador. As etapas a seguir devem ser seguidas para armazenar dados nos buffers.

  • Crie um buffer vazio.

  • Vincule um objeto de array apropriado ao buffer vazio.

  • Passe os dados (vértices / índices) para o buffer usando um dos typed arrays.

  • Desvincule o buffer (opcional).

Criando um Buffer

Para criar um objeto de buffer vazio, o WebGL fornece um método chamado createBuffer(). Este método retorna um objeto buffer recém-criado, se a criação foi bem-sucedida; caso contrário, ele retorna um valor nulo em caso de falha.

WebGL opera como uma máquina de estado. Depois que um buffer é criado, qualquer operação de buffer subsequente será executada no buffer atual até que o desviemos. Use o seguinte código para criar um buffer -

var vertex_buffer = gl.createBuffer();

Note - gl é a variável de referência para o contexto WebGL atual.

Ligar o Buffer

Depois de criar um objeto de buffer vazio, você precisa vincular um buffer de array apropriado (destino) a ele. WebGL fornece um método chamadobindBuffer() para este propósito.

Sintaxe

A sintaxe de bindBuffer() método é o seguinte -

void bindBuffer (enum target, Object buffer)

Este método aceita dois parâmetros e eles são discutidos a seguir.

target- A primeira variável é um valor enum que representa o tipo de buffer que queremos vincular ao buffer vazio. Você tem dois valores enum predefinidos como opções para este parâmetro. Eles são -

  • ARRAY_BUFFER que representa os dados do vértice.

  • ELEMENT_ARRAY_BUFFER que representa os dados do índice.

Object buffer- A segunda é a variável de referência para o objeto buffer criado na etapa anterior. A variável de referência pode ser de um objeto de buffer de vértice ou de um objeto de buffer de índice.

Exemplo

O fragmento de código a seguir mostra como usar o método bindBuffer ().

//vertex buffer
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

//Index buffer
var Index_Buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);

Passando dados para o buffer

O próximo passo é passar os dados (vértices / índices) para o buffer. Até agora, os dados estão na forma de um array e antes de passá-los para o buffer, precisamos envolvê-los em um dos arrays do tipo WebGL. WebGL fornece um método chamadobufferData() para este propósito.

Sintaxe

A sintaxe do método bufferData () é a seguinte -

void bufferData (enum target, Object data, enum usage)

Este método aceita três parâmetros e eles são discutidos abaixo -

target - O primeiro parâmetro é um valor enum que representa o tipo de buffer de matriz que usamos. As opções para este parâmetro são -

  • ARRAY_BUFFER que representa vertex data.

  • ELEMENT_ARRAY_BUFFER que representa index data.

Object data- O segundo parâmetro é o valor do objeto que contém os dados a serem gravados no objeto buffer. Aqui temos que passar os dados usandotyped arrays.

Usage- O terceiro parâmetro deste método é uma variável enum que especifica como usar os dados do objeto de buffer (dados armazenados) para desenhar formas. Existem três opções para este parâmetro, conforme listado abaixo.

  • gl.STATIC_DRAW - Os dados serão especificados uma vez e usados ​​muitas vezes.

  • gl.STREAM_DRAW - Os dados serão especificados uma vez e usados ​​algumas vezes.

  • gl.DYNAMIC_DRAW - Os dados serão especificados repetidamente e usados ​​muitas vezes.

Exemplo

O seguinte snippet de código mostra como usar o bufferData()método. Suponha que os vértices e índices são as matrizes que contêm os dados do vértice e do índice, respectivamente.

//vertex buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

//Index buffer
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

Matrizes digitadas

WebGL fornece um tipo especial de array chamado typed arrayspara transferir os elementos de dados, como vértice de índice e textura. Essas matrizes digitadas armazenam grandes quantidades de dados e os processam em formato binário nativo, o que resulta em melhor desempenho. Os arrays digitados usados ​​pelo WebGL são Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, UInt32Array, Float32Array e Float64Array.

Note

  • Geralmente, para armazenar dados de vértice, usamos Float32Array; e para armazenar dados de índice, usamosUint16Array.

  • Você pode criar matrizes digitadas assim como matrizes de JavaScript usando new palavra-chave.

Desvincular os Buffers

É recomendável que você desvincule os buffers após usá-los. Isso pode ser feito passando um valor nulo no lugar do objeto buffer, conforme mostrado abaixo.

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

WebGL fornece os seguintes métodos para realizar operações de buffer -

Sr. Não. Métodos e Descrição
1

vazio bindBuffer(enum target , Object buffer )

alvo - ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER

2

vazio bufferData( destino de enum , tamanho longo , uso de enum )

alvo - ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER

uso - STATIC_DRAW, STREAM_DRAW, DYNAMIC_DRAW

3

vazio bufferData(enum target , dados de objeto , uso de enum )

alvo e uso - o mesmo que parabufferData acima

4

vazio bufferSubData( destino enum , deslocamento longo , dados do objeto )

alvo - ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER

5 Objeto createBuffer()
6 vazio deleteBuffer( Buffer de objeto )
7

qualquer getBufferParameter(enum target , enum pname )

alvo - ARRAY_BUFFER, ELEMENT_ ARRAY_BUFFER

pname - BUFFER_SIZE, BUFFER_USAGE

8 bool isBuffer( Buffer de objeto )

Shaders são os programas executados na GPU. Shaders são escritos em OpenGL ES Shader Language (conhecido como ES SL). ES SL tem variáveis ​​próprias, tipos de dados, qualificadores, entradas e saídas integradas.

Tipos de dados

A tabela a seguir lista os tipos de dados básicos fornecidos pelo OpenGL ES SL.

Sr. Não. Tipo e descrição
1

void

Representa um valor vazio.

2

bool

Aceita verdadeiro ou falso.

3

int

Este é um tipo de dados inteiro assinado.

4

float

Este é um tipo de dados escalar flutuante.

5

vec2, vec3, vec4

vetor de ponto flutuante de n componentes

6

bvec2, bvec3, bvec4

Vetor booleano

7

ivec2, ivec3, ivec4

vetor inteiro assinado

8

mat2, mat3, mat4

Matriz de flutuação 2x2, 3x3, 4x4

9

sampler2D

Acesse uma textura 2D

10

samplerCube

Acessar textura de cubo mapeada

Qualificadores

Existem três qualificadores principais no OpenGL ES SL -

Sr. Não. Qualificador e descrição
1

attribute

Este qualificador atua como um link entre um sombreador de vértice e OpenGL ES para dados por vértice. O valor deste atributo muda para cada execução do sombreador de vértice.

2

uniform

Este qualificador vincula programas de sombreador e o aplicativo WebGL. Ao contrário do qualificador de atributo, os valores dos uniformes não mudam. Os uniformes são somente leitura; você pode usá-los com quaisquer tipos de dados básicos para declarar uma variável.

Example - uniforme vec4 lightPosition;

3

varying

Este qualificador forma um link entre um sombreador de vértice e um sombreador de fragmento para dados interpolados. Ele pode ser usado com os seguintes tipos de dados - float, vec2, vec3, vec4, mat2, mat3, mat4 ou arrays.

Example - variando vec3 normal;

Vertex Shader

Vertex shader é um código de programa chamado em todos os vértices. Ele transforma (move) a geometria (ex: triângulo) de um lugar para outro. Ele lida com os dados de cada vértice (dados por vértice), como coordenadas de vértice, normais, cores e coordenadas de textura.

No código ES GL do sombreador de vértice, os programadores precisam definir atributos para manipular os dados. Esses atributos apontam para um objeto Vertex Buffer escrito em JavaScript. As tarefas a seguir podem ser realizadas usando sombreadores de vértice junto com a transformação de vértice -

  • Transformação de vértice
  • Transformação normal e normalização
  • Geração de coordenadas de textura
  • Transformação de coordenadas de textura
  • Lighting
  • Aplicação de material de cor

Variáveis ​​Predefinidas

OpenGL ES SL fornece as seguintes variáveis ​​predefinidas para sombreador de vértice -

Sr. Não. Variáveis ​​e descrição
1

highp vec4 gl_Position;

Mantém a posição do vértice.

2

mediump float gl_PointSize;

Contém o tamanho do ponto transformado. As unidades desta variável são pixels.

Código de amostra

Dê uma olhada no seguinte código de exemplo de um sombreador de vértice. Ele processa os vértices de um triângulo.

attribute vec2 coordinates;

void main(void) {
   gl_Position = vec4(coordinates, 0.0, 1.0);
};

Se você observar o código acima com atenção, declaramos uma variável de atributo com o nome coordinates. (Esta variável seráassociada ao objeto Vertex Buffer usando o métodogetAttribLocation(). O atributocoordinates é passado como um parâmetro para este método junto com o objeto de programa de sombreador.)

Na segunda etapa do programa de sombreador de vértice fornecido, o gl_position variável é definida.

gl_Position

gl_Position é a variável predefinida que está disponível apenas no programa de sombreador de vértice. Ele contém a posição do vértice. No código acima, ocoordinatesatributo é passado na forma de um vetor. Como o sombreador de vértice é uma operação por vértice, o valor gl_position é calculado para cada vértice.

Posteriormente, o valor gl_position é usado pela montagem primitiva, recorte, seleção e outras operações de funcionalidade fixa que operam nas primitivas após o término do processamento do vértice.

Podemos escrever programas de sombreador de vértice para todas as operações possíveis de sombreador de vértice, que discutiremos individualmente neste tutorial.

Fragment Shader

UMA mesh é formado por múltiplos triângulos, e a superfície de cada triângulo é conhecida como fragment. Um sombreador de fragmento é o código executado em cada pixel de cada fragmento. Isso é escrito para calcular e preencher a cor em pixels individuais. As seguintes tarefas podem ser realizadas usando shaders de fragmento -

  • Operações em valores interpolados
  • Acesso à textura
  • Aplicação de textura
  • Fog
  • Soma de cores

Variáveis ​​Predefinidas

OpenGL ES SL fornece as seguintes variáveis ​​predefinidas para o sombreador de fragmento -

Sr. Não. Variáveis ​​e descrição
1

mediump vec4 gl_FragCoord;

Mantém a posição do fragmento dentro do buffer de quadros.

2

bool gl_FrontFacing;

Contém o fragmento que pertence a um primitivo frontal.

3

mediump vec2 gl_PointCoord;

Mantém a posição do fragmento dentro de um ponto (apenas rasterização de ponto).

4

mediump vec4 gl_FragColor;

Contém o valor da cor do fragmento de saída do sombreador

5

mediump vec4 gl_FragData[n]

Contém a cor do fragmento para fixação da cor n.

Código de amostra

O código de amostra a seguir de um sombreador de fragmento mostra como aplicar cor a cada pixel em um triângulo.

void main(void) {
   gl_FragColor = vec4(0, 0.8, 0, 1);
}

No código acima, o coloro valor é armazenado na variável gl.FragColor. O programa de sombreador de fragmento passa a saída para o pipeline usando variáveis ​​de função fixas; FragColor é um deles. Esta variável contém o valor da cor dos pixels do modelo.

Armazenando e Compilando os Programas Shader

Como os sombreadores são programas independentes, podemos escrevê-los como um script separado e usar no aplicativo. Ou você pode armazená-los diretamente emstring formato, conforme mostrado abaixo.

var vertCode =
   'attribute vec2 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 0.0, 1.0);' +
   '}';

Compilando o Shader

A compilação envolve seguir três etapas -

  • Criação do objeto shader
  • Anexar o código-fonte ao objeto shader criado
  • Compilando o programa

Criação do Vertex Shader

Para criar um sombreador vazio, o WebGL fornece um método chamado createShader(). Ele cria e retorna o objeto sombreador. Sua sintaxe é a seguinte -

Object createShader (enum type)

Conforme observado na sintaxe, esse método aceita um valor de enum predefinido como parâmetro. Temos duas opções para isso -

  • gl.VERTEX_SHADER para criar vértice shader

  • gl.FRAGMENT_SHADER para criar o sombreador de fragmento.

Anexando a fonte ao sombreador

Você pode anexar o código-fonte ao objeto shader criado usando o método shaderSource(). Sua sintaxe é a seguinte -

void shaderSource(Object shader, string source)

Este método aceita dois parâmetros -

  • shader - Você deve passar o objeto de sombreador criado como um parâmetro.

  • Source - Você tem que passar o código do programa shader em formato de string.

Compilando o programa

Para compilar o programa, você deve usar o método compileShader(). Sua sintaxe é a seguinte -

compileShader(Object shader)

Este método aceita o objeto de programa de sombreador como parâmetro. Depois de criar um objeto de programa de sombreador, anexe o código-fonte a ele e passe esse objeto para este método.

O fragmento de código a seguir mostra como criar e compilar um sombreador de vértice, bem como um sombreador de fragmento para criar um triângulo.

// Vertex Shader
var vertCode =
   'attribute vec3 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';

var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
 
// Fragment Shader
var fragCode =
   'void main(void) {' +
      ' gl_FragColor = vec4(0, 0.8, 0, 1);' +
   '}';

var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);

Programa Combinado

Depois de criar e compilar ambos os programas de sombreador, você precisa criar um programa combinado contendo ambos os sombreadores (vértice e fragmento). As seguintes etapas precisam ser seguidas -

  • Crie um objeto de programa
  • Anexe ambos os shaders
  • Ligue ambos os shaders
  • Use o programa

Criar um objeto de programa

Crie um objeto de programa usando o método createProgram(). Ele retornará um objeto de programa vazio. Aqui está sua sintaxe -

createProgram();

Anexe os Shaders

Anexe os sombreadores ao objeto de programa criado usando o método attachShader(). Sua sintaxe é a seguinte -

attachShader(Object program, Object shader);

Este método aceita dois parâmetros -

  • Program - Passe o objeto de programa vazio criado como um parâmetro.

  • Shader - Passe um dos programas de shaders compilados (shader de vértice, shader de fragmento)

Note - Você precisa conectar os dois sombreadores usando este método.

Ligue os Shaders

Vincule os shaders usando o método linkProgram(), passando o objeto de programa ao qual você anexou os sombreadores. Sua sintaxe é a seguinte -

linkProgram(shaderProgram);

Use o programa

WebGL fornece um método chamado useProgram(). Você precisa passar o programa vinculado a ele. Sua sintaxe é a seguinte -

useProgram(shaderProgram);

O fragmento de código a seguir mostra como criar, vincular e usar um programa de sombreador combinado.

var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);

Cada atributo no programa de sombreador de vértice aponta para um objeto de buffer de vértice. Depois de criar os objetos de buffer de vértice, os programadores devem associá-los aos atributos do programa de sombreador de vértice. Cada atributo aponta para apenas um objeto de buffer de vértice do qual eles extraem os valores de dados e, em seguida, esses atributos são passados ​​para o programa de sombreador.

Para associar os Vertex Buffer Objects com os atributos do programa vertex shader, você deve seguir os passos abaixo -

  • Obtenha a localização do atributo
  • Aponte o atributo para um objeto de buffer de vértice
  • Habilite o atributo

Obtenha a localização do atributo

WebGL fornece um método chamado getAttribLocation()que retorna a localização do atributo. Sua sintaxe é a seguinte -

ulong getAttribLocation(Object program, string name)

Este método aceita o objeto de programa de sombreador de vértice e os valores de atributo do programa de sombreador de vértice.

O trecho de código a seguir mostra como usar esse método.

var coordinatesVar = gl.getAttribLocation(shader_program, "coordinates");

Aqui, shader_program é o objeto do programa shader e coordinates é o atributo do programa de sombreador de vértice.

Aponte o atributo para um VBO

Para atribuir o objeto buffer à variável de atributo, o WebGL fornece um método chamado vertexAttribPointer(). Aqui está a sintaxe deste método -

void vertexAttribPointer(location, int size, enum type, bool normalized, long stride, long offset)

Este método aceita seis parâmetros e eles são discutidos a seguir.

  • Location- Especifica o local de armazenamento de uma variável de atributo. Nesta opção, você deve passar o valor retornado pelogetAttribLocation() método.

  • Size - Especifica o número de componentes por vértice no objeto buffer.

  • Type - Especifica o tipo de dados.

  • Normalized- Este é um valor booleano. Se verdadeiro, os dados não flutuantes são normalizados para [0, 1]; caso contrário, é normalizado para [-1, 1].

  • Stride - Especifica o número de bytes entre os diferentes elementos de dados do vértice ou zero para a distância padrão.

  • Offset- Ele especifica o deslocamento (em bytes) em um objeto buffer para indicar de qual byte os dados do vértice são armazenados. Se os dados forem armazenados desde o início, o deslocamento é 0.

O seguinte snippet mostra como usar vertexAttribPointer() em um programa -

gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);

Habilitando o Atributo

Ative o atributo vertex shader para acessar o objeto buffer em um vertex shader. Para esta operação, o WebGL forneceenableVertexAttribArray()método. Este método aceita a localização do atributo como parâmetro. Aqui está como usar este método em um programa -

gl.enableVertexAttribArray(coordinatesVar);

Depois de associar os buffers aos sombreadores, a etapa final é desenhar as primitivas necessárias. WebGL fornece dois métodos, a saber,drawArrays() e drawElements() para desenhar modelos.

drawArrays ()

drawArrays()é o método usado para desenhar modelos usando vértices. Aqui está sua sintaxe -

void drawArrays(enum mode, int first, long count)

Este método leva os seguintes três parâmetros -

  • mode- No WebGL, os modelos são desenhados usando tipos primitivos. Usando o modo, os programadores precisam escolher um dos tipos primitivos fornecidos pelo WebGL. Os valores possíveis para esta opção são - gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN e gl.TRIANGLES.

  • first- Esta opção especifica o elemento inicial nas matrizes habilitadas. Não pode ser um valor negativo.

  • count - Esta opção especifica o número de elementos a serem renderizados.

Se você desenhar um modelo usando drawArrays() método, então WebGL, enquanto renderiza as formas, cria a geometria na ordem em que as coordenadas do vértice são definidas.

Exemplo

Se você deseja desenhar um único triângulo usando drawArray() método, então você tem que passar três vértices e chamar o drawArrays() método, conforme mostrado abaixo.

var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5,];
gl.drawArrays(gl.TRIANGLES, 0, 3);

Isso produzirá um triângulo conforme mostrado abaixo.

Suponha que você queira desenhar triângulos contíguos, então você tem que passar os próximos três vértices em ordem no buffer de vértices e mencionar o número de elementos a serem renderizados como 6.

var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5, 0.0,-0.5, 0.25,0.5, 0.5,-0.5,];
gl.drawArrays(gl.TRIANGLES, 0, 6);

Isso produzirá um triângulo contíguo conforme mostrado abaixo.

drawElements ()

drawElements()é o método usado para desenhar modelos usando vértices e índices. Sua sintaxe é a seguinte -

void drawElements(enum mode, long count, enum type, long offset)

Este método leva os seguintes quatro parâmetros -

  • mode- Os modelos WebGL são desenhados usando tipos primitivos. Usando o modo, os programadores precisam escolher um dos tipos primitivos fornecidos pelo WebGL. A lista de valores possíveis para esta opção são - gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN e gl.TRIANGLES.

  • count - Esta opção especifica o número de elementos a serem renderizados.

  • type - Esta opção especifica o tipo de dados dos índices que devem ser UNSIGNED_BYTE ou UNSIGNED_SHORT.

  • offset- Esta opção especifica o ponto de partida para renderização. Geralmente é o primeiro elemento (0).

Se você desenhar um modelo usando drawElements(), então o objeto de buffer de índice também deve ser criado junto com o objeto de buffer de vértice. Se você usar este método, os dados do vértice serão processados ​​uma vez e usados ​​quantas vezes forem mencionadas nos índices.

Exemplo

Se você quiser desenhar um único triângulo usando índices, você precisa passar os índices junto com os vértices e chamar o drawElements() método conforme mostrado abaixo.

var vertices = [ -0.5,-0.5,0.0, -0.25,0.5,0.0, 0.0,-0.5,0.0 ];
var indices = [0,1,2];

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Ele produzirá a seguinte saída -

Se você deseja desenhar triângulos contagiosos usando drawElements() , simplesmente adicione os outros vértices e mencione os índices para os vértices restantes.

var vertices = [
   -0.5,-0.5,0.0,
   -0.25,0.5,0.0,
   0.0,-0.5,0.0,
   0.25,0.5,0.0,
   0.5,-0.5,0.0 
];

var indices = [0,1,2,2,3,4];

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Ele produzirá a seguinte saída -

Operações Requeridas

Antes de desenhar uma primitiva, você precisa realizar algumas operações, que são explicadas a seguir.

Limpar a tela

Primeiro de tudo, você deve limpar a tela, usando clearColor()método. Você pode passar os valores RGBA de uma cor desejada como parâmetro para este método. Em seguida, o WebGL limpa a tela e a preenche com a cor especificada. Portanto, você pode usar este método para definir a cor de fundo.

Dê uma olhada no exemplo a seguir. Aqui estamos passando o valor RGBA da cor cinza.

gl.clearColor(0.5, 0.5, .5, 1);

Habilitar teste de profundidade

Habilite o teste de profundidade usando o enable() método, conforme mostrado abaixo.

gl.enable(gl.DEPTH_TEST);

Limpe o bit do buffer de cor

Limpe a cor, bem como o buffer de profundidade usando o clear() método, conforme mostrado abaixo.

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

Definir a porta de visualização

A porta de visualização representa uma área retangular visível que contém os resultados da renderização do buffer de desenho. Você pode definir as dimensões da janela de visualização usandoviewport()método. No código a seguir, as dimensões da porta de visualização são definidas para as dimensões da tela.

gl.viewport(0,0,canvas.width,canvas.height);

Discutimos anteriormente (no Capítulo 5) como seguir um processo passo a passo para desenhar uma primitiva. Explicamos o processo em cinco etapas. Você precisa repetir essas etapas sempre que desenhar uma nova forma. Este capítulo explica como desenhar pontos com coordenadas 3D em WebGL. Antes de prosseguir, vamos dar uma olhada nas cinco etapas.

Passos Requeridos

As etapas a seguir são necessárias para criar um aplicativo WebGL para desenhar pontos.

Step 1 − Prepare the Canvas and Get the WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando o método getContext().

Step 2 − Define the Geometry and Store it in the Buffer Objects

Como estamos desenhando três pontos, definimos três vértices com coordenadas 3D e os armazenamos em buffers.

var vertices = [
   -0.5,0.5,0.0,
   0.0,0.5,0.0,
   -0.25,0.25,0.0, 
];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever programas de shader de vértice e shader de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader - No sombreador de vértice do exemplo dado, definimos um atributo de vetor para armazenar coordenadas 3D e atribuí-lo ao gl_position variável.

  • gl_pointsizeé a variável usada para atribuir um tamanho ao ponto. Atribuímos o tamanho do ponto como 10.

var vertCode = 'attribute vec3 coordinates;' +

   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
      'gl_PointSize = 10.0;'+
   '}';
  • Fragment Shader - No shader de fragmento, simplesmente atribuímos a cor do fragmento ao gl_FragColor variável

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

Step 4 − Associate the Shader Programs to Buffer Objects

Nesta etapa, associamos os objetos buffer ao programa shader.

Step 5 − Drawing the Required Object

Nós usamos o método drawArrays()para desenhar pontos. Como o número de pontos que queremos desenhar é três, o valor da contagem é 3.

gl.drawArrays(gl.POINTS, 0, 3)

Exemplo - Desenhe três pontos usando WebGL

Aqui está o programa WebGL completo para desenhar três pontos -

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*================Creating a canvas=================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*==========Defining and storing the geometry=======*/

         var vertices = [
            -0.5,0.5,0.0,
            0.0,0.5,0.0,
            -0.25,0.25,0.0, 
         ];

         // Create an empty buffer object to store the vertex buffer
         var vertex_buffer = gl.createBuffer();

         //Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         /*=========================Shaders========================*/

         // vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +

            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'gl_PointSize = 10.0;'+
            '}';

         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         
         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);

         // fragment shader source code
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragmentt shader
         gl.compileShader(fragShader);
         
         // Create a shader program object to store
         // the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader); 

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /*======== Associating shaders to buffer objects ========*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*============= Drawing the primitive ===============*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);
 
         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawArrays(gl.POINTS, 0, 3);
      </script>
   </body>
</html>

Isso produzirá o seguinte resultado -

No capítulo anterior (Capítulo 11), discutimos como desenhar três pontos usando WebGL. No Capítulo 5, pegamos um aplicativo de amostra para demonstrar como desenhar um triângulo. Em ambos os exemplos, desenhamos as primitivas usando apenas vértices.

Para desenhar formas / malhas mais complexas, passamos os índices de uma geometria também, junto com os vértices, para os sombreadores. Neste capítulo, veremos como desenhar um triângulo usando índices.

Etapas necessárias para desenhar um triângulo

As etapas a seguir são necessárias para criar um aplicativo WebGL para desenhar um triângulo.

Step 1 − Prepare the Canvas and Get WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando getContext().

Step 2 − Define the Geometry and Store it in Buffer Objects

Como estamos desenhando um triângulo usando índices, temos que passar os três vértices do triângulo, incluindo os índices, e armazená-los nos buffers.

var vertices = [
   -0.5,0.5,0.0,
   -0.5,-0.5,0.0,
   0.5,-0.5,0.0, 
];
	
indices = [0,1,2];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever programas de shader de vértice e shader de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader - No sombreador de vértice do programa, definimos o atributo vetorial para armazenar coordenadas 3D e atribuí-lo a gl_position.

var vertCode =
   'attribute vec3 coordinates;' +
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';
  • Fragment Shader - No shader de fragmento, simplesmente atribuímos a cor do fragmento ao gl_FragColor variável.

var fragCode = 'void main(void) {' +
   ' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +
'}';

Step 4 − Associate the Shader Programs to the Buffer Objects

Nesta etapa, associamos os objetos buffer e o programa shader.

Step 5 − Drawing the Required Object

Como estamos desenhando um triângulo usando índices, usaremos drawElements(). Para este método, temos que passar o número de índices. O valor doindices.length significa o número de índices.

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Exemplo - Desenhando um Triângulo

O código do programa a seguir mostra como desenhar um triângulo em WebGL usando índices -

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============== Creating a canvas ====================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*======== Defining and storing the geometry ===========*/

         var vertices = [
            -0.5,0.5,0.0,
            -0.5,-0.5,0.0,
            0.5,-0.5,0.0, 
         ];
         
         indices = [0,1,2];
         
         // Create an empty buffer object to store vertex buffer
         var vertex_buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         
         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         // Create an empty buffer object to store Index buffer
         var Index_Buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
         
         // Unbind the buffer
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

         /*================ Shaders ====================*/
         
         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
				
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
            '}';
            
         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);

         //fragment shader source code
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';
            
         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode); 
         
         // Compile the fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to store
         // the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader);

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both the programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /*======= Associating shaders to buffer objects =======*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
         
         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); 
         
         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*=========Drawing the triangle===========*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);

         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
    </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

No capítulo anterior (Capítulo 12), discutimos como desenhar um triângulo usando WebGL. Além dos triângulos, o WebGL oferece suporte a vários outros modos de desenho. Este capítulo explica os modos de desenho suportados pelo WebGL.

O parâmetro de modo

Vamos dar uma olhada na sintaxe dos métodos - drawElements() E desenhe Arrays().

void drawElements(enum mode, long count, enum type, long offset);

void drawArrays(enum mode, int first, long count);

Se você observar claramente, ambos os métodos aceitam um parâmetro mode. Usando este parâmetro, os programadores podem selecionar o modo de desenho no WebGL.

Os modos de desenho fornecidos pelo WebGL estão listados na tabela a seguir.

Sr. Não. Modo e descrição
1

gl.POINTS

Para desenhar uma série de pontos.

2

gl.LINES

Para desenhar uma série de segmentos de linha não conectados (linhas individuais).

3

gl.LINE_STRIP

Para desenhar uma série de segmentos de linha conectados.

4

gl.LINE_LOOP

Para desenhar uma série de segmentos de linha conectados. Ele também une o primeiro e o último vértices para formar um loop.

5

gl.TRIANGLES

Para desenhar uma série de triângulos separados.

6

gl.TRIANGLE_STRIP

Para desenhar uma série de triângulos conectados em forma de faixa.

7

gl.TRIANGLE_FAN

Para desenhar uma série de triângulos conectados compartilhando o primeiro vértice em forma de leque.

Exemplo - Desenhe Três Linhas Paralelas

O exemplo a seguir mostra como desenhar três linhas paralelas usando gl.LINES.

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
         /*======= Creating a canvas =========*/

         var canvas = document.getElementById('my_Canvas');
         var gl = canvas.getContext('experimental-webgl');

         /*======= Defining and storing the geometry ======*/

         var vertices = [
            -0.7,-0.1,0,
            -0.3,0.6,0,
            -0.3,-0.3,0,
            0.2,0.6,0,
            0.3,-0.3,0,
            0.7,0.6,0 
         ]

         // Create an empty buffer object
         var vertex_buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
      
         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         /*=================== Shaders ====================*/

         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
            '}';

         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);

         // Fragment shader source code
         var fragCode =
            'void main(void) {' +
               'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to store
         // the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader);

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both the programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /*======= Associating shaders to buffer objects ======*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*============ Drawing the triangle =============*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);

         // Clear the color and depth buffer
         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawArrays(gl.LINES, 0, 6);

         // POINTS, LINE_STRIP, LINE_LOOP, LINES,
         // TRIANGLE_STRIP,TRIANGLE_FAN, TRIANGLES
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Modos de Desenho

No programa acima, se você substituir o modo de drawArrays() com um dos seguintes modos de desenho, ele produzirá saídas diferentes a cada vez.

Modos de Desenho Saídas
LINE_STRIP
LINE_LOOP
TRIANGLE_STRIP
TRIANGLE_FAN
TRIÂNGULOS

No capítulo anterior, discutimos os diferentes modos de desenho fornecidos pelo WebGL. Também podemos usar índices para desenhar primitivas usando um desses modos. Para desenhar modelos em WebGL, temos que escolher um desses primitivos e desenhar a malha necessária (ou seja, um modelo formado usando um ou mais primitivos).

Neste capítulo, daremos um exemplo para demonstrar como desenhar um quadrilátero usando WebGL.

Etapas para desenhar um quadrilátero

As etapas a seguir são necessárias para criar um aplicativo WebGL para desenhar um quadrilátero.

Step 1 − Prepare the Canvas and Get the WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando getContext().

Step 2 − Define the Geometry and Store it in the Buffer Objects

Um quadrado pode ser desenhado usando dois triângulos. Neste exemplo, fornecemos os vértices para dois triângulos (com uma aresta comum) e índices.

var vertices = [
   -0.5,0.5,0.0,
   -0.5,-0.5,0.0,
   0.5,-0.5,0.0,
   0.5,0.5,0.0 
];

indices = [3,2,1,3,1,0];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever os programas de sombreador de vértice e sombreador de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader - No sombreador de vértice do programa, definimos o atributo vetorial para armazenar coordenadas 3D e atribuí-lo a gl_position.

var vertCode =
   'attribute vec3 coordinates;' +
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';
  • Fragment Shader - No shader de fragmento, simplesmente atribuímos a cor do fragmento ao gl_FragColor variável.

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(0.5, 0.3, 0.0, 7.5);' +'}';

Step 4 − Associate the Shader Programs to Buffer Objects

Nesta etapa, associamos os objetos buffer ao programa shader.

Step 5 − Drawing the Required Object

Como estamos desenhando dois triângulos para formar um quadrante, usando índices, usaremos o método drawElements(). Para este método, temos que passar o número de índices. O valor deindices.length dá o número de índices.

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Exemplo - Desenhe um Quadrilátero

O programa a seguir mostra como criar um aplicativo WebGL para desenhar um quadrilátero.

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============ Creating a canvas =================*/
      
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
      
         /*========== Defining and storing the geometry =========*/

         var vertices = [
            -0.5,0.5,0.0,
            -0.5,-0.5,0.0,
            0.5,-0.5,0.0,
            0.5,0.5,0.0 
         ];

         indices = [3,2,1,3,1,0];

         // Create an empty buffer object to store vertex buffer
         var vertex_buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         // Create an empty buffer object to store Index buffer
         var Index_Buffer = gl.createBuffer();

         // Bind appropriate array buffer to it
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Pass the vertex data to the buffer
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

         // Unbind the buffer
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

         /*====================== Shaders =======================*/

         // Vertex shader source code
         var vertCode =
            'attribute vec3 coordinates;' +
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
            '}';

         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);

         // Fragment shader source code
         var fragCode =
            'void main(void) {' +
               ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         // Create fragment shader object 
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to
         // store the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader);

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both the programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /* ======= Associating shaders to buffer objects =======*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); 

         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // Point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         /*============= Drawing the Quad ================*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);

         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         // Draw the triangle
         gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Em todos os nossos exemplos anteriores, aplicamos cor ao objeto atribuindo um valor de cor desejado ao gl_FragColorvariável. Além disso, podemos definir cores para cada vértice - assim como as coordenadas e índices dos vértices. Este capítulo dá um exemplo para demonstrar como aplicar cores a um quadrilátero usando WebGL.

Aplicando Cores

Para aplicar cores, você deve definir as cores para cada vértice usando os valores RGB, em array JavaScript. Você pode atribuir os mesmos valores a todos os vértices para ter uma cor única para o objeto. Depois de definir as cores, você deve criar um buffer de cores e armazenar esses valores nele, e associá-lo aos atributos do sombreador de vértice.

No sombreador de vértice, junto com o atributo de coordenadas (que mantém a posição dos vértices), definimos um attribute e um varying para lidar com cores.

o color atributo contém o valor da cor por vértice, e varyingé a variável que é passada como entrada para o sombreador de fragmento. Portanto, temos que atribuir ocolor valor para varying.

No shader de fragmento, o varying que contém o valor da cor é atribuído a gl_FragColor, que contém a cor final do objeto.

Etapas para aplicar cores

As etapas a seguir são necessárias para criar um aplicativo WebGL para desenhar um Quad e aplicar cores a ele.

Step 1 − Prepare the Canvas and Get the WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando getContext().

Step 2 − Define the Geometry and Store it in the Buffer Objects

Um quadrado pode ser desenhado usando dois triângulos. Portanto, neste exemplo, fornecemos os vértices para dois triângulos (com uma aresta comum) e índices. Como queremos aplicar cores a ela, uma variável contendo os valores das cores também é definida e os valores das cores para cada uma (vermelho, azul, verde e rosa) são atribuídos a ela.

var vertices = [
   -0.5,0.5,0.0,
   -0.5,-0.5,0.0, 
   0.5,-0.5,0.0,
   0.5,0.5,0.0 
];

var colors = [ 0,0,1, 1,0,0, 0,1,0, 1,0,1,];
indices = [3,2,1,3,1,0];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever os programas de sombreador de vértice e sombreador de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader- No sombreador de vértice do programa, definimos atributos de vetor para armazenar coordenadas 3D (posição) e a cor de cada vértice. UMAvaringvariável é declarada para passar os valores de cor do sombreador de vértice para o sombreador de fragmento. E, finalmente, o valor armazenado no atributo de cor é atribuído avarying.

var vertCode = 'attribute vec3 coordinates;'+
   'attribute vec3 color;'+
   'varying vec3 vColor;'+
	
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
      'vColor = color;'+
   '}';
  • Fragment Shader - No shader de fragmento, atribuímos o varying ao gl_FragColor variável.

var fragCode = 'precision mediump float;'+
   'varying vec3 vColor;'+
   'void main(void) {'+
      'gl_FragColor = vec4(vColor, 1.);'+
   '}';

Step 4 − Associate the Shader Programs with the Buffer Objects

Nesta etapa, associamos os objetos buffer e o programa shader.

Step 5 − Drawing the Required Object

Como estamos desenhando dois triângulos que formarão um quadrante, usando índices, usaremos o método drawElements(). Para este método, temos que passar o número de índices. O valor deindices.length indica o número de índices.

gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);

Exemplo - Aplicando Cor

O programa a seguir demonstra como desenhar um quadrante usando o aplicativo WebGL e aplicar cores a ele.

<!doctype html>
<html>
   <body>
    <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
         /*============= Creating a canvas ==================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');
         
         /*========== Defining and storing the geometry ==========*/

         var vertices = [
            -0.5,0.5,0.0,
            -0.5,-0.5,0.0,
            0.5,-0.5,0.0,
            0.5,0.5,0.0
         ];

         var colors = [0,0,1, 1,0,0, 0,1,0, 1,0,1,];
         
         indices = [3,2,1,3,1,0];
         
         // Create an empty buffer object and store vertex data
         var vertex_buffer = gl.createBuffer();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
         gl.bindBuffer(gl.ARRAY_BUFFER, null);

         // Create an empty buffer object and store Index data
         var Index_Buffer = gl.createBuffer();
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

         // Create an empty buffer object and store color data
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         /*======================= Shaders =======================*/
         
         // vertex shader source code
         var vertCode = 'attribute vec3 coordinates;'+
            'attribute vec3 color;'+
            'varying vec3 vColor;'+
            'void main(void) {' +
               ' gl_Position = vec4(coordinates, 1.0);' +
               'vColor = color;'+
            '}';
            
         // Create a vertex shader object
         var vertShader = gl.createShader(gl.VERTEX_SHADER);

         // Attach vertex shader source code
         gl.shaderSource(vertShader, vertCode);

         // Compile the vertex shader
         gl.compileShader(vertShader);


         // fragment shader source code
         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';
            
         // Create fragment shader object
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);

         // Attach fragment shader source code
         gl.shaderSource(fragShader, fragCode);

         // Compile the fragmentt shader
         gl.compileShader(fragShader);

         // Create a shader program object to
         // store the combined shader program
         var shaderProgram = gl.createProgram();

         // Attach a vertex shader
         gl.attachShader(shaderProgram, vertShader);

         // Attach a fragment shader
         gl.attachShader(shaderProgram, fragShader);

         // Link both the programs
         gl.linkProgram(shaderProgram);

         // Use the combined shader program object
         gl.useProgram(shaderProgram);

         /* ======== Associating shaders to buffer objects =======*/

         // Bind vertex buffer object
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         // Bind index buffer object
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);

         // Get the attribute location
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         // point an attribute to the currently bound VBO
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);

         // Enable the attribute
         gl.enableVertexAttribArray(coord);

         // bind the color buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         
         // get the attribute location
         var color = gl.getAttribLocation(shaderProgram, "color");
 
         // point attribute to the volor buffer object
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;
 
         // enable the color attribute
         gl.enableVertexAttribArray(color);

         /*============Drawing the Quad====================*/

         // Clear the canvas
         gl.clearColor(0.5, 0.5, 0.5, 0.9);

         // Enable the depth test
         gl.enable(gl.DEPTH_TEST);

         // Clear the color buffer bit
         gl.clear(gl.COLOR_BUFFER_BIT);

         // Set the view port
         gl.viewport(0,0,canvas.width,canvas.height);

         //Draw the triangle
         gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Até agora, discutimos como desenhar várias formas e aplicar cores nelas usando WebGL. Aqui, neste capítulo, vamos dar um exemplo para mostrar como traduzir um triângulo.

Tradução

A tradução é uma das affine transformationsfornecido por WebGL. Usando a translação, podemos mover um triângulo (qualquer objeto) no plano xyz. Suponha que temos um triângulo [a, b, c] e queremos mover o triângulo para uma posição que é 5 unidades em direção ao eixo X positivo e 3 unidades em direção ao eixo Y positivo. Então, os novos vértices seriam [a + 5, b + 3, c + 0]. Isso significa que, para traduzir o triângulo, precisamos adicionar as distâncias de translação, digamos, tx, ty, tz a cada vértice.

Uma vez que é um per-vertex operation, podemos carregá-lo no programa de sombreador de vértice.

No sombreador de vértice, junto com o atributo, coordinates(que contém as posições dos vértices), definimos uma variável uniforme que mantém as distâncias de translação (x, y, z). Mais tarde, adicionamos esta variável uniforme à variável de coordenadas e atribuímos o resultado aogl_Position variável.

Note - Como o sombreador de vértice será executado em cada vértice, todos os vértices do triângulo serão convertidos.

Etapas para traduzir um triângulo

As etapas a seguir são necessárias para criar um aplicativo WebGL para desenhar um triângulo e, em seguida, traduzi-lo para uma nova posição.

Step 1 − Prepare the Canvas and Get the WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando getContext().

Step 2 − Define the Geometry and Store it in the Buffer Objects

Uma vez que estamos desenhando um triângulo, temos que passar três vértices do triângulo e armazená-los em buffers.

var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever os programas de sombreador de vértice e sombreador de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader- No sombreador de vértice do programa, definimos um atributo de vetor para armazenar coordenadas 3D. Junto com ele, definimos uma variável uniforme para armazenar as distâncias de translação e, finalmente, adicionamos esses dois valores e atribuímos agl_position que mantém a posição final dos vértices.

var vertCode =
   'attribute vec4 coordinates;' +
   'uniform vec4 translation;'+
   'void main(void) {' +
      ' gl_Position = coordinates + translation;' +
   '}';
  • Fragment Shader - No sombreador de fragmento, simplesmente atribuímos a cor do fragmento à variável gl_FragColor.

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

Step 4 − Associate the Shader Programs to the Buffer Objects

Nesta etapa, associamos os objetos buffer ao programa shader.

Step 5 − Drawing the Required Object

Uma vez que estamos desenhando o triângulo usando índices, usaremos o método drawArrays(). Para este método, temos que passar o número de vértices / elementos a serem considerados. Como estamos desenhando um triângulo, passaremos 3 como parâmetro.

gl.drawArrays(gl.TRIANGLES, 0, 3);

Exemplo - Traduzir um triângulo

O exemplo a seguir mostra como traduzir um triângulo no plano xyz.

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>
         
      <script>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 
 
         /*===========Defining and storing the geometry==============*/
         var vertices = [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];
            
         //Create an empty buffer object and store vertex data            
         var vertex_buffer = gl.createBuffer(); 
			
         //Create a new buffer
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);   
			
         //bind it to the current buffer			
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 
			
         // Pass the buffer data
         gl.bindBuffer(gl.ARRAY_BUFFER, null);  
            
         /*========================Shaders============================*/
            
         //vertex shader source code 
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform vec4 translation;'+
            'void main(void) {' +
               '  gl_Position = coordinates + translation;' +
            '}';
            
         //Create a vertex shader program object and compile it              
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);
            
   
         //fragment shader source code
         var fragCode =
            'void main(void) {' +
               '   gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         //Create a fragment shader program object and compile it            
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);
            
         //Create and use combiened shader program
         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);
   
         gl.useProgram(shaderProgram);
   
         /* ===========Associating shaders to buffer objects============*/
      
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);    
         var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates");
         gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);   
         gl.enableVertexAttribArray(coordinatesVar); 
   
         /* ==========translation======================================*/
         var Tx = 0.5, Ty = 0.5, Tz = 0.0;
         var translation = gl.getUniformLocation(shaderProgram, 'translation');
         gl.uniform4f(translation, Tx, Ty, Tz, 0.0);
 
         /*=================Drawing the riangle and transforming it========================*/ 

         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);
   
         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
    </body>
 </html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Neste capítulo, vamos dar um exemplo para demonstrar como modificar a escala de um triângulo usando WebGL.

Dimensionamento

O dimensionamento nada mais é do que aumentar ou diminuir o tamanho de um objeto. Por exemplo, se um triângulo tem vértices do tamanho [a, b, c], então o triângulo com os vértices [2a, 2b, 2c] terá o dobro de seu tamanho. Portanto, para dimensionar um triângulo, você deve multiplicar cada vértice pelo fator de escala. Você também pode escalar um determinado vértice.

Para dimensionar um triângulo, no sombreador de vértice do programa, criamos uma matriz uniforme e multiplicamos os valores das coordenadas com esta matriz. Posteriormente, passamos uma matriz diagonal 4 × 4 com os fatores de escala das coordenadas x, y, z nas posições diagonais (última posição diagonal 1).

Passos Requeridos

As etapas a seguir são necessárias para criar um aplicativo WebGL para dimensionar um triângulo.

Step 1 − Prepare the Canvas and Get the WebGL Rendering Context

Nesta etapa, obtemos o objeto de contexto WebGL Rendering usando getContext().

Step 2 − Define the Geometry and Store it in the Buffer Objects

Uma vez que estamos desenhando um triângulo, temos que passar três vértices do triângulo e armazená-los em buffers.

var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ];

Step 3 − Create and Compile the Shader Programs

Nesta etapa, você precisa escrever os programas de sombreador de vértice e sombreador de fragmento, compilá-los e criar um programa combinado vinculando esses dois programas.

  • Vertex Shader- No sombreador de vértice do programa, definimos um atributo de vetor para armazenar coordenadas 3D. Junto com isso, definimos uma matriz uniforme para armazenar os fatores de escala e, finalmente, multiplicamos esses dois valores e atribuímos agl_position que mantém a posição final dos vértices.

var vertCode =
   'attribute vec4 coordinates;' +
   'uniform mat4 u_xformMatrix;' +
   'void main(void) {' +
      ' gl_Position = u_xformMatrix * coordinates;' +
   '}';
  • Fragment Shader - No shader de fragmento, simplesmente atribuímos a cor do fragmento ao gl_FragColor variável.

var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';

Step 4 − Associate the Shader Programs with the Buffer Objects

Nesta etapa, associamos os objetos buffer ao programa shader.

Step 5 − Drawing the Required Object

Uma vez que estamos desenhando o triângulo usando índices, usamos o drawArrays()método. Para este método, temos que passar o número de vértices / elementos a serem considerados. Como estamos desenhando um triângulo, passaremos 3 como parâmetro.

gl.drawArrays(gl.TRIANGLES, 0, 3);

Exemplo - dimensionar um triângulo

O exemplo a seguir mostra como dimensionar um triângulo -

<!doctype html>
<html>
   <body>
      <canvas width = "300" height = "300" id = "my_Canvas"></canvas>

      <script>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl'); 

         /*===========Defining and storing the geometry==============*/
         var vertices =  [
            -0.5,0.5,0.0, 	
            -0.5,-0.5,0.0, 	
            0.5,-0.5,0.0,   
         ];

         //Create an empty buffer object and store vertex data

         var vertex_buffer = gl.createBuffer();                                                     
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);                                                
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);           
         gl.bindBuffer(gl.ARRAY_BUFFER, null);  

         /*========================Shaders============================*/

         //Vertex shader source code
         var vertCode =
            'attribute vec4 coordinates;' + 
            'uniform mat4 u_xformMatrix;' +
            'void main(void) {' +
               '  gl_Position = u_xformMatrix * coordinates;' +
            '}';

         //Create a vertex shader program object and compile it                
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         //fragment shader source code
         var fragCode =
            'void main(void) {' +
               '   gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
            '}';

         //Create a fragment shader program object and compile it 
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);

         //Create and use combiened shader program
         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);

         gl.useProgram(shaderProgram); 

         /*===================scaling==========================*/

         var Sx = 1.0, Sy = 1.5, Sz = 1.0;
         var xformMatrix = new Float32Array([
            Sx,   0.0,  0.0,  0.0,
            0.0,  Sy,   0.0,  0.0,
            0.0,  0.0,  Sz,   0.0,
            0.0,  0.0,  0.0,  1.0  
         ]);

         var u_xformMatrix = gl.getUniformLocation(shaderProgram, 'u_xformMatrix');
         gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

         /* ===========Associating shaders to buffer objects============*/
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);   

         var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates"); 
         gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);  
         gl.enableVertexAttribArray(coordinatesVar);

         /*=================Drawing the Quad========================*/ 
         gl.clearColor(0.5, 0.5, 0.5, 0.9);
         gl.enable(gl.DEPTH_TEST);

         gl.clear(gl.COLOR_BUFFER_BIT);
         gl.viewport(0,0,canvas.width,canvas.height);
         gl.drawArrays(gl.TRIANGLES, 0, 3);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Neste capítulo, vamos dar um exemplo para demonstrar como girar um triângulo usando WebGL.

Exemplo - girar um triângulo

O programa a seguir mostra como girar um triângulo usando WebGL.

<!doctype html>
<html>
   <body>
      <canvas width = "400" height = "400" id = "my_Canvas"></canvas>

      <script>
         /*=================Creating a canvas=========================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*===========Defining and storing the geometry==============*/

         var vertices = [ -1,-1,-1, 1,-1,-1, 1, 1,-1 ];
         var colors = [ 1,1,1, 1,1,1, 1,1,1 ];
         var indices = [ 0,1,2 ];

         //Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         //Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         //Create and store data into index buffer
         var index_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

         /*==========================Shaders=========================*/

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+

            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);

         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);

         /*===========associating attributes to vertex shader ============*/

         var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix");
         var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix");
         var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix");
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

         var position = gl.getAttribLocation(shaderProgram, "position");
         gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ; //position
         gl.enableVertexAttribArray(position);
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);

         var color = gl.getAttribLocation(shaderProgram, "color");
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ; //color
         gl.enableVertexAttribArray(color);
         gl.useProgram(shaderProgram);

         /*========================= MATRIX ========================= */

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0
            ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);
         var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
         var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];

         //translating z
         view_matrix[14] = view_matrix[14]-6; //zoom

         /*=======================rotation========================*/
         function rotateZ(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8]; 

            m[0] = c*m[0]-s*m[1];
            m[4] = c*m[4]-s*m[5];
            m[8] = c*m[8]-s*m[9];
            m[1] = c*m[1]+s*mv0;
            m[5] = c*m[5]+s*mv4;
            m[9] = c*m[9]+s*mv8;
         }

         /*=================Drawing===========================*/

         var time_old = 0;
         var animate = function(time) {
            var dt = time-time_old;
            rotateZ(mov_matrix, dt*0.002);
            time_old = time;

            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);
            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Neste capítulo, vamos dar um exemplo para demonstrar como desenhar um cubo 3D giratório usando WebGL.

Exemplo - Desenhe um cubo 3D giratório

O programa a seguir mostra como desenhar um cubo 3D giratório -

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============= Creating a canvas =================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*============ Defining and storing the geometry =========*/

         var vertices = [
            -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
            -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
            -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
            1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
            -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
            -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, 
         ];

         var colors = [
            5,3,7, 5,3,7, 5,3,7, 5,3,7,
            1,1,3, 1,1,3, 1,1,3, 1,1,3,
            0,0,1, 0,0,1, 0,0,1, 0,0,1,
            1,0,0, 1,0,0, 1,0,0, 1,0,0,
            1,1,0, 1,1,0, 1,1,0, 1,1,0,
            0,1,0, 0,1,0, 0,1,0, 0,1,0
         ];

         var indices = [
            0,1,2, 0,2,3, 4,5,6, 4,6,7,
            8,9,10, 8,10,11, 12,13,14, 12,14,15,
            16,17,18, 16,18,19, 20,21,22, 20,22,23 
         ];

         // Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         // Create and store data into index buffer
         var index_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

         /*=================== Shaders =========================*/

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+

            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);

         var shaderProgram = gl.createProgram();
         gl.attachShader(shaderProgram, vertShader);
         gl.attachShader(shaderProgram, fragShader);
         gl.linkProgram(shaderProgram);

         /* ====== Associating attributes to vertex shader =====*/
         var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix");
         var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix");
         var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix");

         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         var position = gl.getAttribLocation(shaderProgram, "position");
         gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ;

         // Position
         gl.enableVertexAttribArray(position);
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         var color = gl.getAttribLocation(shaderProgram, "color");
         gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;

         // Color
         gl.enableVertexAttribArray(color);
         gl.useProgram(shaderProgram);

         /*==================== MATRIX =====================*/

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 
            ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);

         var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
         var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];

         // translating z
         view_matrix[14] = view_matrix[14]-6;//zoom

         /*==================== Rotation ====================*/

         function rotateZ(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]-s*m[1];
            m[4] = c*m[4]-s*m[5];
            m[8] = c*m[8]-s*m[9];

            m[1]=c*m[1]+s*mv0;
            m[5]=c*m[5]+s*mv4;
            m[9]=c*m[9]+s*mv8;
         }

         function rotateX(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv1 = m[1], mv5 = m[5], mv9 = m[9];

            m[1] = m[1]*c-m[2]*s;
            m[5] = m[5]*c-m[6]*s;
            m[9] = m[9]*c-m[10]*s;

            m[2] = m[2]*c+mv1*s;
            m[6] = m[6]*c+mv5*s;
            m[10] = m[10]*c+mv9*s;
         }

         function rotateY(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]+s*m[2];
            m[4] = c*m[4]+s*m[6];
            m[8] = c*m[8]+s*m[10];

            m[2] = c*m[2]-s*mv0;
            m[6] = c*m[6]-s*mv4;
            m[10] = c*m[10]-s*mv8;
         }

         /*================= Drawing ===========================*/
         var time_old = 0;

         var animate = function(time) {

            var dt = time-time_old;
            rotateZ(mov_matrix, dt*0.005);//time
            rotateY(mov_matrix, dt*0.002);
            rotateX(mov_matrix, dt*0.003);
            time_old = time;

            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);

            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -

Neste capítulo, vamos dar um exemplo para demonstrar como desenhar um cubo 3D que pode ser girado usando os controles do mouse.

Exemplo - Desenhe um Cubo Interativo

O programa a seguir mostra como girar um cubo usando os controles do mouse -

<!doctype html>
<html>
   <body>
      <canvas width = "570" height = "570" id = "my_Canvas"></canvas>

      <script>
         /*============= Creating a canvas ======================*/
         var canvas = document.getElementById('my_Canvas');
         gl = canvas.getContext('experimental-webgl');

         /*========== Defining and storing the geometry ==========*/

         var vertices = [
            -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
            -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
            -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
            1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
            -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
            -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, 
         ];

         var colors = [
            5,3,7, 5,3,7, 5,3,7, 5,3,7,
            1,1,3, 1,1,3, 1,1,3, 1,1,3,
            0,0,1, 0,0,1, 0,0,1, 0,0,1,
            1,0,0, 1,0,0, 1,0,0, 1,0,0,
            1,1,0, 1,1,0, 1,1,0, 1,1,0,
            0,1,0, 0,1,0, 0,1,0, 0,1,0 
         ];

         var indices = [
            0,1,2, 0,2,3, 4,5,6, 4,6,7,
            8,9,10, 8,10,11, 12,13,14, 12,14,15,
            16,17,18, 16,18,19, 20,21,22, 20,22,23 
         ];

         // Create and store data into vertex buffer
         var vertex_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

         // Create and store data into color buffer
         var color_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

         // Create and store data into index buffer
         var index_buffer = gl.createBuffer ();
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

         /*=================== SHADERS =================== */

         var vertCode = 'attribute vec3 position;'+
            'uniform mat4 Pmatrix;'+
            'uniform mat4 Vmatrix;'+
            'uniform mat4 Mmatrix;'+
            'attribute vec3 color;'+//the color of the point
            'varying vec3 vColor;'+
            'void main(void) { '+//pre-built function
               'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+
               'vColor = color;'+
            '}';

         var fragCode = 'precision mediump float;'+
            'varying vec3 vColor;'+
            'void main(void) {'+
               'gl_FragColor = vec4(vColor, 1.);'+
            '}';

         var vertShader = gl.createShader(gl.VERTEX_SHADER);
         gl.shaderSource(vertShader, vertCode);
         gl.compileShader(vertShader);

         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
         gl.shaderSource(fragShader, fragCode);
         gl.compileShader(fragShader);

         var shaderprogram = gl.createProgram();
         gl.attachShader(shaderprogram, vertShader);
         gl.attachShader(shaderprogram, fragShader);
         gl.linkProgram(shaderprogram);

         /*======== Associating attributes to vertex shader =====*/
         var _Pmatrix = gl.getUniformLocation(shaderprogram, "Pmatrix");
         var _Vmatrix = gl.getUniformLocation(shaderprogram, "Vmatrix");
         var _Mmatrix = gl.getUniformLocation(shaderprogram, "Mmatrix");

         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         var _position = gl.getAttribLocation(shaderprogram, "position");
         gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0);
         gl.enableVertexAttribArray(_position);

         gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
         var _color = gl.getAttribLocation(shaderprogram, "color");
         gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ;
         gl.enableVertexAttribArray(_color);
         gl.useProgram(shaderprogram);

         /*==================== MATRIX ====================== */

         function get_projection(angle, a, zMin, zMax) {
            var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
            return [
               0.5/ang, 0 , 0, 0,
               0, 0.5*a/ang, 0, 0,
               0, 0, -(zMax+zMin)/(zMax-zMin), -1,
               0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 
			   ];
         }

         var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100);
         var mo_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];
         var view_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ];

         view_matrix[14] = view_matrix[14]-6;

         /*================= Mouse events ======================*/

         var AMORTIZATION = 0.95;
         var drag = false;
         var old_x, old_y;
         var dX = 0, dY = 0;

         var mouseDown = function(e) {
            drag = true;
            old_x = e.pageX, old_y = e.pageY;
            e.preventDefault();
            return false;
         };

         var mouseUp = function(e){
            drag = false;
         };

         var mouseMove = function(e) {
            if (!drag) return false;
            dX = (e.pageX-old_x)*2*Math.PI/canvas.width,
            dY = (e.pageY-old_y)*2*Math.PI/canvas.height;
            THETA+= dX;
            PHI+=dY;
            old_x = e.pageX, old_y = e.pageY;
            e.preventDefault();
         };

         canvas.addEventListener("mousedown", mouseDown, false);
         canvas.addEventListener("mouseup", mouseUp, false);
         canvas.addEventListener("mouseout", mouseUp, false);
         canvas.addEventListener("mousemove", mouseMove, false);

         /*=========================rotation================*/

         function rotateX(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv1 = m[1], mv5 = m[5], mv9 = m[9];

            m[1] = m[1]*c-m[2]*s;
            m[5] = m[5]*c-m[6]*s;
            m[9] = m[9]*c-m[10]*s;

            m[2] = m[2]*c+mv1*s;
            m[6] = m[6]*c+mv5*s;
            m[10] = m[10]*c+mv9*s;
         }

         function rotateY(m, angle) {
            var c = Math.cos(angle);
            var s = Math.sin(angle);
            var mv0 = m[0], mv4 = m[4], mv8 = m[8];

            m[0] = c*m[0]+s*m[2];
            m[4] = c*m[4]+s*m[6];
            m[8] = c*m[8]+s*m[10];

            m[2] = c*m[2]-s*mv0;
            m[6] = c*m[6]-s*mv4;
            m[10] = c*m[10]-s*mv8;
         }

         /*=================== Drawing =================== */

         var THETA = 0,
         PHI = 0;
         var time_old = 0;

         var animate = function(time) {
            var dt = time-time_old;

            if (!drag) {
               dX *= AMORTIZATION, dY*=AMORTIZATION;
               THETA+=dX, PHI+=dY;
            }

            //set model matrix to I4

            mo_matrix[0] = 1, mo_matrix[1] = 0, mo_matrix[2] = 0,
            mo_matrix[3] = 0,

            mo_matrix[4] = 0, mo_matrix[5] = 1, mo_matrix[6] = 0,
            mo_matrix[7] = 0,

            mo_matrix[8] = 0, mo_matrix[9] = 0, mo_matrix[10] = 1,
            mo_matrix[11] = 0,

            mo_matrix[12] = 0, mo_matrix[13] = 0, mo_matrix[14] = 0,
            mo_matrix[15] = 1;

            rotateY(mo_matrix, THETA);
            rotateX(mo_matrix, PHI);

            time_old = time; 
            gl.enable(gl.DEPTH_TEST);

            // gl.depthFunc(gl.LEQUAL);

            gl.clearColor(0.5, 0.5, 0.5, 0.9);
            gl.clearDepth(1.0);
            gl.viewport(0.0, 0.0, canvas.width, canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.uniformMatrix4fv(_Pmatrix, false, proj_matrix);
            gl.uniformMatrix4fv(_Vmatrix, false, view_matrix);
            gl.uniformMatrix4fv(_Mmatrix, false, mo_matrix);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
            gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

            window.requestAnimationFrame(animate);
         }
         animate(0);
      </script>
   </body>
</html>

Se você executar este exemplo, ele produzirá a seguinte saída -