# 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