7.5 KiB
7.5 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-20k-skinned-instances-demo | 20k Skinned Instances Demo (Three.js) | 10_Wiki/Topics | verified | self |
|
none | B | 0.85 | applied |
|
2026-05-09 | pending |
|
20k Skinned Instances Demo
📌 한 줄 통찰
20,000 character 의 same scene + 5 draw call only. agargaro 의 InstancedMesh2 + frustum culling + LOD + animation throttle. Mobile 도 3000+ instance 60fps.
📖 핵심
매 demo 의 capability
- Desktop: 20,000 skinned instance @ 60 FPS.
- Mobile: 3,000 instance @ 60 FPS.
- Draw calls: 5 only (despite 20k unit).
- GPU: integrated 도 OK.
→ 매 traditional 의 100-1000 instance 의 limit 의 break.
매 optimization technique
1. Frustum culling
- 매 camera 의 outside instance 의 skip.
- 매 bone update 의 only visible.
2. Distance-based animation
- Near: 60 FPS bone update.
- Mid: 30 FPS.
- Far: 10 FPS.
- Very far: 0 (static pose).
→ 매 update cost 의 80% saving.
3. Multi-LOD
- LOD 0: 매 detailed mesh + bone.
- LOD 1: 매 simplified mesh.
- LOD 2: 매 imposter (billboard).
→ 매 distance 의 different polygon count.
4. GPU skinning
- 매 bone matrix 의 texture 저장.
- 매 vertex shader 의 calculate.
- CPU 의 free.
5. Single material / atlas
- 매 instance 의 same material.
- 매 atlas texture (1 texture, multiple variation).
6. Indirect rendering
- GPU 의 매 instance 의 visibility 결정.
- CPU → GPU transfer 최소화.
매 architecture
Scene
└── InstancedMesh2 (1)
├── Geometry: skinned mesh (1)
├── Material: shared (1)
├── BoneTexture: 매 instance 의 bone matrix (RGBA float)
└── Per-instance:
├── Position
├── Rotation
├── Scale
├── Color (optional)
└── Animation state (frame, speed)
→ 1 InstancedMesh2 = 1 draw call.
매 use case
Game (RTS / open world)
- 매 1000+ unit (StarCraft 식).
- 매 crowd (city, stadium).
Visualization
- 매 large dataset (data point 의 character).
- 매 scientific (molecule, particle).
Simulation
- 매 swarm.
- 매 evacuation.
- 매 pedestrian.
Web 3D
- 매 metaverse-style.
- 매 large social space.
💻 Code
Setup
import { InstancedMesh2 } from '@three.ez/instanced-mesh';
import * as THREE from 'three';
// Load skinned mesh
const loader = new GLTFLoader();
const gltf = await loader.loadAsync('character.glb');
const skinnedMesh = gltf.scene.children[0] as THREE.SkinnedMesh;
// Create InstancedMesh2
const instancedMesh = new InstancedMesh2(
skinnedMesh.geometry,
skinnedMesh.material,
{
capacity: 20000,
createEntities: true,
skinned: true, // GPU skinning
}
);
// Add instances
for (let i = 0; i < 20000; i++) {
instancedMesh.addInstances(1, (instance) => {
instance.position.set(
(Math.random() - 0.5) * 1000,
0,
(Math.random() - 0.5) * 1000
);
instance.rotation.y = Math.random() * Math.PI * 2;
});
}
scene.add(instancedMesh);
Frustum culling + distance animation
function animate() {
const cameraPos = camera.position;
instancedMesh.instances.forEach((instance, i) => {
const dist = instance.position.distanceTo(cameraPos);
// Distance-based update rate
if (dist < 50) {
instance.updateAnimation(deltaTime); // 60 FPS
} else if (dist < 200) {
if (frame % 2 === 0) instance.updateAnimation(deltaTime * 2); // 30 FPS
} else if (dist < 500) {
if (frame % 6 === 0) instance.updateAnimation(deltaTime * 6); // 10 FPS
}
// > 500: no animation update (static pose)
});
// Auto frustum culling
instancedMesh.performFrustumCulling(camera);
renderer.render(scene, camera);
}
Multi-LOD
const lod0 = new InstancedMesh2(highPolyGeo, mat, { capacity: 5000, skinned: true });
const lod1 = new InstancedMesh2(midPolyGeo, mat, { capacity: 10000, skinned: true });
const lod2 = new InstancedMesh2(impostorGeo, mat, { capacity: 5000 });
function updateLOD(instances) {
instances.forEach((inst, i) => {
const d = inst.distanceToCamera(camera);
if (d < 50) inst.assignTo(lod0);
else if (d < 200) inst.assignTo(lod1);
else inst.assignTo(lod2);
});
}
GPU skinning (custom shader)
// Vertex shader
attribute vec4 skinIndices;
attribute vec4 skinWeights;
uniform sampler2D boneTexture; // 매 instance 의 bone matrix
uniform float boneTextureSize;
mat4 getBoneMatrix(float index, float instanceIndex) {
float u = (index * 4.0 + 0.5) / boneTextureSize;
float v = (instanceIndex + 0.5) / boneTextureSize;
return mat4(
texture2D(boneTexture, vec2(u, v)),
texture2D(boneTexture, vec2(u + 1.0/boneTextureSize, v)),
texture2D(boneTexture, vec2(u + 2.0/boneTextureSize, v)),
texture2D(boneTexture, vec2(u + 3.0/boneTextureSize, v))
);
}
void main() {
mat4 boneMat =
getBoneMatrix(skinIndices.x, gl_InstanceID) * skinWeights.x +
getBoneMatrix(skinIndices.y, gl_InstanceID) * skinWeights.y +
getBoneMatrix(skinIndices.z, gl_InstanceID) * skinWeights.z +
getBoneMatrix(skinIndices.w, gl_InstanceID) * skinWeights.w;
vec4 transformed = boneMat * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * transformed;
}
→ 매 vertex 의 GPU 가 calculate.
Performance metric
const stats = new Stats();
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
// ... render
stats.end();
requestAnimationFrame(animate);
}
console.log({
draws: renderer.info.render.calls,
triangles: renderer.info.render.triangles,
geometries: renderer.info.memory.geometries,
});
// Goal: draws < 10, FPS = 60
🤔 결정 기준
| Instance count | Approach |
|---|---|
| < 100 | Native skinned mesh (each its own) |
| 100-1000 | InstancedMesh2 + frustum cull |
| 1000-10000 | + LOD + distance animation |
| 10000+ | + GPU skinning + impostor |
| Mobile | 3000 max + heavy LOD |
기본값: InstancedMesh2 + 5 optimization (frustum, LOD, animation throttle, GPU skin, atlas).
🔗 Graph
- 부모: Three-js-Performance · Skinned-Mesh · Instancing
- 변형: InstancedMesh2-agargaro · BatchedMesh
- 응용: Crowd-Simulation · Open-World-Rendering · Metaverse
- 기술: GPU-Skinning · Frustum-Culling · Level-of-Detail · Bone-Texture
🤖 LLM 활용
언제: 매 large character scene 의 design. 매 mobile / web 3D 의 performance. 언제 X: 매 small scene. 매 specific Unity / Unreal (different).
❌ 안티패턴
- Native skinned + 1000 instance: 30 FPS.
- No LOD + variable distance: GPU waste.
- Bone update 60 FPS 매 instance: CPU 의 bottleneck.
- No frustum cull: hidden update.
- Multiple material per instance: 매 draw call 의 multiply.
🧪 검증 / 중복
- Applied (agargaro 의 demo).
- 신뢰도 B (GitHub repo, real demo).
- Related: agargaro-libraries · Three-js-Performance.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-09 | Manual cleanup — 5 optimization + Three.js code + GPU skinning shader |