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:
Profissionais 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:
Para 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.
A 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:
O 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.
### 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],
}
A 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:
### 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:
### 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],
}
Para 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],
}
A 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',
}
}
Funcionalidades 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],
}
O 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',
},
}
### 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],
}
As 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,
},
}
A 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],
}
Racks 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.
### 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',
},
}
Conclusã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:
Este 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.