Visão Geral dos Materiais no Three.js
No Three.js, os materiais podem ser classificados em duas categorias principais:
- Materiais Simples: Não respondem à iluminação da cena
- Materiais Físicos (PBR): Simulam interações realistas com a luz, apresentando variações visuais conforme a iluminação
Para prototipagem rápida, MeshNormalMaterial é uma excelente escolha por ser leve e oferecer percepção imediata da geometria. Em produção, MeshStandardMaterial é recomendado para resultados fotorealistas, especialmente quando combinado com environment maps para capturar reflexões do entorno.
MeshBasicMaterial — O Material Básico
Este material ignora completamente a iluminação da cena. Seus atributos principais incluem:
color: Cor uniforme da superfíciewireframe: Exibe apenas a estrutura de arestasopacityetransparent: Controlam transparência
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const cena = new THREE.Scene();
const perspectiva = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
100
);
perspectiva.position.set(2, 2, 6);
const geometriaCaixa = new THREE.BoxGeometry(1.8, 1.8, 1.8);
const materialBasico = new THREE.MeshBasicMaterial({
color: 0x2196f3,
wireframe: false
});
const caixa = new THREE.Mesh(geometriaCaixa, materialBasico);
cena.add(caixa);
const renderizador = new THREE.WebGLRenderer({ antialias: true });
renderizador.setPixelRatio(window.devicePixelRatio);
renderizador.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderizador.domElement);
const orbitControls = new OrbitControls(perspectiva, renderizador.domElement);
orbitControls.enableDamping = true;
function cicloRenderizacao() {
caixa.rotation.y += 0.005;
orbitControls.update();
renderizador.render(cena, perspectiva);
requestAnimationFrame(cicloRenderizacao);
}
cicloRenderizacao();
window.addEventListener('resize', () => {
perspectiva.aspect = window.innerWidth / window.innerHeight;
perspectiva.updateProjectionMatrix();
renderizador.setSize(window.innerWidth, window.innerHeight);
});
Trabalhando com o Objeto Color
O Three.js armazena cores no intervalo [0, 1] como ponto flutuante. Quando atribuímos um valor hexadecimal, internamente o framework realiza a normalização:
// Conversão simplificada de hexadecimal para RGB normalizado
const hexValue = 0x2196f3;
const componenteR = ((hexValue >> 16) & 0xff) / 255; // 0.129
const componenteG = ((hexValue >> 8) & 0xff) / 255; // 0.588
const componenteB = (hexValue & 0xff) / 255; // 0.953
Isso significa que ao manipular propriedaeds de cor via código, você pode tanto usar hexadecimal quanto componentes RGB em ponto flutuante diretamente:
// Ambas as formas são equivalentes
materialBasico.color.set(0xff5722);
materialBasico.color.setRGB(1.0, 0.34, 0.13);
MeshNormalMaterial — Visualização de Normais
O MeshNormalMaterial utiliza os vetores normais de cada superfície como valores de cor. Como normais são vetores tridimensionais com componentes x, y, z no intervalo [0, 1], cada componente é mapeada diretamante para os canais R, G e B.
Isso produz uma coloração arco-íris intuitiva que revela a orientação de cada face, sendo extremamente útil para debugging de geometrias.
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const cena = new THREE.Scene();
const perspectiva = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
100
);
perspectiva.position.set(3, 3, 5);
const geoEsfera = new THREE.SphereGeometry(1.5, 64, 64);
const matNormais = new THREE.MeshNormalMaterial({
flatShading: false
});
const esfera = new THREE.Mesh(geoEsfera, matNormais);
cena.add(esfera);
const renderizador = new THREE.WebGLRenderer({ antialias: true });
renderizador.setPixelRatio(window.devicePixelRatio);
renderizador.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderizador.domElement);
const controles = new OrbitControls(perspectiva, renderizador.domElement);
function animacao() {
esfera.rotation.x += 0.003;
esfera.rotation.z += 0.003;
controles.update();
renderizador.render(cena, perspectiva);
requestAnimationFrame(animacao);
}
animacao();
window.addEventListener('resize', () => {
perspectiva.aspect = window.innerWidth / window.innerHeight;
perspectiva.updateProjectionMatrix();
renderizador.setSize(window.innerWidth, window.innerHeight);
});
Materiais PBR — Simulação de Iluminação Física
Os materiais PBR (Physically Based Rendering) simulam como a luz interage com superfícies reais. Dois modelos clássicos merecem destaque:
- MeshLambertMaterial: Calcula reflexão difusa (lambertiana), ideal para superfícies opacas como madeira, concreto e tecidos
- MeshPhongMaterial: Adiciona reflexão especular, perfeito para superfícies brilhantes como metal polido e plástico
Indispensável: Fontes de Luz
Diferentemente do material básico, os materiais PBR requerem fontes de luz na cena. Sem iluminação, os objetos aparecem completamente pretos.
// Iluminação uniforme — sem sombras, apenas preenche a cena
const luzAmbiente = new THREE.AmbientLight(0xffffff, 0.4);
cena.add(luzAmbiente);
// Luz pontual — emite radiação em todas as direções a partir de um ponto
const luzPonto = new THREE.PointLight(0xffffff, 80);
luzPonto.position.set(5, 8, 5);
cena.add(luzPonto);
Exemplo: Construindo uma Árvore Estilizada
Para demonstrar ambos os materiais, vamos criar uma composição simples de árvore usando um cone (copa) e um cilindro (tronco).
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const cena = new THREE.Scene();
cena.background = new THREE.Color(0x87ceeb);
const cam = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
200
);
cam.position.set(8, 6, 10);
// ── Copa da árvore (Lambert) ──
const geoCopa = new THREE.ConeGeometry(2.5, 6, 24);
const matCopa = new THREE.MeshLambertMaterial({ color: 0x2e7d32 });
const copa = new THREE.Mesh(geoCopa, matCopa);
copa.position.y = 6;
// ── Tronco da árvore (Phong) ──
const geoTronco = new THREE.CylinderGeometry(0.6, 0.8, 4, 16);
const matTronco = new THREE.MeshPhongMaterial({
color: 0x6d4c41,
shininess: 15
});
const tronco = new THREE.Mesh(geoTronco, matTronco);
tronco.position.y = 2;
// ── Agrupamento ──
const arvore = new THREE.Group();
arvore.add(copa);
arvore.add(tronco);
cena.add(arvore);
// ── Plano do chão ──
const geoPlano = new THREE.PlaneGeometry(40, 40);
const matPlano = new THREE.MeshLambertMaterial({ color: 0x4caf50 });
const plano = new THREE.Mesh(geoPlano, matPlano);
plano.rotation.x = -Math.PI / 2;
cena.add(plano);
// ── Iluminação ──
const solAmbiente = new THREE.AmbientLight(0xffffff, 0.3);
cena.add(solAmbiente);
const solPonto = new THREE.PointLight(0xfff8e1, 120);
solPonto.position.set(10, 15, 10);
cena.add(solPonto);
// ── Renderização ──
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const orbita = new OrbitControls(cam, renderer.domElement);
orbita.target.set(0, 3, 0);
orbita.update();
function loop() {
orbita.update();
renderer.render(cena, cam);
requestAnimationFrame(loop);
}
loop();
window.addEventListener('resize', () => {
cam.aspect = window.innerWidth / window.innerHeight;
cam.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Comparação Visual
Ao observar a cena acima, note que o tronco com material MeshPhongMaterial exibe um brilho especular sutil na superfície, enquanto a copa com MeshLambertMaterial apresenta apenas difusão suave. Ajuste o parâmetro shininess do material Phong para controlar a intensidade do reflexo especular — valores maiores produzem realces menores e mais concentrados.