Complete collection of AI agent skills including: - Frontend Development (Vue, React, Next.js, Three.js) - Backend Development (NestJS, FastAPI, Node.js) - Mobile Development (React Native, Expo) - Testing (E2E, frontend, webapp) - DevOps (GitHub Actions, CI/CD) - Marketing (SEO, copywriting, analytics) - Security (binary analysis, vulnerability scanning) - And many more... Synchronized from: https://skills.sh/ Co-Authored-By: Claude <noreply@anthropic.com>
549 lines
14 KiB
Markdown
549 lines
14 KiB
Markdown
---
|
|
name: threejs-geometry
|
|
description: Three.js geometry creation - built-in shapes, BufferGeometry, custom geometry, instancing. Use when creating 3D shapes, working with vertices, building custom meshes, or optimizing with instanced rendering.
|
|
---
|
|
|
|
# Three.js Geometry
|
|
|
|
## Quick Start
|
|
|
|
```javascript
|
|
import * as THREE from "three";
|
|
|
|
// Built-in geometry
|
|
const box = new THREE.BoxGeometry(1, 1, 1);
|
|
const sphere = new THREE.SphereGeometry(0.5, 32, 32);
|
|
const plane = new THREE.PlaneGeometry(10, 10);
|
|
|
|
// Create mesh
|
|
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
|
const mesh = new THREE.Mesh(box, material);
|
|
scene.add(mesh);
|
|
```
|
|
|
|
## Built-in Geometries
|
|
|
|
### Basic Shapes
|
|
|
|
```javascript
|
|
// Box - width, height, depth, widthSegments, heightSegments, depthSegments
|
|
new THREE.BoxGeometry(1, 1, 1, 1, 1, 1);
|
|
|
|
// Sphere - radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength
|
|
new THREE.SphereGeometry(1, 32, 32);
|
|
new THREE.SphereGeometry(1, 32, 32, 0, Math.PI * 2, 0, Math.PI); // Full sphere
|
|
new THREE.SphereGeometry(1, 32, 32, 0, Math.PI); // Hemisphere
|
|
|
|
// Plane - width, height, widthSegments, heightSegments
|
|
new THREE.PlaneGeometry(10, 10, 1, 1);
|
|
|
|
// Circle - radius, segments, thetaStart, thetaLength
|
|
new THREE.CircleGeometry(1, 32);
|
|
new THREE.CircleGeometry(1, 32, 0, Math.PI); // Semicircle
|
|
|
|
// Cylinder - radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded
|
|
new THREE.CylinderGeometry(1, 1, 2, 32, 1, false);
|
|
new THREE.CylinderGeometry(0, 1, 2, 32); // Cone
|
|
new THREE.CylinderGeometry(1, 1, 2, 6); // Hexagonal prism
|
|
|
|
// Cone - radius, height, radialSegments, heightSegments, openEnded
|
|
new THREE.ConeGeometry(1, 2, 32, 1, false);
|
|
|
|
// Torus - radius, tube, radialSegments, tubularSegments, arc
|
|
new THREE.TorusGeometry(1, 0.4, 16, 100);
|
|
|
|
// TorusKnot - radius, tube, tubularSegments, radialSegments, p, q
|
|
new THREE.TorusKnotGeometry(1, 0.4, 100, 16, 2, 3);
|
|
|
|
// Ring - innerRadius, outerRadius, thetaSegments, phiSegments
|
|
new THREE.RingGeometry(0.5, 1, 32, 1);
|
|
```
|
|
|
|
### Advanced Shapes
|
|
|
|
```javascript
|
|
// Capsule - radius, length, capSegments, radialSegments
|
|
new THREE.CapsuleGeometry(0.5, 1, 4, 8);
|
|
|
|
// Dodecahedron - radius, detail
|
|
new THREE.DodecahedronGeometry(1, 0);
|
|
|
|
// Icosahedron - radius, detail (0 = 20 faces, higher = smoother)
|
|
new THREE.IcosahedronGeometry(1, 0);
|
|
|
|
// Octahedron - radius, detail
|
|
new THREE.OctahedronGeometry(1, 0);
|
|
|
|
// Tetrahedron - radius, detail
|
|
new THREE.TetrahedronGeometry(1, 0);
|
|
|
|
// Polyhedron - vertices, indices, radius, detail
|
|
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);
|
|
```
|
|
|
|
### Path-Based Shapes
|
|
|
|
```javascript
|
|
// Lathe - points[], segments, phiStart, phiLength
|
|
const points = [
|
|
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(points, 32);
|
|
|
|
// Extrude - shape, options
|
|
const shape = new THREE.Shape();
|
|
shape.moveTo(0, 0);
|
|
shape.lineTo(1, 0);
|
|
shape.lineTo(1, 1);
|
|
shape.lineTo(0, 1);
|
|
shape.lineTo(0, 0);
|
|
|
|
const extrudeSettings = {
|
|
steps: 2,
|
|
depth: 1,
|
|
bevelEnabled: true,
|
|
bevelThickness: 0.1,
|
|
bevelSize: 0.1,
|
|
bevelSegments: 3,
|
|
};
|
|
new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
|
|
|
// Tube - path, tubularSegments, radius, radialSegments, closed
|
|
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);
|
|
```
|
|
|
|
### Text Geometry
|
|
|
|
```javascript
|
|
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
|
|
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
|
|
|
|
const loader = new FontLoader();
|
|
loader.load("fonts/helvetiker_regular.typeface.json", (font) => {
|
|
const geometry = new TextGeometry("Hello", {
|
|
font: font,
|
|
size: 1,
|
|
depth: 0.2, // Was 'height' in older versions
|
|
curveSegments: 12,
|
|
bevelEnabled: true,
|
|
bevelThickness: 0.03,
|
|
bevelSize: 0.02,
|
|
bevelSegments: 5,
|
|
});
|
|
|
|
// Center text
|
|
geometry.computeBoundingBox();
|
|
geometry.center();
|
|
|
|
const mesh = new THREE.Mesh(geometry, material);
|
|
scene.add(mesh);
|
|
});
|
|
```
|
|
|
|
## BufferGeometry
|
|
|
|
The base class for all geometries. Stores data as typed arrays for GPU efficiency.
|
|
|
|
### Custom BufferGeometry
|
|
|
|
```javascript
|
|
const geometry = new THREE.BufferGeometry();
|
|
|
|
// Vertices (3 floats per vertex: x, y, z)
|
|
const vertices = new Float32Array([
|
|
-1,
|
|
-1,
|
|
0, // vertex 0
|
|
1,
|
|
-1,
|
|
0, // vertex 1
|
|
1,
|
|
1,
|
|
0, // vertex 2
|
|
-1,
|
|
1,
|
|
0, // vertex 3
|
|
]);
|
|
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
|
|
|
|
// Indices (for indexed geometry - reuse vertices)
|
|
const indices = new Uint16Array([
|
|
0,
|
|
1,
|
|
2, // triangle 1
|
|
0,
|
|
2,
|
|
3, // triangle 2
|
|
]);
|
|
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
|
|
|
|
// Normals (required for lighting)
|
|
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
|
|
geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));
|
|
|
|
// UVs (for texturing)
|
|
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
|
|
|
|
// Colors (per-vertex colors)
|
|
const colors = new Float32Array([
|
|
1,
|
|
0,
|
|
0, // red
|
|
0,
|
|
1,
|
|
0, // green
|
|
0,
|
|
0,
|
|
1, // blue
|
|
1,
|
|
1,
|
|
0, // yellow
|
|
]);
|
|
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
|
|
// Use with: material.vertexColors = true
|
|
```
|
|
|
|
### BufferAttribute Types
|
|
|
|
```javascript
|
|
// Common attribute types
|
|
new THREE.BufferAttribute(array, itemSize);
|
|
|
|
// Typed array options
|
|
new Float32Array(count * itemSize); // Positions, normals, UVs
|
|
new Uint16Array(count); // Indices (up to 65535 vertices)
|
|
new Uint32Array(count); // Indices (larger meshes)
|
|
new Uint8Array(count * itemSize); // Colors (0-255 range)
|
|
|
|
// Item sizes
|
|
// Position: 3 (x, y, z)
|
|
// Normal: 3 (x, y, z)
|
|
// UV: 2 (u, v)
|
|
// Color: 3 (r, g, b) or 4 (r, g, b, a)
|
|
// Index: 1
|
|
```
|
|
|
|
### Modifying BufferGeometry
|
|
|
|
```javascript
|
|
const positions = geometry.attributes.position;
|
|
|
|
// Modify vertex
|
|
positions.setXYZ(index, x, y, z);
|
|
|
|
// Access vertex
|
|
const x = positions.getX(index);
|
|
const y = positions.getY(index);
|
|
const z = positions.getZ(index);
|
|
|
|
// Flag for GPU update
|
|
positions.needsUpdate = true;
|
|
|
|
// Recompute normals after position changes
|
|
geometry.computeVertexNormals();
|
|
|
|
// Recompute bounding box/sphere after changes
|
|
geometry.computeBoundingBox();
|
|
geometry.computeBoundingSphere();
|
|
```
|
|
|
|
### Interleaved Buffers (Advanced)
|
|
|
|
```javascript
|
|
// More efficient memory layout for large meshes
|
|
const interleavedBuffer = new THREE.InterleavedBuffer(
|
|
new Float32Array([
|
|
// pos.x, pos.y, pos.z, uv.u, uv.v (repeated per vertex)
|
|
-1, -1, 0, 0, 0, 1, -1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 0, 0, 1,
|
|
]),
|
|
5, // stride (floats per vertex)
|
|
);
|
|
|
|
geometry.setAttribute(
|
|
"position",
|
|
new THREE.InterleavedBufferAttribute(interleavedBuffer, 3, 0),
|
|
); // size 3, offset 0
|
|
geometry.setAttribute(
|
|
"uv",
|
|
new THREE.InterleavedBufferAttribute(interleavedBuffer, 2, 3),
|
|
); // size 2, offset 3
|
|
```
|
|
|
|
## EdgesGeometry & WireframeGeometry
|
|
|
|
```javascript
|
|
// Edge lines (only hard edges)
|
|
const edges = new THREE.EdgesGeometry(boxGeometry, 15); // 15 = threshold angle
|
|
const edgeMesh = new THREE.LineSegments(
|
|
edges,
|
|
new THREE.LineBasicMaterial({ color: 0xffffff }),
|
|
);
|
|
|
|
// Wireframe (all triangles)
|
|
const wireframe = new THREE.WireframeGeometry(boxGeometry);
|
|
const wireMesh = new THREE.LineSegments(
|
|
wireframe,
|
|
new THREE.LineBasicMaterial({ color: 0xffffff }),
|
|
);
|
|
```
|
|
|
|
## Points
|
|
|
|
```javascript
|
|
// Create point cloud
|
|
const geometry = new THREE.BufferGeometry();
|
|
const positions = new Float32Array(1000 * 3);
|
|
|
|
for (let i = 0; i < 1000; i++) {
|
|
positions[i * 3] = (Math.random() - 0.5) * 10;
|
|
positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
|
|
positions[i * 3 + 2] = (Math.random() - 0.5) * 10;
|
|
}
|
|
|
|
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
|
|
|
const material = new THREE.PointsMaterial({
|
|
size: 0.1,
|
|
sizeAttenuation: true, // Size decreases with distance
|
|
color: 0xffffff,
|
|
});
|
|
|
|
const points = new THREE.Points(geometry, material);
|
|
scene.add(points);
|
|
```
|
|
|
|
## Lines
|
|
|
|
```javascript
|
|
// Line (connected points)
|
|
const points = [
|
|
new THREE.Vector3(-1, 0, 0),
|
|
new THREE.Vector3(0, 1, 0),
|
|
new THREE.Vector3(1, 0, 0),
|
|
];
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const line = new THREE.Line(
|
|
geometry,
|
|
new THREE.LineBasicMaterial({ color: 0xff0000 }),
|
|
);
|
|
|
|
// LineLoop (closed loop)
|
|
const loop = new THREE.LineLoop(geometry, material);
|
|
|
|
// LineSegments (pairs of points)
|
|
const segmentsGeometry = new THREE.BufferGeometry();
|
|
segmentsGeometry.setAttribute(
|
|
"position",
|
|
new THREE.BufferAttribute(
|
|
new Float32Array([
|
|
-1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0, // segment 1
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
0,
|
|
0, // segment 2
|
|
]),
|
|
3,
|
|
),
|
|
);
|
|
const segments = new THREE.LineSegments(segmentsGeometry, material);
|
|
```
|
|
|
|
## InstancedMesh
|
|
|
|
Efficiently render many copies of the same geometry.
|
|
|
|
```javascript
|
|
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
|
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
|
const count = 1000;
|
|
|
|
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
|
|
|
|
// Set transforms for each instance
|
|
const dummy = new THREE.Object3D();
|
|
const matrix = new THREE.Matrix4();
|
|
|
|
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);
|
|
}
|
|
|
|
// Flag for GPU update
|
|
instancedMesh.instanceMatrix.needsUpdate = true;
|
|
|
|
// Optional: per-instance colors
|
|
instancedMesh.instanceColor = new THREE.InstancedBufferAttribute(
|
|
new Float32Array(count * 3),
|
|
3,
|
|
);
|
|
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);
|
|
```
|
|
|
|
### Update Instance at Runtime
|
|
|
|
```javascript
|
|
// Update single instance
|
|
const matrix = new THREE.Matrix4();
|
|
instancedMesh.getMatrixAt(index, matrix);
|
|
// Modify matrix...
|
|
instancedMesh.setMatrixAt(index, matrix);
|
|
instancedMesh.instanceMatrix.needsUpdate = true;
|
|
|
|
// Raycasting with instanced mesh
|
|
const intersects = raycaster.intersectObject(instancedMesh);
|
|
if (intersects.length > 0) {
|
|
const instanceId = intersects[0].instanceId;
|
|
}
|
|
```
|
|
|
|
## InstancedBufferGeometry (Advanced)
|
|
|
|
For custom per-instance attributes beyond transform/color.
|
|
|
|
```javascript
|
|
const geometry = new THREE.InstancedBufferGeometry();
|
|
geometry.copy(new THREE.BoxGeometry(1, 1, 1));
|
|
|
|
// Add per-instance attribute
|
|
const offsets = new Float32Array(count * 3);
|
|
for (let i = 0; i < count; i++) {
|
|
offsets[i * 3] = Math.random() * 10;
|
|
offsets[i * 3 + 1] = Math.random() * 10;
|
|
offsets[i * 3 + 2] = Math.random() * 10;
|
|
}
|
|
geometry.setAttribute("offset", new THREE.InstancedBufferAttribute(offsets, 3));
|
|
|
|
// Use in shader
|
|
// attribute vec3 offset;
|
|
// vec3 transformed = position + offset;
|
|
```
|
|
|
|
## Geometry Utilities
|
|
|
|
```javascript
|
|
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
|
|
// Merge geometries (must have same attributes)
|
|
const merged = BufferGeometryUtils.mergeGeometries([geo1, geo2, geo3]);
|
|
|
|
// Merge with groups (for multi-material)
|
|
const merged = BufferGeometryUtils.mergeGeometries([geo1, geo2], true);
|
|
|
|
// Compute tangents (required for normal maps)
|
|
BufferGeometryUtils.computeTangents(geometry);
|
|
|
|
// Interleave attributes for better performance
|
|
const interleaved = BufferGeometryUtils.interleaveAttributes([
|
|
geometry.attributes.position,
|
|
geometry.attributes.normal,
|
|
geometry.attributes.uv,
|
|
]);
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Center Geometry
|
|
|
|
```javascript
|
|
geometry.computeBoundingBox();
|
|
geometry.center(); // Move vertices so center is at origin
|
|
```
|
|
|
|
### Scale to Fit
|
|
|
|
```javascript
|
|
geometry.computeBoundingBox();
|
|
const size = new THREE.Vector3();
|
|
geometry.boundingBox.getSize(size);
|
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
geometry.scale(1 / maxDim, 1 / maxDim, 1 / maxDim);
|
|
```
|
|
|
|
### Clone and Transform
|
|
|
|
```javascript
|
|
const clone = geometry.clone();
|
|
clone.rotateX(Math.PI / 2);
|
|
clone.translate(0, 1, 0);
|
|
clone.scale(2, 2, 2);
|
|
```
|
|
|
|
### Morph Targets
|
|
|
|
```javascript
|
|
// Base geometry
|
|
const geometry = new THREE.BoxGeometry(1, 1, 1, 4, 4, 4);
|
|
|
|
// Create morph target
|
|
const morphPositions = geometry.attributes.position.array.slice();
|
|
for (let i = 0; i < morphPositions.length; i += 3) {
|
|
morphPositions[i] *= 2; // Scale X
|
|
morphPositions[i + 1] *= 0.5; // Squash Y
|
|
}
|
|
|
|
geometry.morphAttributes.position = [
|
|
new THREE.BufferAttribute(new Float32Array(morphPositions), 3),
|
|
];
|
|
|
|
const mesh = new THREE.Mesh(geometry, material);
|
|
mesh.morphTargetInfluences[0] = 0.5; // 50% blend
|
|
```
|
|
|
|
## Performance Tips
|
|
|
|
1. **Use indexed geometry**: Reuse vertices with indices
|
|
2. **Merge static meshes**: Reduce draw calls with `mergeGeometries`
|
|
3. **Use InstancedMesh**: For many identical objects
|
|
4. **Choose appropriate segment counts**: More segments = smoother but slower
|
|
5. **Dispose unused geometry**: `geometry.dispose()`
|
|
|
|
```javascript
|
|
// Good segment counts for common uses
|
|
new THREE.SphereGeometry(1, 32, 32); // Good quality
|
|
new THREE.SphereGeometry(1, 64, 64); // High quality
|
|
new THREE.SphereGeometry(1, 16, 16); // Performance mode
|
|
|
|
// Dispose when done
|
|
geometry.dispose();
|
|
```
|
|
|
|
## See Also
|
|
|
|
- `threejs-fundamentals` - Scene setup and Object3D
|
|
- `threejs-materials` - Material types for meshes
|
|
- `threejs-shaders` - Custom vertex manipulation
|