init
This commit is contained in:
269
.opencode/skills/threejs/references/12-performance.md
Normal file
269
.opencode/skills/threejs/references/12-performance.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Performance Optimization
|
||||
|
||||
Techniques for fast, smooth 3D experiences.
|
||||
|
||||
## Instancing
|
||||
|
||||
Render many copies of same geometry efficiently:
|
||||
|
||||
```javascript
|
||||
// Instead of creating 10,000 individual meshes
|
||||
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
||||
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
||||
const mesh = new THREE.InstancedMesh(geometry, material, 10000);
|
||||
|
||||
// Set transforms for each instance
|
||||
const matrix = new THREE.Matrix4();
|
||||
const position = new THREE.Vector3();
|
||||
const rotation = new THREE.Euler();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
const scale = new THREE.Vector3(1, 1, 1);
|
||||
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
position.set(
|
||||
Math.random() * 100 - 50,
|
||||
Math.random() * 100 - 50,
|
||||
Math.random() * 100 - 50
|
||||
);
|
||||
|
||||
rotation.set(
|
||||
Math.random() * Math.PI,
|
||||
Math.random() * Math.PI,
|
||||
Math.random() * Math.PI
|
||||
);
|
||||
|
||||
quaternion.setFromEuler(rotation);
|
||||
matrix.compose(position, quaternion, scale);
|
||||
mesh.setMatrixAt(i, matrix);
|
||||
}
|
||||
|
||||
mesh.instanceMatrix.needsUpdate = true;
|
||||
scene.add(mesh);
|
||||
|
||||
// Per-instance colors
|
||||
mesh.instanceColor = new THREE.InstancedBufferAttribute(
|
||||
new Float32Array(10000 * 3),
|
||||
3
|
||||
);
|
||||
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
mesh.setColorAt(i, new THREE.Color(Math.random(), Math.random(), Math.random()));
|
||||
}
|
||||
```
|
||||
|
||||
## Level of Detail (LOD)
|
||||
|
||||
Switch between detail levels based on distance:
|
||||
|
||||
```javascript
|
||||
const lod = new THREE.LOD();
|
||||
|
||||
// High detail (close)
|
||||
const geometryHigh = new THREE.IcosahedronGeometry(10, 4);
|
||||
const meshHigh = new THREE.Mesh(geometryHigh, material);
|
||||
lod.addLevel(meshHigh, 0);
|
||||
|
||||
// Medium detail
|
||||
const geometryMed = new THREE.IcosahedronGeometry(10, 2);
|
||||
const meshMed = new THREE.Mesh(geometryMed, material);
|
||||
lod.addLevel(meshMed, 50);
|
||||
|
||||
// Low detail (far)
|
||||
const geometryLow = new THREE.IcosahedronGeometry(10, 0);
|
||||
const meshLow = new THREE.Mesh(geometryLow, material);
|
||||
lod.addLevel(meshLow, 100);
|
||||
|
||||
scene.add(lod);
|
||||
|
||||
// Update LOD in animation loop
|
||||
function animate() {
|
||||
lod.update(camera);
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
```
|
||||
|
||||
## Frustum Culling
|
||||
|
||||
Automatic - objects outside camera view aren't rendered.
|
||||
|
||||
```javascript
|
||||
// Force disable for specific object
|
||||
object.frustumCulled = false;
|
||||
|
||||
// Manually test if in view
|
||||
const frustum = new THREE.Frustum();
|
||||
const cameraViewProjectionMatrix = new THREE.Matrix4();
|
||||
cameraViewProjectionMatrix.multiplyMatrices(
|
||||
camera.projectionMatrix,
|
||||
camera.matrixWorldInverse
|
||||
);
|
||||
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);
|
||||
|
||||
if (frustum.intersectsObject(object)) {
|
||||
// Object is visible
|
||||
}
|
||||
```
|
||||
|
||||
## Geometry Optimization
|
||||
|
||||
```javascript
|
||||
// Merge geometries (reduce draw calls)
|
||||
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
|
||||
|
||||
const geometries = [geom1, geom2, geom3];
|
||||
const mergedGeometry = mergeGeometries(geometries);
|
||||
const mesh = new THREE.Mesh(mergedGeometry, material);
|
||||
|
||||
// Dispose old geometries
|
||||
geometries.forEach(g => g.dispose());
|
||||
|
||||
// Simplify geometry
|
||||
import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js';
|
||||
|
||||
const modifier = new SimplifyModifier();
|
||||
const simplified = modifier.modify(geometry, Math.floor(geometry.attributes.position.count * 0.5));
|
||||
```
|
||||
|
||||
## Texture Optimization
|
||||
|
||||
```javascript
|
||||
// Use appropriate sizes (power of 2)
|
||||
// 512x512, 1024x1024, 2048x2048
|
||||
|
||||
// Compress textures
|
||||
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
|
||||
|
||||
// Use mipmaps (auto-generated by default)
|
||||
texture.generateMipmaps = true;
|
||||
|
||||
// Appropriate filtering
|
||||
texture.minFilter = THREE.LinearMipmapLinearFilter;
|
||||
texture.magFilter = THREE.LinearFilter;
|
||||
|
||||
// Anisotropic filtering (balance quality/performance)
|
||||
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
||||
|
||||
// Dispose unused textures
|
||||
texture.dispose();
|
||||
```
|
||||
|
||||
## Material Sharing
|
||||
|
||||
```javascript
|
||||
// Share materials between meshes (reduce memory)
|
||||
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
|
||||
|
||||
const mesh1 = new THREE.Mesh(geometry1, sharedMaterial);
|
||||
const mesh2 = new THREE.Mesh(geometry2, sharedMaterial);
|
||||
const mesh3 = new THREE.Mesh(geometry3, sharedMaterial);
|
||||
```
|
||||
|
||||
## Shadow Optimization
|
||||
|
||||
```javascript
|
||||
// Reduce shadow map resolution
|
||||
light.shadow.mapSize.width = 1024; // instead of 2048
|
||||
light.shadow.mapSize.height = 1024;
|
||||
|
||||
// Limit shadow camera frustum
|
||||
light.shadow.camera.near = 0.5;
|
||||
light.shadow.camera.far = 50; // only cast shadows within this range
|
||||
light.shadow.camera.left = -10;
|
||||
light.shadow.camera.right = 10;
|
||||
|
||||
// Use fewer shadow-casting objects
|
||||
object.castShadow = false; // for distant/small objects
|
||||
object.receiveShadow = false; // for objects that don't need shadows
|
||||
|
||||
// Cheaper shadow type
|
||||
renderer.shadowMap.type = THREE.PCFShadowMap; // instead of PCFSoftShadowMap
|
||||
```
|
||||
|
||||
## Render Target Optimization
|
||||
|
||||
```javascript
|
||||
// Lower resolution for post-processing
|
||||
const renderTarget = new THREE.WebGLRenderTarget(
|
||||
window.innerWidth * 0.5, // half resolution
|
||||
window.innerHeight * 0.5
|
||||
);
|
||||
|
||||
// Appropriate pixel format
|
||||
renderTarget.texture.format = THREE.RGBAFormat;
|
||||
renderTarget.texture.type = THREE.UnsignedByteType;
|
||||
|
||||
// Dispose when done
|
||||
renderTarget.dispose();
|
||||
```
|
||||
|
||||
## Object Pooling
|
||||
|
||||
```javascript
|
||||
// Reuse objects instead of creating/destroying
|
||||
class ObjectPool {
|
||||
constructor(factory, initialSize) {
|
||||
this.factory = factory;
|
||||
this.pool = [];
|
||||
for (let i = 0; i < initialSize; i++) {
|
||||
this.pool.push(factory());
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.pool.length > 0 ? this.pool.pop() : this.factory();
|
||||
}
|
||||
|
||||
release(obj) {
|
||||
this.pool.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
const bulletPool = new ObjectPool(() => {
|
||||
return new THREE.Mesh(bulletGeometry, bulletMaterial);
|
||||
}, 100);
|
||||
|
||||
// Use
|
||||
const bullet = bulletPool.get();
|
||||
scene.add(bullet);
|
||||
|
||||
// Return when done
|
||||
scene.remove(bullet);
|
||||
bulletPool.release(bullet);
|
||||
```
|
||||
|
||||
## Monitoring Performance
|
||||
|
||||
```javascript
|
||||
// FPS counter
|
||||
const stats = new Stats();
|
||||
document.body.appendChild(stats.dom);
|
||||
|
||||
function animate() {
|
||||
stats.begin();
|
||||
// ... rendering
|
||||
stats.end();
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Renderer info
|
||||
console.log(renderer.info);
|
||||
// Shows: geometries, textures, programs, calls, triangles, points, lines
|
||||
|
||||
// GPU timing
|
||||
const query = renderer.extensions.get('EXT_disjoint_timer_query_webgl2');
|
||||
```
|
||||
|
||||
## General Best Practices
|
||||
|
||||
- Limit draw calls (merge geometries, use instancing)
|
||||
- Reduce polygon count (LOD, simplification)
|
||||
- Optimize textures (compression, appropriate sizes)
|
||||
- Share materials and geometries
|
||||
- Use frustum culling
|
||||
- Limit number of lights (3-5 max)
|
||||
- Avoid transparent materials when possible
|
||||
- Use object pooling for frequently created/destroyed objects
|
||||
- Profile with browser DevTools
|
||||
- Test on target devices
|
||||
- Use WebGL 2 features when available
|
||||
Reference in New Issue
Block a user