UtkarshBirla28/threejs-geometry

Criação e modelagem de geometria three.js: formas nativas, texto 3D, buffers de vértices e atributos, mesclagem, instanciamento, batching, nuvens de pontos e linhas, em WebGL ou WebGPU com TSL. Use ao construir formas 3D, texto, dados de vértices personalizados, malhas personalizadas, malhas instanciadas ou nuvens de pontos.

O que é threejs-geometry?

Este skill fornece um guia abrangente e prático para trabalhar com geometria no Three.js, cobrindo desde as formas geométricas nativas (caixa, esfera, cilindro, toro, etc.) até conceitos avançados como BufferGeometry personalizada, atributos de vértices, instanciamento (InstancedMesh) e o uso de WebGPU com TSL. É extremamente útil para desenvolvedores que precisam criar cenas 3D, visualizações de dados, jogos ou animações, oferecendo exemplos de código prontos para uso e explicações detalhadas sobre parâmetros e boas práticas. O skill também inclui uma seção de override local para um pipeline específico (animated-shorts) que usa Three.js r175 com Remotion, ajustando as recomendações para essa versão mais antiga. As plataformas suportadas incluem WebGLRenderer e WebGPURenderer, com ênfase em TSL para shaders em WebGPU. O funcionamento é baseado na criação e manipulação de objetos Geometry e BufferGeometry, fornecendo métodos para definir posições, normais, UVs, cores e índices, além de técnicas de otimização como interleaved buffers e instanciamento.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/UtkarshBirla28/animated-shorts-pipeline/tree/HEAD/.claude/skills/threejs-geometry

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Geometria Three.js

Tem como alvo three.js r186. Exemplos usam WebGLRenderer; a seção WebGPU e TSL usa WebGPURenderer.

Substituição local (adicionada em 2026-11-10 por Claude para este PC; não faz parte do skill original). Nosso pipeline de curtas animados (<ROOT>\studio, do mromidev/claude-animated-shorts) está fixado na versão three 0.175 (r175)

  • Remotion 4.0.290 + @react-three/fiber 9.1.2 + pmndrs postprocessing 6.37.2. Mantenha three fixado: shade.ts/cardshade.ts fazem patch de strings de shader do three. Ao trabalhar nesse pipeline:
  • Ignore "Erros Comuns" / conselhos de API que precisam de r176+ (HDRLoader, Timer, PCFSoftShadowMap deprecation, setEffects, RenderPipeline, outputBufferType, mesh.pivot, Capsule height, TSL/WebGPU). Verifique se a API existe no r175 primeiro.
  • Nunca anime com useFrame, Clock, Timer, delta time, setAnimationLoop ou requestAnimationFrame. O Remotion renderiza quadro a quadro: derive cada valor de useCurrentFrame() / fps (e mixer.setTime(frame / fps) para clipes).
  • Pós-processamento é pmndrs postprocessing, não three/addons EffectComposer: não adicione um OutputPass (dupla codificação sRGB).
  • Cores Toon: o mapeamento de tom é NEUTRAL em nosso compositor; cuidado com padrões ACES e tingimento de tonemapping_fragment.
  • Para outros projetos three.js, as orientações originais para r186 abaixo se aplicam conforme escritas.

Início Rápido

import * as THREE from "three";

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);

// Uma geometria é a forma; muitas malhas podem compartilhar uma instância de geometria
const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial());
scene.add(mesh);

renderer.setAnimationLoop(animate);

function animate() {
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
}

Geometrias Nativas

Formas Básicas

