ν°μ€ν 리 λ·°
π Three.jsμμ μ±λ₯ μ΅μ ν & λ‘λ© μλ κ°μ νκΈ°
μ§κΈκΉμ§ μ°λ¦¬λ 3D λͺ¨λΈμ λΆλ¬μ€κ³ , μ λλ©μ΄μ
κ³Ό μ‘°λͺ
μ μ μ©νμ¬ νμ€μ μΈ 3D μ¬μ ꡬμΆν΄λ³΄μμ΅λλ€.
νμ§λ§ μ€μ μλΉμ€μ μ μ©νλ €λ©΄ μ±λ₯ μ΅μ νκ° λ°λμ νμν©λλ€.
μ΄λ² κΈμμλ Three.js νλ‘μ νΈμ μ±λ₯μ ν₯μμν€κ³ λ‘λ© μλλ₯Ό κ°μ νλ ν΅μ¬ μ λ΅λ€μ μκ°ν©λλ€.
π 1. μ μ±λ₯ μ΅μ νκ° μ€μνκ°?
3Dλ λΈλΌμ°μ μμ λ§μ GPU 리μμ€λ₯Ό μ¬μ©νκΈ° λλ¬Έμ,
μ΅μ νλ₯Ό νμ§ μμΌλ©΄ λ€μκ³Ό κ°μ λ¬Έμ κ° λ°μν©λλ€:
λ¬Έμ μμΈ
β λλ¦° μ΄κΈ° λ‘λ© | GLB/GLTF νμΌμ΄ λ무 νΌ |
β νλ μ λλ | λ€κ°ν μ(poly count)κ° λ무 λ§μ |
β μ‘°μ μ§μ° | κ·Έλ¦Όμ, μ‘°λͺ , μ λλ©μ΄μ κ³ΌλΆν |
β λͺ¨λ°μΌ νκ²½μμ μλ λΆκ° | νλμ¨μ΄ μ¬μ λ―Έμ§μ |
π§ 2. μ±λ₯ μ΅μ νλ₯Ό μν ν΅μ¬ μ λ΅ 7κ°μ§
β 1) GLB λͺ¨λΈ μμΆ (Draco Compression)
- GLB/GLTF λͺ¨λΈμ μ©λμ μ΅λ 90%κΉμ§ μ€μΌ μ μλ μμΆ λ°©μ
- @react-three/dreiμμ useGLTFλ μλμΌλ‘ Dracoλ₯Ό μ§μν©λλ€.
π μμΆ λͺ¨λΈ μ¬μ© μμ
const { scene } = useGLTF('/models/robot_draco.glb', true); // draco: true
π Draco μμΆμ Blenderμμ κ°λ₯νλ©°, glTF-Pipeline CLIλ‘λ μ μ©ν μ μμ΅λλ€.
β 2) Lazy Loading (Suspense + dynamic import)
- μ΄κΈ° νλ©΄μ νμνμ§ μμ 리μμ€λ λμ€μ λ‘λνλ λ°©μ
- React.lazyμ Suspenseλ₯Ό μ¬μ©
π μμ μ½λ
import { Suspense, lazy } from 'react';
const HeavyModel = lazy(() => import('./HeavyModel'));
function Scene() {
return (
<Suspense fallback={<span>Loading...</span>}>
<HeavyModel />
</Suspense>
);
}
β 3) μ‘°λͺ & κ·Έλ¦Όμ μ΅μ ν
- κ·Έλ¦Όμλ₯Ό μ¬μ©ν κ²½μ° μ±λ₯μ΄ κΈκ²©ν λ¨μ΄μ§ μ μμ
- νμν κ²½μ°μλ§ castShadow, receiveShadow μ μ©
- mapSize μ€μ΄κ³ distance μ ννκΈ°
π κ·Έλ¦Όμ mapSize μ€μ΄κΈ°
<directionalLight
castShadow
shadow-mapSize-width={1024}
shadow-mapSize-height={1024}
/>
β 4) λͺ¨λΈ λ¨μν (Low-Poly μ¬μ©)
- High-poly λͺ¨λΈμ μμ² κ°μ μΌκ°ν → GPU λΆν
- Blender, Sketchfab λ±μμ Low-Poly λͺ¨λΈ μ¬μ© κΆμ₯
β 5) Postprocessing μ΅μν λλ μ‘°κ±΄λΆ μ μ©
- Bloom, SSR, Outline λ±μ λ§€μ° λ¦¬μμ€λ₯Ό λ§μ΄ μ¬μ©
- UIμμ On/Off ν κΈμ λμ΄ μ‘°κ±΄λΆ μ¬μ©
β 6) Renderer μ΅μ μ‘°μ
- physicallyCorrectLights, toneMapping, antialias λ±μ κΊΌλκ±°λ μ‘°μ κ°λ₯
π Canvas μ€μ μ΅μ ν μμ
<Canvas
shadows
dpr={window.devicePixelRatio}
gl={{ antialias: false, physicallyCorrectLights: false }}
camera={{ position: [0, 3, 5], fov: 50 }}
>
β 7) FPS & μ±λ₯ λͺ¨λν°λ§ λꡬ νμ©
- @react-three/dreiμ <Stats /> μ»΄ν¬λνΈλ₯Ό νμ©νμ¬ FPS λ° λ©λͺ¨λ¦¬ μ¬μ©λ νμΈ
π μμ
import { Stats } from '@react-three/drei';
function Scene() {
return (
<>
<Canvas>{/* ... */}</Canvas>
<Stats />
</>
);
}
π 3. μ€μ μ μ© μμ: μ΅μ ν μ μ©ν Viewer
import { Canvas } from '@react-three/fiber';
import { OrbitControls, useGLTF, Stats } from '@react-three/drei';
import { Suspense } from 'react';
function OptimizedModel() {
const { scene } = useGLTF('/models/robot_draco.glb', true);
return <primitive object={scene} scale={1.5} />;
}
export default function OptimizedViewer() {
return (
<>
<Canvas
shadows
dpr={1.5}
gl={{ antialias: false }}
camera={{ position: [0, 2, 5], fov: 50 }}
>
<ambientLight intensity={0.3} />
<directionalLight castShadow position={[5, 5, 5]} intensity={1} />
<Suspense fallback={<span>λͺ¨λΈ λ‘λ© μ€...</span>}>
<OptimizedModel />
</Suspense>
<OrbitControls />
</Canvas>
<Stats />
</>
);
}
β 4. μ΅μ ν ν κΈ°λ ν¨κ³Ό
νλͺ© μ΅μ ν μ μ΅μ ν ν
λͺ¨λΈ λ‘λ© μλ | 5μ΄ μ΄μ | 1~2μ΄ |
FPS | 15~30 | 50~60 |
λͺ¨λ°μΌ λμ | μ νμ | μνν¨ |
μ΄κΈ° νλ©΄ λ λλ§ | λ²λ² μ | λΆλλ¬μ |
π― 5. λ€μ κΈ μκ³ : μμ±λ μ¬μ΄νΈ λ°°ν¬ & νμ©
μ΄μ μ°λ¦¬κ° λ§λ 3D λ·°μ΄ μ¬μ΄νΈλ μ΅μ νκΉμ§ λ§μΉ μνμ
λλ€.
λ€μ κΈμμλ μ΄ μ¬μ΄νΈλ₯Ό Netlify / Vercelμ λ°°ν¬νκ³ μ€μ λ‘ νμ©νλ λ°©λ²μ μκ°ν©λλ€.
λν, μ€μ ν¬νΈν΄λ¦¬μ€/μΌνλͺ°/κ²μ μμ΄ν
λ·°μ΄λ‘ νμ₯νλ μμ΄λμ΄λ ν¨κ» λ€λ£Ήλλ€.
β
λ€μ νΈ μκ³ :
"Three.js 3D λ·°μ΄ μ¬μ΄νΈλ₯Ό μ€μ λ‘ λ°°ν¬νκ³ νμ©νλ λ°©λ²" π
'project' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
- Total
- Today
- Yesterday
- LangChain
- κ°λ°λΈλ‘κ·Έ
- λ°±μλκ°λ°
- fastapi
- λ°±μλ
- Next.js
- rag
- nodejs
- Prisma
- AI μλν
- μΉκ°λ°
- NestJS
- CI/CD
- μ€λ§νΈ 컨νΈλνΈ
- kotlin
- κ΄λ¦¬μ
- AIμ±λ΄
- SEOμ΅μ ν
- Docker
- seo μ΅μ ν 10κ°
- github
- Webpack
- gatsbyjs
- Ktor
- App Router
- nextJS
- llm
- PostgreSQL
- νλ‘ νΈμλ
- REACT
μΌ | μ | ν | μ | λͺ© | κΈ | ν |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |