drei とアセット

@react-three/drei は R3F 公式の「便利コンポーネント詰め合わせ」。 カメラ操作・環境光・GLTF・テキスト・shader・物理シャドウまで、よくある定石が宣言的に揃っている。 R3F を使うなら drei も入れるがデフォルトでいい。

導入

npm install @react-three/drei

全 import すると巨大バンドルになる。必ず named import で必要なものだけ:

import { OrbitControls, Environment, useGLTF } from "@react-three/drei"

カメラ・コントロール

<OrbitControls />

マウスでぐるぐる回せる定番カメラ。これを置くだけで使える。

import { OrbitControls } from "@react-three/drei"

<Canvas>
  <OrbitControls
    enableDamping
    dampingFactor={0.1}
    enablePan={false}
    enableZoom
    minDistance={2}
    maxDistance={20}
    minPolarAngle={0}
    maxPolarAngle={Math.PI / 2}    {/* 地面より下に潜らない */}
    minAzimuthAngle={-Infinity}
    maxAzimuthAngle={Infinity}
    autoRotate
    autoRotateSpeed={1}
    target={[0, 0, 0]}
  />
  ...
</Canvas>

その他のコントロール

コンポーネント用途
<PointerLockControls />FPS 風(マウスをロックして視点回転)
<FlyControls />飛行
<FirstPersonControls />地表を歩く視点
<TrackballControls />制限なしのトラックボール
<CameraControls />シネマティック(drei 製、API がリッチ)
<PresentationControls />商品紹介向けの「見せる」回転
<TransformControls />Blender 風ガイザー(移動/回転/スケール)
<DragControls />オブジェクトをドラッグ
<MapControls />Google Maps 風の操作(パン優先)

カメラコンポーネント

環境光・空・背景

<Environment />

HDR 画像で環境光(IBL)を作る。Standard / Physical Material が一気にリアルになる。

import { Environment } from "@react-three/drei"

<Environment preset="city" />
<Environment preset="sunset" background />             {/* 背景にも反映 */}
<Environment files="/hdr/studio.hdr" background />     {/* 自前 HDR */}
<Environment files={["px","nx","py","ny","pz","nz"]} path="/cubemap/" /> {/* CubeMap */}

プリセット: apartment / city / dawn / forest / lobby / night / park / studio / sunset / warehouse

環境光を JSX で組む(Lightformer)

既製プリセットでは足りない時、ライトを並べた仮想空間を IBL 化できる:

import { Environment, Lightformer } from "@react-three/drei"

<Environment background={false} resolution={256}>
  <Lightformer position={[5, 5, 5]} scale={[2, 2, 1]} intensity={1} />
  <Lightformer position={[-5, 0, 0]} scale={3} color="#9bd1ff" intensity={0.5} />
</Environment>

商品スタジオの定番: <Stage />

ライト + 環境光 + 接地影 + カメラ調整を全部入りで提供。プロトタイプ最強。

import { Stage } from "@react-three/drei"

<Canvas shadows>
  <Stage environment="city" intensity={0.5} adjustCamera shadows="contact">
    <MyModel />
  </Stage>
</Canvas>

アセット読み込み

useGLTF

import { useGLTF } from "@react-three/drei"

function Model() {
  const { scene, nodes, materials, animations } = useGLTF("/robot.glb")
  return <primitive object={scene} />
}

useGLTF.preload("/robot.glb")

DRACO や KTX2 もサポート:

// 内部で DRACOLoader / KTX2Loader を自動セットアップ
const gltf = useGLTF("/compressed.glb")
gltfjsx で生成 → 置き換え

gltfjsx に GLB をドロップすると、JSX のコードが生成される。
コマンドラインなら npx gltfjsx model.glb -t(TS 出力)。生成された JSX を貼って細部を調整するのが圧倒的に楽。

useTexture

import { useTexture } from "@react-three/drei"

// 配列で複数読み込み
const [map, normal, rough] = useTexture([
  "/wall_color.jpg", "/wall_normal.jpg", "/wall_rough.jpg",
])

// オブジェクトでマップ名を直接渡す
const props = useTexture({
  map: "/wall_color.jpg",
  normalMap: "/wall_normal.jpg",
  roughnessMap: "/wall_rough.jpg",
  aoMap: "/wall_ao.jpg",
})
<meshStandardMaterial {...props} />

その他のローダー

テキスト・3D テキスト

<Text />

SDF(signed distance field)方式の高品質 3D テキスト。

import { Text } from "@react-three/drei"

