Criação de um Sistema de Monitoramento de Sala de Servidores 3D com WebGL

Após a conclusão de uma fase de desenvolvimento de projeto, surgiu a oportunidade de consolidar os resultados obtidos nos últimos meses. Embora o desenvolvimento com HTML5 seja relativamente recente para mim, e minha compreensão de JavaScript ainda esteja em aprofundamento, a experiência em Java moldou minha forma de pensar, e a transição para as características e conceitos de JavaScript tem sido um processo de adaptação.

A primeira parte desta série abordou a visualização de grandes volumes de dados com um gráfico de explosão de arco-íris, utilizando primariamente a tecnologia de renderização 2D do Canvas HTML5. Nesta etapa, o foco será em uma tecnologia de destaque do projeto: o 3D do HTML5, e como utilizá-lo para construir um sofisticado sistema de monitoramento de salas de servidores em 3D.

Objetivo Visual

A imagem abaixo, fornecida pelo cliente, serviu como referência visual, com a expectativa de que a sala de servidores alcançasse um nível de detalhe 3D semelhante:

Imagem de Referência de Sala de Servidores 3DProfissionais da área reconhecerão que esta é uma imagem de renderização de design, que exigiria um esforço considerável de modelagem, mesmo utilizando softwares como o 3ds Max. Dado o contexto de um projeto de visualização de data center, a abordagem de um programador difere significativamente. Superando o ímpeto inicial de frustração, a prioridade foi estabelecer uma base sólida com a construção de uma cena WebGL.

Estrutura Básica de WebGL

A utilização de 3D no HTML5 é acessível através do WebGL, um subconjunto do OpenGL para navegadores, que expõe interfaces para a maioria das funcionalidades 3D. Navegadores modernos oferecem bom suporte, com o Internet Explorer necessitando da versão 11.

Para verificar o suporte do seu navegador a WebGL, visite http://get.webgl.org/. Se um cubo giratório for exibido, seu navegador é compatível. Caso contrário, experimente o navegador Chrome, que geralmente apresenta o melhor suporte e desempenho para WebGL.

A implementação de WebGL em um navegador requer o estudo de suas tecnologias e métodos. Desenvolver aplicações 3D não é uma tarefa trivial. Mesmo a configuração mais simples de uma cena WebGL demanda um código como o seguinte:


var larguraTela = window.innerWidth;
var alturaTela = window.innerHeight;
var container = document.createElement('div');
document.body.appendChild(container);
var canvasWebGL = document.createElement('canvas');
container.appendChild(canvasWebGL);
var gl = canvasWebGL.getContext('experimental-webgl');

function atualizarFrame() {
   gl.viewport(0, 0, larguraTela, alturaTela);
   gl.clearColor(0.4, 0.4, 0.7, 1);
   gl.clear(gl.COLOR_BUFFER_BIT);
   setTimeout(atualizarFrame, 20);
}

setTimeout(atualizarFrame, 20);
 

Semelhante ao HTML, é necessário criar um elemento canvas e obter seu contexto WebGL:


var gl = canvasWebGL.getContext("experimental-webgl");
 

Posteriormente, uma função atualizarFrame é empregada para renderizar o conteúdo 3D, de forma análoga ao contexto 2D do HTML5. É crucial manter um loop contínuo que invoque esta função em intervalos regulares para redesenhar a cena. Diferentemente do 2D, cenas 3D são dinâmicas, exigindo atualizações constantes, como a reprodução de um filme. Embora loops infinitos sejam tecnicamente necessários, otimizações em projetos reais visam a atualização "sob demanda" para economizar recursos, especialmente em dispositivos móveis. Para os interessados, um artigo dedicado a essas otimizações pode ser futuramente elaborado.

O código apresentado, embora não renderize conteúdo 3D visível, constitui um programa WebGL funcional, estabelecendo a base para a construção da nossa sala de servidores 3D.

Encapsulamento de Objetos