new THREE.BoxGeometry(1, 1, 1); // largura, altura, profundidade, segmentosLargura, segmentosAltura, segmentosProfundidade
new THREE.SphereGeometry(1, 32, 16); // raio, segmentosLargura, segmentosAltura, phiInicio, phiComprimento, thetaInicio, thetaComprimento
new THREE.SphereGeometry(1, 32, 16, 0, Math.PI); // meia esfera (varredura phi, corte através dos polos)
new THREE.SphereGeometry(1, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2); // cúpula (corte no equador)
new THREE.PlaneGeometry(10, 10, 1, 1); // largura, altura, segmentosLargura, segmentosAltura
new THREE.CircleGeometry(1, 32); // raio, segmentos, thetaInicio, thetaComprimento
new THREE.CylinderGeometry(1, 1, 2, 32); // raioTopo, raioBase, altura, segmentosRadiais, segmentosAltura, extremidadesAbertas, thetaInicio, thetaComprimento
new THREE.CylinderGeometry(1, 1, 2, 6); // prisma hexagonal
new THREE.ConeGeometry(1, 2, 32); // raio, altura, segmentosRadiais, segmentosAltura, extremidadesAbertas, thetaInicio, thetaComprimento
new THREE.TorusGeometry(1, 0.4, 16, 100); // raio, tubo, segmentosRadiais, segmentosTubulares, arco, thetaInicio, thetaComprimento
new THREE.TorusGeometry(1, 0.4, 16, 100, Math.PI); // meio arco
new THREE.TorusKnotGeometry(1, 0.4, 100, 16, 2, 3); // raio, tubo, segmentosTubulares, segmentosRadiais, p, q
new THREE.RingGeometry(0.5, 1, 32, 1); // raioInterno, raioExterno, segmentosTheta, segmentosPhi, thetaInicio, thetaComprimento

Formas Avançadas

new THREE.CapsuleGeometry(0.5, 1, 4, 8, 1); // raio, altura (apenas a seção do meio), segmentosCap, segmentosRadiais, segmentosAltura

// raio, detalhe - cada nível de detalhe subdivide cada face
new THREE.IcosahedronGeometry(1, 0); // 20 faces no detalhe 0
new THREE.DodecahedronGeometry(1, 0);
new THREE.OctahedronGeometry(1, 0);
new THREE.TetrahedronGeometry(1, 0);

// Poliedro - array de vértices plano, índices, raio, detalhe
const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1];
const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1];
new THREE.PolyhedronGeometry(vertices, indices, 1, 0);

Formas Baseadas em Caminhos

// Torno - um perfil Vector2 rotacionado em torno do eixo Y: pontos, segmentos, phiInicio, phiComprimento
const profile = [
  new THREE.Vector2(0, 0), new THREE.Vector2(0.5, 0), new THREE.Vector2(0.5, 1), new THREE.Vector2(0, 1),
];
new THREE.LatheGeometry(profile, 32);

// Extrusão - uma forma 2D empurrada ao longo de Z
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(1, 0);
shape.lineTo(1, 1);
shape.lineTo(0, 1);
new THREE.ExtrudeGeometry(shape, {
  steps: 1, depth: 1, bevelEnabled: true,
  bevelThickness: 0.1, bevelSize: 0.1, bevelSegments: 3,
});

// Tubo - uma varredura circular ao longo de uma curva: caminho, segmentosTubulares, raio, segmentosRadiais, fechado
const curve = new THREE.CatmullRomCurve3([
  new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0),
]);
new THREE.TubeGeometry(curve, 64, 0.2, 8, false);

Geometria de Texto

import { FontLoader } from "three/addons/loaders/FontLoader.js";
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";

// Uma fonte .typeface.json, de examples/fonts no repositório three.js
const font = await new FontLoader().loadAsync("fonts/helvetiker_regular.typeface.json");
const geometry = new TextGeometry("Olá", {
  font: font,
  size: 1, // tamanho do texto em unidades do mundo
  depth: 0.2, // profundidade da extrusão
  curveSegments: 12,
  bevelEnabled: true,
  bevelThickness: 0.03,
  bevelSize: 0.02,
  direction: "ltr", // 'ltr', 'rtl' (direita para esquerda) ou 'tb' (cima para baixo)
});