<Text
  fontSize={1}
  color="#7dd3fc"
  anchorX="center"
  anchorY="middle"
  font="/fonts/Inter-Bold.ttf"   {/* 任意のフォント */}
  outlineWidth={0.02}
  outlineColor="#000"
  maxWidth={5}
  textAlign="center"
>
  Hello R3F
</Text>

<Text3D />

押し出しのある立体テキスト。font プロパティに JSON フォントを渡す。

import { Text3D, Center } from "@react-three/drei"

<Center>
  <Text3D font="/fonts/helvetiker_regular.typeface.json" size={1} height={0.2}>
    Hello
    <meshStandardMaterial color="#fbbf24" />
  </Text3D>
</Center>

HTML を 3D に置く: <Html />

DOM のボタンやツールチップを 3D 空間に portal する。 詳しくは インタラクション参照。

マテリアル系

ガラスっぽい球
import { MeshTransmissionMaterial } from "@react-three/drei"

<mesh>
  <sphereGeometry args={[1, 64, 64]} />
  <MeshTransmissionMaterial
    transmission={1}
    thickness={0.5}
    roughness={0}
    ior={1.5}
    chromaticAberration={0.05}
    distortion={0.1}
  />
</mesh>

鏡面床

import { MeshReflectorMaterial } from "@react-three/drei"

<mesh rotation-x={-Math.PI / 2}>
  <planeGeometry args={[20, 20]} />
  <MeshReflectorMaterial
    blur={[300, 100]}
    resolution={1024}
    mixBlur={1}
    mixStrength={2}
    color="#101010"
  />
</mesh>

レイアウト系

コンポーネント用途
<Center />子要素のバウンディングから中央寄せ
<Bounds />子全体が画面に収まるようカメラを自動調整
<Resize />子要素を一定サイズに正規化
<Float />ふわふわ浮遊
<Backdrop />無限の壁・床(写真背景の白ホリゾント)
<ScrollControls /> + <Scroll />スクロールに連動

<Bounds /> の例

import { Bounds } from "@react-three/drei"

<Bounds fit clip observe margin={1.2}>
  <MyModel />
</Bounds>

パフォーマンス・最適化系

コンポーネント用途
<Detailed />LOD(距離で別モデルに切替)
<Instances /> + <Instance />InstancedMesh の宣言的ラッパ
<Merged />静的メッシュを 1 つに合体(draw call 削減)
<Preload all />シーン全体を GPU に転送して初回フリーズを防ぐ
<BakeShadows />シャドウを 1 回だけ計算
<AdaptiveDpr />FPS 低下時に DPR を下げる
<AdaptiveEvents />FPS 低下時にイベントを止める
<PerformanceMonitor />FPS を監視してコールバック

<Instances /> の例

import { Instances, Instance } from "@react-three/drei"

<Instances limit={1000} range={1000}>
  <coneGeometry args={[0.5, 1.5, 8]} />
  <meshStandardMaterial color="#22c55e" />
  {trees.map((t, i) => (
    <Instance key={i} position={t.pos} rotation={t.rot} scale={t.scale} color={t.color} />
  ))}
</Instances>

3D UI コンポーネント

デバッグ系

leva でデバッグ UI

npm install leva
import { useControls } from "leva"

function DebugBox() {
  const { color, scale, rotate } = useControls({
    color: "#7dd3fc",
    scale: { value: 1, min: 0.1, max: 3, step: 0.01 },
    rotate: false,
  })
  const ref = useRef<THREE.Mesh>(null!)
  useFrame((_, dt) => { if (rotate) ref.current.rotation.y += dt })
  return (
    <mesh ref={ref} scale={scale}>
      <boxGeometry />
      <meshStandardMaterial color={color} />
    </mesh>
  )
}

キーボード入力

<KeyboardControls /> + useKeyboardControlsインタラクション参照。

postprocessing(@react-three/postprocessing)

ポストエフェクト(Bloom / DoF / Vignette / Outline 等)は別パッケージ。 詳細は シェーダ・ポストプロセス

VR / AR — @react-three/xr

WebXR でヘッドセット・ハンドトラッキングに対応。drei とは別パッケージ。

import { XR, createXRStore, XROrigin } from "@react-three/xr"

const store = createXRStore()

<button onClick={() => store.enterVR()}>VR 開始</button>
<Canvas>
  <XR store={store}>
    <XROrigin>
      <Scene />
    </XROrigin>
  </XR>
</Canvas>
drei は「全部入り」だけど巨大

drei はトップレベル import すると大きい。 ツリーシェイク済みでも数百 KB。named import で必要なものだけ使うのが基本。 バンドル分析(vite-bundle-visualizer 等)で時々確認すると吉。