Communitygithub.com

phido0410/threejs-3d

Three.js, React Three Fiber(R3F), Drei, Rapier 물리, 후처리 효과를 사용한 3D 씬 구축. 웹 또는 비디오용 3D 씬(모델, 조명, 카메라, 환경 맵, 물리, 블룸/비네트/피사계 심도)을 만들 때 유용합니다.

threejs-3d란 무엇인가요?

이 스킬은 Three.js와 React Three Fiber(R3F) 생태계를 활용하여 웹 및 비디오용 3D 씬을 구축하는 방법을 설명합니다. 기본 씬 설정, Drei 헬퍼, Rapier 물리 엔진, 후처리 효과 등 주요 구성 요소를 다루며, 각각의 최신 버전 호환성(2026-10 기준)과 설치 명령어를 제공합니다. 또한 웹 환경에서의 일반적인 3D 렌더링과 Remotion을 사용한 비디오 제작 시의 차이점을 명확히 구분하여 설명하며, 비디오에서는 useFrame 대신 useCurrentFrame을 사용하고, 물리는 미리 베이킹하는 등 특별한 주의 사항을 강조합니다. SKILL.md는 구체적인 코드 예제와 함께 실용적인 규칙(예: 비동기 작업은 Suspense 아래에 배치, 씬 스케일 유지, 후처리 효과 개수 제한 등)을 제공하여 개발자가 빠르고 효율적으로 3D 콘텐츠를 제작할 수 있도록 돕습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/phido0410/claude-video-skills/tree/HEAD/skills/threejs-3d

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Three.js 3D 스택

2026-10 확인: three 0.186, @react-three/fiber 9.8, @react-three/drei 10.7, @react-three/rapier 2.2, @react-three/postprocessing 3.2 (+ postprocessing 6.39). Fiber 9 / Drei 10 / Rapier 2 / postprocessing 3은 React 19 필요 (fiber 9.8 peer: react >=19 <19.4).

npm i three @react-three/fiber @react-three/drei
npm i @react-three/rapier                         # 물리
npm i @react-three/postprocessing postprocessing  # 효과
npm i -D @types/three

기본 씬 (웹)

import { useRef, Suspense } from 'react'
import * as THREE from 'three'
import { Canvas, useFrame } from '@react-three/fiber'
import { OrbitControls, Environment, ContactShadows } from '@react-three/drei'

function Box() {
  const ref = useRef<THREE.Mesh>(null!)
  useFrame((_, delta) => { ref.current.rotation.y += delta }) // refs를 직접 변경, 프레임당 setState 금지
  return (
    <mesh ref={ref} castShadow>
      <boxGeometry />
      <meshStandardMaterial color="#4a9eff" />
    </mesh>
  )
}

<Canvas camera={{ position: [3, 2, 5], fov: 45 }} dpr={[1, 2]} shadows>
  <ambientLight intensity={0.3} />
  <directionalLight position={[5, 5, 5]} castShadow />
  <Suspense fallback={null}>
    <Box />
    <Environment preset="city" />
  </Suspense>
  <ContactShadows position={[0, -0.5, 0]} opacity={0.5} blur={2} />
  <OrbitControls />
</Canvas>

규칙:

  • 비동기 작업(useGLTF, useTexture, Environment, Text 폰트, Physics WASM)은 모두 <Suspense> 아래에 배치합니다.
  • 첫 페인트에 사용되는 모델은 모듈 레벨에서 useGLTF.preload(url)을 호출합니다.
  • 씬 크기는 약 1~10 단위로 유지합니다. 너무 크거나 작은 스케일은 그림자와 컨트롤을 망가뜨립니다.
  • MeshStandardMaterial에는 조명 또는 <Environment>가 필요합니다. 둘 다 없으면 검은색으로 렌더링됩니다.

Drei (필요한 것만 선택)

  • 카메라/컨트롤: OrbitControls, CameraControls, PerspectiveCamera makeDefault
  • 에셋: useGLTF, useTexture, Environment (프리셋: apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse), Center, Bounds
  • 스테이징: ContactShadows, AccumulativeShadows, Stage, Float, Sparkles, Stars, Cloud
  • 재질: MeshTransmissionMaterial (유리), MeshReflectorMaterial (반사 바닥)
  • 텍스트: Text (SDF, 모든 폰트 URL 지원), Text3D (typeface JSON 필요), Html (DOM 오버레이)

Rapier 물리

import { Physics, RigidBody, CuboidCollider, type RapierRigidBody } from '@react-three/rapier'

<Suspense>
  <Physics gravity={[0, -9.81, 0]} /* debug */>
    <RigidBody colliders="hull" restitution={0.6} ref={bodyRef}>
      <mesh><torusGeometry /><meshStandardMaterial /></mesh>
    </RigidBody>
    <RigidBody type="fixed">
      <CuboidCollider args={[10, 0.1, 10]} /> {/* 절반 크기 */}
    </RigidBody>
  </Physics>
</Suspense>
// bodyRef.current?.applyImpulse({ x: 0, y: 5, z: 0 }, true)
  • colliders: "cuboid" (기본값), "ball", "hull", "trimesh", false (직접 충돌체 컴포넌트 추가).
  • type: "dynamic" (기본값), "fixed", "kinematicPosition", "kinematicVelocity".
  • timeStep 기본값은 고정 1/60입니다. timeStep="vary"는 결정론적이지 않습니다. 수동 스테핑: useRapier().step(1 / 60).
  • <Physics>의 debug는 충돌체를 그립니다.

후처리

import { EffectComposer, Bloom, Vignette, DepthOfField, Noise } from '@react-three/postprocessing'

<EffectComposer>{/* <Canvas>의 마지막 자식 */}
  <Bloom luminanceThreshold={0.9} intensity={1.2} mipmapBlur />
  <Vignette offset={0.1} darkness={0.9} />
</EffectComposer>
  • Bloom은 luminanceThreshold 이상의 픽셀만 캡처합니다. 특정 오브젝트를 빛나게 하려면: 재질에 emissive 색상 + emissiveIntensity > 1 + toneMapped={false}를 설정합니다.
  • 각 효과는 풀스크린 패스 하나를 소모합니다. 2~4개의 효과면 충분합니다.

비디오용

Remotion (remotion-markup/3d.md 참조): @remotion/three의 <ThreeCanvas width height>를 사용하며, useFrame 대신 모든 움직임은 useCurrentFrame()에서 비롯됩니다. 추가로:

  • OrbitControls는 사용하지 않습니다. 대신 frame으로 카메라를 움직입니다 (interpolate / spring).
  • 자체 클록으로 애니메이션되는 Drei 헬퍼(Float, Sparkles, Stars, Cloud, MeshWobbleMaterial)는 렌더링에서 깜빡임을 유발합니다. 사용을 피하거나 frame으로 동작을 재현합니다.
  • 물리: Remotion은 프레임을 여러 탭에서 순서에 관계없이 렌더링하므로 실시간 시뮬레이션을 일치시킬 수 없습니다. 베이킹합니다: @dimforge/rapier3d-compat(@react-three/rapier의 기반 엔진)을 한 번 초기화하고, 모든 프레임에 대해 world.step()을 timestep = 1 / fps로 호출하여 각 바디의 위치 + 회전을 프레임별로 저장한 다음, frame 인덱스에서 변환을 렌더링합니다.

Remotion 외부 (화면 녹화, GSAP 구동): useFrame 사용 가능. 장면이 반복 가능해야 하는 경우 delta 대신 타임라인의 시간(tl.time())으로 구동합니다.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

관련 스킬