goometry.center(); // recentraliza os glifos na origem
scene.add(new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()));

BufferGeometry

A classe base de toda geometria: arrays tipados de dados de vértices mais um índice opcional. Atributos são buffers nomeados com valores itemSize por vértice; position, normal e uv são os que os materiais leem por padrão.

BufferGeometry Personalizada

const geometry = new THREE.BufferGeometry();

// Quatro vértices, três floats cada (x, y, z)
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]);
goometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

// Índices reutilizam vértices nos dois triângulos
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
goometry.setIndex(new THREE.BufferAttribute(indices, 1));

// Normais dirigem a iluminação, UVs endereçam texturas
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
goometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
goometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
// Cores opcionais por vértice, renderizadas com material.vertexColors = true
const colors = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0]);
goometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));

scene.add(new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ vertexColors: true })));

Tipos de Atributo

const count = 4;
const geometry = new THREE.BufferGeometry();
// Float32 para posições, normais, UVs e cores de ponto flutuante
goometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(count * 3), 3));
// Índices Uint16 endereçam vértices 0 a 65534 (65535 é o valor de reinicialização primitiva); setIndex([...]) com um array simples escolhe Uint16 ou Uint32 automaticamente
goometry.setIndex(new THREE.BufferAttribute(new Uint16Array(count), 1));
// Cores Uint8 precisam de normalized = true (terceiro argumento) para que os bytes cheguem ao shader como 0.0-1.0
goometry.setAttribute("color", new THREE.BufferAttribute(new Uint8Array(count * 3), 3, true));

Atualizando uma BufferGeometry

const geometry = new THREE.PlaneGeometry(2, 2);
const positions = geometry.attributes.position;
positions.setXYZ(0, -2, -2, 0); // sobrescreve o vértice 0
positions.getX(0); // lê componentes individuais com getX / getY / getZ
positions.needsUpdate = true; // envia a alteração; um array não pode crescer após sua primeira renderização, então aloque o máximo e use geometry.setDrawRange(0, n)
goometry.computeVertexNormals(); // normais devem corresponder às novas posições
goometry.computeBoundingBox(); // volumes delimitadores dirigem o frustum culling e raycasting
goometry.computeBoundingSphere();

Buffers Intercalados

// Um buffer contendo posição (3 floats) e uv (2 floats) por vértice
const interleavedBuffer = new THREE.InterleavedBuffer(
  new Float32Array([
    -1, -1, 0, 0, 0, 1, -1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 0, 0, 1, // quatro vértices: xyz + uv
  ]),
  5, // stride: floats por vértice
);
const geometry = new THREE.BufferGeometry();
goometry.setAttribute("position", new THREE.InterleavedBufferAttribute(interleavedBuffer, 3, 0));
goometry.setAttribute("uv", new THREE.InterleavedBufferAttribute(interleavedBuffer, 2, 3));
goometry.setIndex([0, 1, 2, 0, 2, 3]); // dois triângulos sobre os quatro vértices
// BufferGeometryUtils.interleaveAttributes([position, normal, uv]) empacota atributos existentes desta forma

Arestas e Wireframes

const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
// Arestas onde as normais das duas faces diferem em pelo menos thresholdAngle (graus; 15 aqui), mais arestas de limite abertas
const edges = new THREE.EdgesGeometry(boxGeometry, 15);
const edgeLines = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xffffff }));
// Cada aresta de triângulo, diagonais incluídas; uma aresta compartilhada por dois triângulos é desenhada uma vez
const wireframe = new THREE.WireframeGeometry(boxGeometry);
const wireLines = new THREE.LineSegments(wireframe, new THREE.LineBasicMaterial({ color: 0xffffff }));
scene.add(edgeLines, wireLines);

Pontos

