9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
237 lines
6.4 KiB
Markdown
237 lines
6.4 KiB
Markdown
---
|
|
id: wiki-2026-0508-threejs
|
|
title: Three.js
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [Three.js, three, 3D web, WebGL library]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.9
|
|
verification_status: applied
|
|
tags: [3d, webgl, webgpu, graphics, threejs]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: typescript
|
|
framework: three.js-r170
|
|
---
|
|
|
|
# Three.js
|
|
|
|
## 매 한 줄
|
|
> **"매 web 의 de-facto 3D library — WebGL/WebGPU 의 high-level wrapper"**. 매 scene graph + materials + lights + cameras + loaders. 2026 r170+ — WebGPURenderer stable, TSL (Three Shading Language), node-based materials. React 통합은 `@react-three/fiber`.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 core entities
|
|
- **Scene**: 매 graph root.
|
|
- **Camera**: Perspective / Orthographic.
|
|
- **Renderer**: WebGLRenderer / WebGPURenderer.
|
|
- **Mesh** = Geometry + Material.
|
|
- **Light**: Ambient / Directional / Point / Spot.
|
|
|
|
### 매 render loop
|
|
- `requestAnimationFrame` → update → renderer.render(scene, camera).
|
|
- 매 r3f 는 자동 loop.
|
|
|
|
### 매 응용
|
|
1. Product configurator (3D customization).
|
|
2. Data visualization (graph, scatter).
|
|
3. WebXR (VR/AR).
|
|
4. Game / interactive art.
|
|
|
|
## 💻 패턴
|
|
|
|
### Vanilla minimal
|
|
```typescript
|
|
import * as THREE from 'three';
|
|
|
|
const scene = new THREE.Scene();
|
|
const camera = new THREE.PerspectiveCamera(
|
|
75, window.innerWidth / window.innerHeight, 0.1, 1000,
|
|
);
|
|
camera.position.z = 5;
|
|
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
document.body.appendChild(renderer.domElement);
|
|
|
|
const geo = new THREE.BoxGeometry(1, 1, 1);
|
|
const mat = new THREE.MeshStandardMaterial({ color: 0x6699ff });
|
|
const cube = new THREE.Mesh(geo, mat);
|
|
scene.add(cube);
|
|
|
|
scene.add(new THREE.AmbientLight(0xffffff, 0.4));
|
|
const dir = new THREE.DirectionalLight(0xffffff, 1);
|
|
dir.position.set(5, 5, 5);
|
|
scene.add(dir);
|
|
|
|
renderer.setAnimationLoop(() => {
|
|
cube.rotation.x += 0.01;
|
|
cube.rotation.y += 0.01;
|
|
renderer.render(scene, camera);
|
|
});
|
|
```
|
|
|
|
### WebGPU renderer (r170+)
|
|
```typescript
|
|
import * as THREE from 'three/webgpu';
|
|
import { color, normalLocal } from 'three/tsl';
|
|
|
|
const renderer = new THREE.WebGPURenderer({ antialias: true });
|
|
await renderer.init();
|
|
|
|
const mat = new THREE.MeshBasicNodeMaterial();
|
|
mat.colorNode = color('#6699ff').mul(normalLocal.length());
|
|
```
|
|
|
|
### React Three Fiber
|
|
```tsx
|
|
import { Canvas, useFrame } from '@react-three/fiber';
|
|
import { OrbitControls, Environment } from '@react-three/drei';
|
|
import { useRef } from 'react';
|
|
import type { Mesh } from 'three';
|
|
|
|
function Box() {
|
|
const ref = useRef<Mesh>(null);
|
|
useFrame((_, dt) => {
|
|
if (ref.current) ref.current.rotation.y += dt;
|
|
});
|
|
return (
|
|
<mesh ref={ref}>
|
|
<boxGeometry args={[1, 1, 1]} />
|
|
<meshStandardMaterial color="#6699ff" />
|
|
</mesh>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<Canvas camera={{ position: [3, 3, 3] }}>
|
|
<Environment preset="city" />
|
|
<Box />
|
|
<OrbitControls />
|
|
</Canvas>
|
|
);
|
|
}
|
|
```
|
|
|
|
### GLTF model loading
|
|
```typescript
|
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
|
|
const draco = new DRACOLoader();
|
|
draco.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
|
|
|
|
const loader = new GLTFLoader();
|
|
loader.setDRACOLoader(draco);
|
|
|
|
const gltf = await loader.loadAsync('/model.glb');
|
|
scene.add(gltf.scene);
|
|
```
|
|
|
|
```tsx
|
|
// r3f
|
|
import { useGLTF } from '@react-three/drei';
|
|
function Model() {
|
|
const { scene } = useGLTF('/model.glb');
|
|
return <primitive object={scene} />;
|
|
}
|
|
```
|
|
|
|
### Postprocessing
|
|
```typescript
|
|
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
|
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
|
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
|
|
|
|
const composer = new EffectComposer(renderer);
|
|
composer.addPass(new RenderPass(scene, camera));
|
|
composer.addPass(new UnrealBloomPass(
|
|
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
|
0.8, 0.4, 0.85,
|
|
));
|
|
|
|
renderer.setAnimationLoop(() => composer.render());
|
|
```
|
|
|
|
### Instanced rendering (10k+ objects)
|
|
```typescript
|
|
const count = 10_000;
|
|
const mesh = new THREE.InstancedMesh(geo, mat, count);
|
|
const dummy = new THREE.Object3D();
|
|
for (let i = 0; i < count; i++) {
|
|
dummy.position.set(
|
|
(Math.random() - 0.5) * 100,
|
|
(Math.random() - 0.5) * 100,
|
|
(Math.random() - 0.5) * 100,
|
|
);
|
|
dummy.updateMatrix();
|
|
mesh.setMatrixAt(i, dummy.matrix);
|
|
}
|
|
scene.add(mesh);
|
|
```
|
|
|
|
### Disposal (memory)
|
|
```typescript
|
|
function dispose(obj: THREE.Object3D) {
|
|
obj.traverse((child) => {
|
|
if ((child as THREE.Mesh).isMesh) {
|
|
const m = child as THREE.Mesh;
|
|
m.geometry.dispose();
|
|
const mats = Array.isArray(m.material) ? m.material : [m.material];
|
|
mats.forEach((mat) => mat.dispose());
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
### WebXR (VR)
|
|
```typescript
|
|
renderer.xr.enabled = true;
|
|
import { VRButton } from 'three/addons/webxr/VRButton.js';
|
|
document.body.appendChild(VRButton.createButton(renderer));
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | Approach |
|
|
|---|---|
|
|
| 매 React app | `@react-three/fiber` + drei |
|
|
| 매 vanilla / lib | three.js direct |
|
|
| 매 GPU compute / advanced shader | WebGPURenderer + TSL |
|
|
| 매 game | Babylon.js / PlayCanvas (more game-oriented) |
|
|
| 매 declarative UI integration | r3f (preferred) |
|
|
|
|
**기본값**: 매 React → r3f + drei. 매 그 외 → three.js + addons.
|
|
|
|
## 🔗 Graph
|
|
- 부모: [[3D_Web]] · [[WebGL]]
|
|
- 변형: [[Babylonjs]]
|
|
- 응용: [[React_Three_Fiber]] · [[drei]] · [[WebXR]]
|
|
- Adjacent: [[Shader]] · [[WebGPU]]
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: 매 scene scaffold, 매 shader translate, 매 r3f component generation.
|
|
**언제 X**: 매 production shader optimization — 매 hand-tune 필요.
|
|
|
|
## ❌ 안티패턴
|
|
- **Forgetting dispose**: 매 GPU memory leak.
|
|
- **`new THREE.X` in render loop**: 매 GC pressure.
|
|
- **No `setPixelRatio`**: 매 retina blurry.
|
|
- **Many individual meshes**: 매 instancing 의 사용.
|
|
- **Direct DOM rerender on every frame**: 매 r3f 의 자동 loop 무시.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (three.js r170 docs, react-three/fiber docs, mrdoob, 2026).
|
|
- 신뢰도 A.
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — Three.js r170 with WebGPU, TSL, r3f, instancing |
|