1단계: 기초 유틸리티 익히기 (씬 구성 편의성)
🎯 목표
기본적인 씬을 구성할 수 있는 drei 유틸리티를 익혀서
OrbitControls, 카메라 설정, 배경, 로딩, 카메라 프레이밍 등을 손쉽게 구현하는 것이 목표입니다.
① OrbitControls – 마우스로 카메라 회전, 줌, 이동 제어
개념:
OrbitControls는 사용자의 마우스 입력에 따라 카메라를 움직이도록 만들어줍니다.
실습 목표:
- 캔버스를 마우스로 드래그하면 회전
- 휠로 확대/축소 가능
import { OrbitControls } from '@react-three/drei';
<Canvas>
{/* ... */}
<OrbitControls />
</Canvas>
② PerspectiveCamera, OrthographicCamera – 카메라 설정
개념:
- PerspectiveCamera: 일반 3D 카메라 (멀리 있는 건 작게 보임)
- OrthographicCamera: 평면 UI나 건축도면처럼 원근이 없음
실습 목표:
- 두 카메라를 비교해서 각각 어떤 느낌인지 체험해보기
import { PerspectiveCamera, OrthographicCamera } from '@react-three/drei';
<Canvas>
<PerspectiveCamera makeDefault position={[0, 0, 5]} fov={75} />
{/* 또는 */}
{/* <OrthographicCamera makeDefault position={[0, 0, 5]} zoom={100} /> */}
<OrbitControls />
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</Canvas>
③ Environment – HDRI 기반 조명 환경 설정
개념:
Environment는 현실감 있는 조명과 반사를 위해 HDRI를 사용한 환경을 설정합니다.
실습 목표:
- 씬에 현실적인 광원 반사 효과 추가
import { Environment } from '@react-three/drei';
<Canvas>
<Environment preset="sunset" background /> {/* 'city', 'sunset', 'night' 등 */}
<mesh>
<sphereGeometry />
<meshStandardMaterial metalness={1} roughness={0.1} />
</mesh>
</Canvas>
④ Sky, Stars – 자연 배경 효과
개념:
- Sky: 태양, 공기 산란 기반의 하늘
- Stars: 밤하늘 별 표현
실습 목표:
- 태양 위치나 별의 밀도 등을 조절해 시각적 효과 비교
import { Sky, Stars } from '@react-three/drei';
<Canvas>
<Sky sunPosition={[100, 20, 100]} />
{/* 또는 */}
{/* <Stars radius={100} depth={50} count={5000} factor={4} fade /> */}
</Canvas>
⑤ useProgress, Html – 로딩 UI 표시
개념:
- useProgress: 현재 로딩 상태 정보 (퍼센트 등) 제공
- Html: 일반 HTML을 Canvas 위에 렌더링 (예: 로딩 텍스트)
실습 목표:
- GLTF 로딩 시 퍼센트 표시되는 UI 만들기
import { Html, useProgress } from '@react-three/drei';
function Loader() {
const { progress } = useProgress();
return (
<Html center>
<div style={{ color: 'white' }}>Loading... {progress.toFixed(0)}%</div>
</Html>
);
}
// 사용 위치
<Canvas>
<Suspense fallback={<Loader />}>
<YourGLTFScene />
</Suspense>
</Canvas>
⑥ Bounds – 자동 카메라 프레이밍
개념:
Bounds는 자동으로 카메라 위치와 줌을 조절해 모든 오브젝트가 보이게 해줍니다.
실습 목표:
- 다양한 위치에 있는 Mesh들을 포함하도록 자동으로 카메라 조절
import { Bounds, OrbitControls } from '@react-three/drei';
<Canvas>
<Bounds fit clip observe margin={1.2}>
<OrbitControls />
<mesh position={[-2, 0, 0]}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
<mesh position={[2, 0, 0]}>
<sphereGeometry />
<meshStandardMaterial />
</mesh>
</Bounds>
</Canvas>
OrbitControls | 마우스 기반 카메라 제어 | 기본 회전/줌 |
PerspectiveCamera, OrthographicCamera | 카메라 유형 비교 | 시야각, 줌 이해 |
Environment | HDR 조명 환경 | preset 테스트 |
Sky, Stars | 자연 배경 효과 | 태양 위치 / 별 표현 |
useProgress, Html | 로딩 상태 표시 | Suspense와 함께 사용 |
Bounds | 자동 카메라 프레이밍 | 오브젝트 자동 보기 맞춤 |
2단계: 3D UI와 애니메이션 연동
목표: 상호작용 가능한 UI와 3D 오브젝트에 애니메이션을 연동하여, 몰입감 있는 인터페이스를 구현
1. Text – 3D 텍스트 생성
import { Text } from '@react-three/drei';
<Text
font="/fonts/Inter-Bold.woff"
fontSize={1}
color="white"
anchorX="center"
anchorY="middle"
position={[0, 2, 0]}>
Hello World!
</Text>
설명:
- font: WebFont (.woff, .ttf) 경로
- fontSize: 글자 크기 (world unit)
- anchorX/Y: 기준점 (중앙 정렬 등)
- 3D 공간 내에 위치하는 텍스트로써 HUD와 다름
2. useCursor – 마우스 상태에 따른 커서 변경
import { useCursor } from '@react-three/drei';
import { useState } from 'react';
const MyMesh = () => {
const [hovered, setHovered] = useState(false);
useCursor(hovered);
return (
<mesh
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
);
};
설명:
- 마우스가 오브젝트 위에 올라가면 cursor: pointer 적용
- hovered 상태만 관리해주면 됨
3. useGLTF, Gltf – GLTF 모델 불러오기
import { useGLTF } from '@react-three/drei';
const Model = () => {
const { scene } = useGLTF('/models/character.glb');
return <primitive object={scene} />;
};
import { Gltf } from '@react-three/drei';
<Gltf src="/models/character.glb" scale={2} position={[0, 0, 0]} />;
설명:
- useGLTF: 로우 레벨 방식, scene, animations 등 세부 데이터 제어 가능
- Gltf: 고수준 컴포넌트, props로 간단히 배치 가능
4. useAnimations – glTF 내장 애니메이션 제어
import { useGLTF, useAnimations } from '@react-three/drei';
const AnimatedModel = () => {
const { scene, animations } = useGLTF('/models/walking.glb');
const { actions } = useAnimations(animations, scene);
useEffect(() => {
actions['Walk']?.play();
}, [actions]);
return <primitive object={scene} />;
};
설명:
- glTF에 포함된 애니메이션 이름을 알아야 사용 가능
- actions[name].play()로 재생
5. Float – 오브젝트 부드러운 부유 애니메이션
import { Float } from '@react-three/drei';
<Float floatIntensity={1} speed={2}>
<mesh>
<sphereGeometry />
<meshStandardMaterial color="hotpink" />
</mesh>
</Float>
설명:
- 상하좌우로 천천히 움직이는 float 효과 부여
- UI 강조나 오브젝트의 “생동감” 표현에 좋음
6. PresentationControls – 오브젝트를 제한된 범위로 회전 가능하게
import { PresentationControls } from '@react-three/drei';
<PresentationControls
global
rotation={[0, 0.3, 0]}
polar={[-0.5, 0.5]}
azimuth={[-1, 1]}
config={{ mass: 2, tension: 400 }}
snap>
<MyModel />
</PresentationControls>
설명:
- 사용자가 마우스로 오브젝트를 회전해서 볼 수 있게 함
- snap을 켜면 마우스 놓았을 때 위치 고정
7. Stage – 빠른 배치 + 조명 + 환경 설정 프리셋
import { Stage } from '@react-three/drei';
<Stage environment="city" intensity={1}>
<MyModel />
</Stage>
설명:
- 기본 조명, 바닥 그림자, HDRI 조명 등을 자동 설정
- 빠르게 제품처럼 “쇼룸” 느낌을 줄 수 있음
3단계: 포스트 프로세싱 및 머티리얼 응용
목표: 시각적으로 완성도 높은 씬 구성과 커스텀 머티리얼 연출 능력 배양
3.1 특수 머티리얼 사용
핵심 목적: 반사, 왜곡, 레이어 표현 등을 통한 시각적 깊이 표현
- [<MeshReflectorMaterial />]
- 수면, 거울 같은 반사 구현
- mirror, blur, mixBlur, depthScale 등의 속성 이해
- [<MeshDistortMaterial />]
- 메시 표면 왜곡 효과
- distort, speed로 애니메이션 주기
- [<LayerMaterial /> + <Depth />, <Fresnel />]
- 레이어 기반 머티리얼 합성
- 레이마칭 느낌의 표현 가능 (예: 유리/플라즈마 효과)
3.2 고급 렌더링 소스 활용
핵심 목적: 셰이더 및 후처리에 사용할 렌더링 정보를 확보
- [useFBO()]
- Framebuffer Object 사용
- 화면을 오프스크린으로 렌더링하여 재사용 (ex: 반사, 피킹)
- [useDepthBuffer()]
- 씬의 깊이 버퍼 정보 접근
- 그림자나 포스트 이펙트 연산에 활용
- [useNormalTexture()]
- 노멀 맵을 실시간으로 생성하거나 적용
- 라이트와의 상호작용 강화
3.3 사용자 정의 셰이더
핵심 목적: 완전한 표현력 확보 및 자신만의 머티리얼 제작
- [shaderMaterial()] + extend()
- Drei에서 쉽게 GLSL 셰이더 정의 및 사용
- vertexShader, fragmentShader, uniforms 직접 제어
- [onBeforeCompile 방식과 비교 학습]
- 기존 머티리얼 커스터마이징 vs 완전 셰이더 작성
3.4 그림자 최적화와 연출
핵심 목적: 퍼포먼스와 리얼리즘의 균형 있는 그림자 처리
- [<BakeShadows />]
- 씬 렌더 후 그림자를 Bake(굽기)해서 고정
- 정적 씬에서 퍼포먼스 향상
- [<ContactShadows />]
- 바닥과 오브젝트 접점에만 그림자 생성 (AO 느낌)
- lightweight + 자연스러운 효과
- [<AccumulativeShadows /> + <RandomizedLight />]
- 시간 누적 기반 부드러운 글로벌 그림자
- soft shadow, ambient occlusion 효과 비슷
3단계 항목별로 시각적 결과를 상상할 수 있게 샘플 코드 + 설명
1. MeshReflectorMaterial: 반사 재질
import { MeshReflectorMaterial } from '@react-three/drei'
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]}>
<planeGeometry args={[10, 10]} />
<MeshReflectorMaterial
blur={[300, 100]} // [blur width, blur height]
resolution={1024}
mixBlur={1} // 반사 블러 강도
mixStrength={2} // 반사 세기
roughness={1}
depthScale={1}
minDepthThreshold={0.9}
maxDepthThreshold={1}
color="#333"
metalness={0.5}
/>
</mesh>
결과 느낌:
카메라 아래 깔린 바닥에서 반사된 오브젝트 실루엣이 보임. 스튜디오 조명 바닥 느낌.
2. MeshDistortMaterial: 부드럽게 왜곡된 표면
import { MeshDistortMaterial } from '@react-three/drei'
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<MeshDistortMaterial color="hotpink" distort={0.3} speed={2} />
</mesh>
결과 느낌:
물방울처럼 펄럭이고 일렁이는 구체. 조명과 함께 생동감 있는 시각 효과.
3. LayerMaterial: 계층별 머티리얼 효과
import { LayerMaterial, Depth, Noise } from 'lamina'
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<LayerMaterial lighting="physical">
<Depth colorA="skyblue" colorB="blue" alpha={1} mode="normal" near={0} far={2} origin={[1, 1, 1]} />
<Noise mapping="local" type="simplex" scale={100} colorA="white" colorB="black" mode="softlight" alpha={0.5} />
</LayerMaterial>
</mesh>
결과 느낌:
빛이 깊이에 따라 바뀌는 구체 위에 노이즈 텍스처가 겹침. 추상적이고 미래지향적 분위기.
4. useFBO: 프레임버퍼 오브젝트로 오프스크린 렌더링
import { useFBO } from '@react-three/drei'
import { useFrame, useThree } from '@react-three/fiber'
import { useRef } from 'react'
function FBOExample() {
const fbo = useFBO()
const { gl, scene, camera } = useThree()
const meshRef = useRef()
useFrame(() => {
gl.setRenderTarget(fbo)
gl.render(scene, camera)
gl.setRenderTarget(null)
})
return (
<mesh ref={meshRef}>
<planeGeometry args={[2, 2]} />
<meshBasicMaterial map={fbo.texture} />
</mesh>
)
}
결과 느낌:
화면에 렌더된 이미지를 텍스처로 활용 → 반사, 미러, 미니맵, 모니터 화면 등에 사용.
5. shaderMaterial: 직접 GLSL 셰이더 작성
import { shaderMaterial } from '@react-three/drei'
import glsl from 'babel-plugin-glsl/macro'
const MyMaterial = shaderMaterial(
{ uTime: 0 },
glsl`void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
glsl`uniform float uTime;
void main() {
gl_FragColor = vec4(abs(sin(uTime)), 0.2, 0.4, 1.0);
}`
)
extend({ MyMaterial })
function CustomShader() {
const ref = useRef()
useFrame((_, delta) => {
ref.current.uTime += delta
})
return (
<mesh>
<planeGeometry args={[2, 2]} />
<myMaterial ref={ref} />
</mesh>
)
}
결과 느낌:
시간에 따라 색이 변화하는 평면, 완전히 사용자 정의된 머티리얼.
6. 그림자 최적화
6-1. BakeShadows
<BakeShadows />
결과 느낌:
정적인 그림자를 미리 계산하여 성능 향상. 애니메이션 없는 배경에서 유용.
6-2. ContactShadows
<ContactShadows
position={[0, -1, 0]}
opacity={0.5}
scale={10}
blur={1.5}
far={10}
/>
결과 느낌:
오브젝트 밑에 부드러운 접촉 그림자, 현실감 증가.
6-3. AccumulativeShadows
<AccumulativeShadows temporal frames={100} color="purple" colorBlend={0.5}>
<RandomizedLight amount={8} radius={4} ambient={0.5} position={[5, 5, 5]} />
</AccumulativeShadows>
결과 느낌:
여러 빛의 누적 효과로 실제 조명에 가까운 그림자. 부드럽고 생생함.
4단계: 포탈, 모듈화, 퍼포먼스 최적화
- 복잡한 씬을 포탈처럼 분기 렌더링
- UI/카메라 분리 구조 구현
- 수천 개의 객체를 렌더링할 수 있도록 인스턴싱
- GPU 사양에 따라 렌더링 품질 자동 조절
주요 컴포넌트 및 훅
1. <MeshPortalMaterial>
기능: 씬 내 일부 메시에서 다른 뷰(서브 씬)를 렌더링하는 포탈 시스템
활용 예:
- 액자 안에서 다른 씬 보여주기
- VR/AR의 창처럼 뷰 전환
핵심 학습 포인트
- ref.blend를 이용한 부드러운 전환
- events 속성으로 포인터 이벤트 전달 여부 제어
2. <View> & <RenderTexture>
기능: 하나의 캔버스에서 멀티 카메라, 멀티 씬 구성
활용 예:
- 미니맵, CCTV 화면
- HUD (Heads Up Display)
- 보조 화면 (멀티 렌더링)
핵심 학습 포인트
- <RenderTexture>로 별도 씬과 카메라를 정의
- <View>를 특정 <div>에 연결하여 UI 내 분할 렌더링
3. <Instances> / <Merged> / <InstancedRigidBodies>
기능: 동일한 지오메트리/머티리얼을 공유하여 수백~수천 개 객체를 GPU 하나의 draw call로 처리
활용 예
- 수천 개의 별, 나무, 돌
- 리듬 게임 노트, 파티클 시스템
핵심 학습 포인트
- <Instances>는 내부에서 THREE.InstancedMesh를 생성
- <Merged>는 여러 메시를 병합하여 일괄 렌더링
- <InstancedRigidBodies>는 Rapier와 함께 사용 시 물리 기반 인스턴싱 가능
4. <Preload> & useDetectGPU()
기능:
- <Preload all />: Drei 내 비동기 리소스를 미리 로드
- useDetectGPU(): 렌더링 전에 사용자의 GPU 성능을 평가
활용 예:
- 고성능 GPU에는 반사 재질, 그림자 활성화
- 저성능에서는 최적화된 경량 씬 로딩
핵심 학습 포인트
- GPU 분류: 'high-performance', 'medium-performance', 'low-performance'
- 조건부 구성 렌더링 (if (gpu.tier > 2) { ... })
실전 예제
- 포탈 갤러리 만들기
- MeshPortalMaterial + Text + onDoubleClick으로 씬 전환
- RenderTexture 기반 CCTV
- 메인 카메라로 렌더되지만 별도 <RenderTexture>로 미니뷰 구성
- Instanced Particles
- <Instances>로 별이나 입자 수천 개 렌더링
- GPU 감지 후 조건 분기
- useDetectGPU()로 저사양에서 그림자 비활성화
추가 기능: 시야 최적화 및 UI 보강
1. useAspect – 뷰포트 비율에 따라 오브젝트 크기 자동 조절
import { useAspect } from '@react-three/drei';
const scale = useAspect(1280, 720, 1); // width, height, scale factor
<mesh scale={scale}>
<planeGeometry />
<meshBasicMaterial color="white" />
</mesh>
- 언제 사용?: 화면 크기에 따라 평면 UI 또는 배경 크기를 자동 조절할 때
2. useBounds – 자동으로 카메라 위치와 줌 조절
import { useBounds } from '@react-three/drei';
const bounds = useBounds();
return (
<mesh
onClick={(e) => {
e.stopPropagation();
bounds.refresh(e.object).fit(); // 카메라가 클릭된 오브젝트에 맞게 이동
}}
/>
);
- 언제 사용?: 특정 오브젝트 클릭 시 자동으로 카메라가 프레이밍되게 할 때
3. Center – 중앙 정렬
import { Center } from '@react-three/drei';
<Center>
<Text fontSize={0.5}>Centered Text</Text>
</Center>
- 언제 사용?: 씬의 중앙에 정확히 위치시키고 싶을 때 (폰트나 복잡한 메시 포함)
4. Billboard – 항상 카메라를 바라보게
import { Billboard } from '@react-three/drei';
<Billboard>
<Text fontSize={0.2}>Always Facing Camera</Text>
</Billboard>
- 언제 사용?: UI 텍스트, 안내 아이콘 등 카메라 시점과 항상 정렬해야 할 때
5. Html – DOM 요소와 Three.js 위치 동기화
import { Html } from '@react-three/drei';
<Html position={[0, 1.5, 0]}>
<div style={{ background: 'white', padding: '0.5em' }}>설명 박스</div>
</Html>
- 언제 사용?: 3D 씬 위에 HTML 툴팁, 버튼, 정보창을 띄우고 싶을 때
6. Backdrop + ContactShadows – 고급 배경 & 그림자 효과
import { Backdrop, ContactShadows } from '@react-three/drei';
<Backdrop floor={1.5} segments={20}>
<meshStandardMaterial color="#f0f0f0" />
</Backdrop>
<ContactShadows position={[0, -1.5, 0]} opacity={0.4} blur={2.5} />
- 언제 사용?: 부드러운 배경과 그림자를 통해 실감 나는 조명 연출을 할 때
'2D&3D > ThreeJS' 카테고리의 다른 글
Three.js Journey 03 (1) | 2025.07.21 |
---|---|
maath (0) | 2025.06.09 |
무료로 R3F 아바타 생성하기 (1) | 2025.05.26 |
R3F(React Three Fiber)의 기본 요소와 기본 문법 (4) | 2025.03.06 |
Three js 시작하기 (0) | 2025.02.28 |