const count = 1000;
const positions = new Float32Array(count * 3).map(() => (Math.random() - 0.5) * 10);
const geometry = new THREE.BufferGeometry();
goometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
// sizeAttenuation (padrão true) diminui pontos com profundidade sob uma câmera perspectiva: um ponto abrange size * (altura CSS do canvas / 2) / profundidade pixels CSS
const material = new THREE.PointsMaterial({ size: 0.1, color: 0xffffff });
const points = new THREE.Points(geometry, material);
scene.add(points);

Sob WebGPURenderer, o backend WebGPU desenha pontos com um pixel independentemente do tamanho do material; veja a seção WebGPU e TSL para o Sprite instanciado que restaura tamanhos.

Linhas

const linePoints = [
  new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0),
];
const geometry = new THREE.BufferGeometry().setFromPoints(linePoints);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });

// Line conecta os pontos em ordem; LineLoop fecha de volta ao primeiro ponto
const line = new THREE.Line(geometry, material);
const loop = new THREE.LineLoop(geometry, material);
scene.add(line, loop);

// LineSegments desenha pares independentes: vértice 0-1, 2-3, ...
const segmentsGeometry = new THREE.BufferGeometry().setFromPoints([linePoints[0], linePoints[1], linePoints[1], linePoints[2]]);
const segments = new THREE.LineSegments(segmentsGeometry, material);
scene.add(segments);

LineLoop não é suportado sob WebGPURenderer: registra um erro e não renderiza nada. Feche um loop lá repetindo o primeiro ponto e usando Line.

InstancedMesh

Uma chamada de draw para muitas cópias de uma geometria, cada uma com sua própria transformação.

const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
const material = new THREE.MeshBasicMaterial(); // multiplica pelas cores das instâncias; MeshNormalMaterial as ignora
const count = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
  dummy.position.set((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20);
  dummy.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, 0);
  dummy.scale.setScalar(0.5 + Math.random());
  dummy.updateMatrix();
  instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true; // necessário após alterações uma vez que a malha tenha sido renderizada; inofensivo antes

// setColorAt cria instanceColor no primeiro uso; sinalize após o loop
for (let i = 0; i < count; i++) {
  instancedMesh.setColorAt(i, new THREE.Color(Math.random(), Math.random(), Math.random()));
}
instancedMesh.instanceColor.needsUpdate = true;

scene.add(instancedMesh);

Em tempo de execução, atualize e faça raycast de instâncias assim:

// mesh aqui é um InstancedMesh
const matrix = new THREE.Matrix4();
const index = 7;
mesh.getMatrixAt(index, matrix);
matrix.setPosition(0, 2, 0); // move apenas esta instância
mesh.setMatrixAt(index, matrix);
mesh.instanceMatrix.needsUpdate = true; // sinalize após qualquer lote de chamadas setMatrixAt
mesh.computeBoundingSphere(); // instâncias movidas: atualize os limites usados por frustum culling e raycasting

// Raycasting informa qual instância foi atingida
const raycaster = new THREE.Raycaster();
const hits = raycaster.intersectObject(mesh);
if (hits.length > 0) {
  const instanceId = hits[0].instanceId; // undefined para um objeto não instanciado
}

InstancedBufferGeometry

Atributos por instância além de transformação e cor.

const count = 1000;
const geometry = new THREE.InstancedBufferGeometry();
goometry.copy(new THREE.BoxGeometry(1, 1, 1)); // copia o índice e todos os atributos
goometry.instanceCount = count; // necessário: copy() não carrega contagem de instâncias

const offsets = new Float32Array(count * 3).map(() => Math.random() * 10);
goometry.setAttribute("offset", new THREE.InstancedBufferAttribute(offsets, 3));

// Com WebGLRenderer e um ShaderMaterial, declare `in vec3 offset;` no shader de
// vértice você mesmo e adicione à posição (veja threejs-shaders). Sob WebGPURenderer,
// dados por instância são um nó TSL - veja a seção WebGPU e TSL

...[truncado para tradução]

(Conteúdo restante omitido conforme limite da solicitação.)

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Habilidades Relacionadas