A complexidade e o cronograma do projeto tornam o uso de ferramentas de tercieros indispensável. Opções como Three.js e Twaver.js oferecem objetos 3D básicos e efeitos diversos. O principal benefício reside na capacidade de alcançar o visual desejado. Para minimizar modificações extensivas no código, implementamos um sistema de encapsulamento que permite definir objetos 3D primitivos (como cubos) através de dados JSON. Essa abordagem facilita a definição e modificação dos elementos:


var dadosConfiguracao = {
   objetos: [
       {
           nome: 'Piso',
           // ... outras propriedades
       },
       // ... outros objetos
   ]
};
 

A seguir, detalharemos o processo de embelezamento desses objetos 3D, um passo a passo que, embora possa parecer prolixo, constrói uma base sólida para desenvolvimentos futuros.

Piso e Rampa

O primeiro objeto a ser implementado é o piso. Em um contexto 3D, o piso pode ser representado por um cubo com espessura e uma textura quadriculada. A definição em JSON para um objeto de piso encapsulado seria:


{
   nome: 'Piso',
   tipo: 'cubo',
   largura: 1600,
   altura: 10,
   profundidade: 1300,
   estilo: {
       'm.cor': '#BEC9BE',
       'm.ambiente': '#BEC9BE',
   }
}
 

Esta configuração cria uma placa de piso com dimensões de 13m x 16m, correspondendo ao tamanho de uma sala de servidores compacta:

Piso BásicoPara aprimorar a aparência, é necessária uma imagem de textura de piso. É importante notar que as dimensões das texturas devem ser potências de dois (por exemplo, 128x128, 256x256) para garantir a qualidade visual. Além disso, a textura deve permitir a repetição contínua sem emendas visíveis.

Textura de PisoA textura é adicionada à seção estilo:


'top.m.texture.imagem': 'imagens/piso.png',
'top.m.texture.repetir': new mono.Vec2(10, 10),
 

O resultado visual é significativamente melhorado com a aplicação da textura:

Piso com TexturaO cliente também solicitou a inclusão de uma rampa de acesso na parte inferior da sala, para facilitar o transporte de equipamentos. Uma abordagem para implementar isso é utilizar as capacidades de operação booleana do Twaver, permitindo subtrair um cubo inclinado do piso. A definição JSON para a rampa seria:


{
   nome: 'Corte Rampa Piso',
   tipo: 'cubo',
   largura: 200,
   altura: 20,
   profundidade: 260,
   translacao: [-348, 0, 530],
   rotacao: [Math.PI / 180 * 3, 0, 0],
   operacao: '-', // Indica subtração
   estilo: {
       // ... propriedades de estilo do cubo de corte
   }
}
 

Nesta configuração, um cubo inclinado é posicionado e rotacionado. O parâmetro operacao: '-' especifica que este cubo será subtraído do objeto principal. O cubo subtraído também pode ter seus próprios materiais, texturas e cores.

Piso com Rampa### Mesa de Corredor

O próximo item solicitado foi uma mesa de recepção no corredor. Para simplificar, utilizamos um cubo como representação:


{
   nome: 'Banco Corredor',
   tipo: 'cubo',
   largura: 300,
   altura: 50,
   profundidade: 100,
   translacao: [350, 0, -500],
}
 

Mesa de CorredorA simplicidade na modelagem de objetos não essenciais é crucial para a performance em 3D. Mesmo um cubo, quando integrado ao estilo geral e com a adição de sombreamento, contribui para a ambientação:

Mesa de Corredor com Sombreamento### Paredes

As paredes são um componente vital da sala de servidores, e a iluminação e sombras adequadas aumentam o realismo. Como as paredes podem possuir contornos irregulares, o motor gráfico suporta a criação de objetos baseados em caminhos definidos por coordenadas. A definição JSON para uma parede:


{
   nome: 'Parede Principal',
   tipo: 'caminho',
   largura: 20,
   altura: 200,
   translacao: [-500, 0, -500],
   dados: [
       [0, 0],
       [1000, 0],
       [1000, 500],
       [500, 500],
       [500, 1000],
       [0, 1000],
       [0, 0],
   ],
}
 

Neste caso, o tipo é caminho, e os dados são um array bidimensional de coordenadas XY. A altura é definida separadamente. A aplicação de cores e sombras melhora significativamente a aparência:

Parede com Cores e Sombras Detalhe da Parede### Portas

As portas são elementos importantes para o sistema de monitoramento, exigindo posicionamento preciso e animação de abertura/fechamento. Para integrar a porta à parede, criamos primeiramente um "buraco" na parede:


{
   nome: 'Abertura Porta',
   tipo: 'cubo',
   largura: 195,
   altura: 170,
   profundidade: 30,
   operacao: '-',
   translacao: [-350, 2, 500],
}
 

Abertura de PortaPara dar um acabamento mais realista, adicionamos uma moldura antes de criar a abertura:


{
   nome: 'Moldura Porta',
   tipo: 'cubo',
   largura: 205,
   altura: 180,
   profundidade: 26,
   operacao: '+',
   translacao: [-350, 0, 500],
}
 

Porta com MolduraA porta em si é um cubo fino, com transparência e textura de imagem para simular vidro:


{
   nome: 'Porta Esquerda',
   tipo: 'cubo',
   largura: 93,
   altura: 165,
   profundidade: 2,
   translacao: [-397, 4, 500],
   estilo: {
       'm.transparente': true,
       'm.texture.imagem': 'imagens/porta_esquerda.png',
   }
}
 

Porta EsquerdaFuncionalidades de animação, como abertura e fechamento ao duplo clique, são implementadas diretamente no JSON, especificando o tipo de animação. É crucial que as animações das portas esquerda e direita tenham direções opostas para um comportamento visualmente coerente.

Janelas

As janelas, embora não possuam funcionalidades de negócio diretas, são essenciais para a estética. Seguimos um método similar ao das portas: definimos uma abertura e adicionamos um peitoril. A criação de uma janela com um peitoril:


{
   nome: 'Abertura Janela Principal',
   tipo: 'cubo',
   largura: 420,
   altura: 150,
   profundidade: 50,
   operacao: '-',
   translacao: [200, 30, 500],
},{
   nome: 'Peitoril Janela Principal',
   tipo: 'cubo',
   largura: 420,
   altura: 10,
   profundidade: 40,
   operacao: '+',
   translacao: [200, 30, 510],
}
 

Janela com PeitorilO vidro da janela é adicionado com transparência, cor e reflexos para simular o material:


{
   nome: 'Vidro Janela Principal',
   tipo: 'cubo',
   largura: 420,
   altura: 150,
   profundidade: 2,
   operacao: '+',
   translacao: [200, 30, 500],
   estilo: {
       'm.transparente': true,
       'm.opacidade': 0.4,
       'm.cor': '#58ACFA',
   },
}
 

Janela com Vidro Colorido### Paredes Externas

Para completar o exterior, adicionamos paredes com divisórias de corredor e grandes janelas de vidro. Paredes retas podem ser definidas como cubos:


{
   nome: 'Parede Externa Esquerda',
   tipo: 'cubo',
   largura: 20,
   altura: 200,
   profundidade: 1300,
   operacao: '+',
   translacao: [-790, 0, 0],
}
 

Parede Externa EsquerdaAs aberturas para as janelas são criadas com a operação de subtração:


{
   nome: 'Abertura Janela Parede Esquerda',
   tipo: 'cubo',
   largura: 30,
   altura: 110,
   profundidade: 1300,
   operacao: '-',
   translacao: [-790, 60, 0],
}
 

A inserção de vidro translúcido com reflexos confere realismo:


{
   nome: 'Vidro Parede Externa Esquerda',
   tipo: 'cubo',
   largura: 4,
   altura: 110,
   profundidade: 1300,
   operacao: '+',
   translacao: [-790, 60, 0],
   estilo: {
       'm.transparente': true,
       'm.opacidade': 0.6,
   },
}
 

Parede Externa com VidroA adição de elementos decorativos como plantas, criadas com a combinação de formas geométricas básicas e texturas, completa o ambiente:


{
   nome: 'Planta 1',
   tipo: 'planta',
   translacao: [560, 0, 400],
}
 

Ambiente Completo com PlantasRacks e Equipamentos

A parte central do sistema de monitoramento são os racks e os equipamentos que eles contêm. No projeto real, esses dados são armazenados em um banco de dados e carregados via API JSON. Para demonstração, apresentamos alguns racks:

Os racks são inicialmente renderizados como cubos com texturas. Um mecanismo de carregamento lazy (preguiçoso) é empregado: os detalhes internos dos servidores só são carregados quando o usuário interage com o rack (por exemplo, com um duplo clique). O rack é então "escavado" para parecer oco, simulando seu interior:


{
   nome: 'Rack',
   tipo: 'rack',
   preguicoso: true, // Habilita carregamento preguiçoso
   largura: 70,
   profundidade: 100,
   altura: 220,
   translacao: [-370, 0, -250],
   severidade: CRITICAL, // Indica nível de alarme
}
 

A propriedade preguicoso determina se o conteúdo do rack será carregado posteriormente. A severidade permite a exibição de indicadores visuais de alarme.

Múltiplos Racks### Equipamentos

Os equipamentos gerenciados são predominantemente de rack, com dimensões padronizadas, facilitando a organização dentro dos racks. Após definir o modelo 3D de um equipamento, ele pode ser instanciado e posicionado com base em informações do banco de dados:

A renderização de equipamentos de rede individuais, e potencialmente seus componentes (placas, portas), pode ser feita com carregamento adicional dinâmico. Estratégias de carregamento/descarregamento dinâmico podem ser usadas para otimizar a performance.

Televisão

Como um elemento decorativo adicional, uma televisão foi adicionada à parede. O processo envolve a criação de um cubo, a subtração de uma área para a tela e a inserção de um vidro transparente com uma imagem de tela:


{
   nome: 'Corpo Televisao',
   tipo: 'cubo',
   largura: 150,
   altura: 80,
   profundidade: 5,
   operacao: '+',
   translacao: [80, 100, 13],
},{
   nome: 'Abertura Tela Televisao',
   tipo: 'cubo',
   largura: 130,
   altura: 75,
   profundidade: 5,
   operacao: '-',
   translacao: [80, 102.5, 17],
},{
   nome: 'Tela Televisao',
   tipo: 'cubo',
   largura: 130,
   altura: 75,
   profundidade: 1,
   operacao: '+',
   translacao: [80, 102.5, 14.6],
   estilo: {
       'frente.m.texture.imagem': 'imagens/tela.jpg',
   },
}
 

Televisão na ParedeConclusão

A construção desta cena 3D demonstrou que a estética em sistemas 3D não depende exclusivamente da fidelidade fotográfica dos modelos. A apresentação geral pode ser altamente eficaz com o uso adequado de técnicas e ferramentas. O resultado final:

Cena 3D CompletaEste sistema 3D interativo, navegável e com animações pode ser acessado diretamente pelo navegador, sem a necessidade de plugins. A concepção e implementação, utilizando apenas um arquivo JSON e poucas centenas de linhas de código, foram realizadas em um único dia, um resultado surpreendente.

A ausência de plugins externos, softwares de modelagem complexos e bibliotecas pesadas resulta em uma aplicação leve e acessível em dispositivos móveis. Otimizações garantiram um tempo de carregamento inferior a 600 milissegundos, com navegação e zoom fluidos.

Embora a busca por aprimoramento técnico e estético seja contínua, e as demandas dos usuários variadas, a escolha de tecnologias e ferramentas adequadas, como o HTML5, potencializa significativamente a eficiência do desenvolvimento.

Tags: WebGL html5 javascript 3d Visualização de Dados

Publicado em 6-29 17:41