はじめに
2025年11月19日(水)にMeta社から「SAM 3D」が公開され、写真1枚から3Dオブジェクトが生成できるようになりました。
これまでも近しいサービスはありましたが
・現状無料で利用できること
・特殊なケースを除き、商用利用可(SAM license)
・写真1枚から3Dオブジェクトが作成でき、映っていない部分の補完が優秀
などの理由から大注目のサービスです!
この記事では、SAM 3Dから3Dオブジェクトを作成し、Webブラウザで扱えるようになるまでの手順について書いていきます。
この記事で扱う技術
| 技術 | 用途 |
|---|---|
| SAM 3D | 画像から3Dオブジェクト生成 |
| SuperSplat | 3Dファイルの編集・変換 |
| React Three Fiber | Webでの3D表示 |
| spark.js | Gaussian Splatファイルの読み込み |
1. 画像から3Dオブジェクトを作成
1-1. デモサイトにアクセス
まずはデモサイトにアクセスして3D化したい画像をアップロードします。
1-2. 3Dオブジェクトを生成
選択範囲を決定して、Generate 3Dをクリックします。

3秒くらいでクリックした3Dの椅子が生成されました。すごい。。。

1-3. 3Dオブジェクトをダウンロード
デモサイトなのでこの3Dオブジェクトがダウンロードできないと思いきや、
Add effects → Share → 3D Modelタブ → Download 3D Model
で流行りのGaussian Splatting形式のPLYファイルがダウンロードできます!

ローカルでの実行について
ちなみにローカルでも利用可能なのですが、
一般家庭にはまずないだろうというスペックが求められるため、デモサイトから実行しています。
A NVIDIA GPU with at least 32 Gb of VRAM.
https://github.com/facebookresearch/sam-3d-objects
Gaussian Splattingとは?
2023年に登場した3D表現の新技術。従来のポリゴンや点群ではなく、
無数の「ぼかし(ガウス分布)」を重ね合わせて3Dを表現します。
リアルな質感を軽量・高速に再現できるため、急速に普及しています。
2. ダウンロードしたオブジェクトの編集
次にWebで扱えるようにするためにSuperSplatを使用していきます。
2-1. ファイルをインポート
サイトを開いたら、左上にある「ファイル」から「インポート」を選択して先ほどダウンロードした3Dオブジェクトを読み込みます。
2-2. 回転を調整
左にあるトランスフォームの回転を変更します。
回転
| 軸 | 値 |
|---|---|
| X | -90 |
| Y | 0 |
| Z | 180 |
椅子がひっくり返った感じになっていますが、Webで表示したときには正しく表示されますのでこの設定で大丈夫です。

2-3. エクスポート
「ファイル」→「エクスポート」→「Splat(.splat)」を選択してエクスポートします。
| 項目 | 変換前 | 変換後 |
|---|---|---|
| 拡張子 | .ply | .splat |
| サイズ | 7,363KB | 3,465KB |
ファイルサイズが約半分になりました!
さらに容量を削減したい場合
以下のサイトでksplat形式にコンバートできます。
https://projects.markkellogg.org/threejs/demo_gaussian_splats_3d.php
3. Web表示
ここまでで表示するオブジェクトファイルの作成が完了したので、React Three Fiberを使用してWeb表示できるようにしましょう。
3-1. プロジェクトのセットアップ
# Vite + Reactプロジェクトの作成
npm create vite@latest my-app -- --template react-ts
次に依存関係の3D関連のライブラリをインストールします。
3-2. 依存関係のインストール
npm install three @react-three/fiber @react-three/drei @sparkjsdev/spark
各ライブラリの役割は以下の通りです。
| ライブラリ | 役割 |
|---|---|
| three | 3D表現のコアライブラリ |
| @react-three/fiber | Three.jsをReactの宣言的な方法で扱う |
| @react-three/drei | React Three Fiber用のヘルパーコンポーネント集 |
| @sparkjsdev/spark | .splat/.ksplatファイルの表示機能 |
3-3. CSSの修正
Viteのテンプレートに存在しているcssを書き換えます。
全画面でCanvasを表示するための修正になります。
body {
margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
+ padding: 0;
+ overflow: hidden;
}
3-4. splat(ksplat)ファイルを配置
作成したsplat(またはksplat)ファイルをpublicフォルダに配置します。
3-5. App.tsxの書き換え
Modelコンポーネントでsplatファイルを読み込み、Canvas内に描画しています。
OrbitControls: マウス操作でカメラを動かし、オブジェクトを様々な角度から見られます。
import { Canvas } from "@react-three/fiber"
import { OrbitControls } from "@react-three/drei"
import { SplatMesh } from "@sparkjsdev/spark"
import { useEffect, useMemo } from "react"
const Model = ({ url }: { url: string }) => {
// SplatMeshインスタンスを生成(urlが変わった時のみ再生成)
const splat = useMemo(() => {
return new SplatMesh({ url })
}, [url])
// クリーンアップ処理:コンポーネント破棄時にメモリを解放
useEffect(() => {
return () => {
splat.dispose()
}
}, [splat])
// Three.jsのオブジェクトをReactコンポーネントとして描画
return (
<primitive object={splat} />
)
}
const App = () => {
return (
<div style={{
width: "100%",
height: "100vh",
background: "linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 50%, #e1bee7 100%)"
}}>
<Canvas camera={{ position: [0, 1, 2], fov: 60 }}>
{/* urlはpublic配下にいれたsplat,ksplatのファイル名を入れます */}
<Model url="/yellow-chair.ksplat" />
<OrbitControls />
</Canvas>
</div>
)
}
export default App
3-6. 動作確認
npm run dev
4. 活用例
4-1. キービジュアル風
Webサイトやアプリのアクセントとして使えそうです。
ここで使用しているFloatはdreiのコンポーネントで、ラップした要素をゆるやかに揺らす事ができます。
import { Canvas } from "@react-three/fiber"
import { Float } from "@react-three/drei"
import { SplatMesh } from "@sparkjsdev/spark"
import { useEffect, useMemo } from "react"
type ModelProps = {
url: string
position?: [number, number, number]
scale?: [number, number, number] | number
rotation?: [number, number, number]
}
const Model = ({ url, position, scale, rotation }: ModelProps) => {
// SplatMeshインスタンスを生成(urlが変わった時のみ再生成)
const splat = useMemo(() => {
return new SplatMesh({ url })
}, [url])
// クリーンアップ処理:コンポーネント破棄時にメモリを解放
useEffect(() => {
return () => {
splat.dispose()
}
}, [splat])
// Three.jsのオブジェクトをReactコンポーネントとして描画
return (
<primitive
object={splat}
position={position}
scale={scale}
rotation={rotation}
/>
)
}
const App = () => {
return (
<div style={{
width: "100%",
height: "100vh",
background: "linear-gradient(135deg, #e0f7fa 0%, #b2ebf2 50%, #e1bee7 100%)"
}}>
{/* 3Dシーンのキャンバス:カメラ位置と視野角を設定 */}
<Canvas camera={{ position: [0, 2, 5], fov: 60 }} onCreated={({ camera }) => camera.lookAt(0, -0.5, 0)}>
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<Model url="/yellow-chair.ksplat" position={[-0.8, 1, 0.5]} scale={1} rotation={[Math.PI / 8, 0, Math.PI / 8]} />
</Float>
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<Model url="/blue-chair.ksplat" position={[1, 0, 0]} scale={1} rotation={[0, Math.PI / 4, 0]} />
</Float>
<Model url="/brown-sofa.ksplat" position={[0, -2, 0]} scale={3} rotation={[0, 0, 0]} />
</Canvas>
</div>
)
}
export default App
4-2. クリックで情報パネルを表示
オブジェクトをクリックすると、詳細情報が表示されます。ギャラリー的な使い方が出来そうですね。
クリックしてオブジェクトの情報を表示する
import { Canvas } from "@react-three/fiber"
import { CameraControls, Html } from "@react-three/drei"
import { SplatMesh } from "@sparkjsdev/spark"
import { useEffect, useMemo, useState, useRef } from "react"
import type CameraControlsImpl from "camera-controls"
// モデルデータの型定義
type ModelData = {
id: string
url: string
position: [number, number, number]
scale: number
rotation: [number, number, number]
title: string
description: string
}
// モデルのデータ
const models: ModelData[] = [
{
id: "yellow-chair",
url: "/yellow-chair.ksplat",
position: [-2, 0, 1],
scale: 1.2,
rotation: [0, Math.PI / 6, 0],
title: "黄色い椅子",
description: "明るい黄色の北欧風デザインチェア。軽量で持ち運びやすく、どんな部屋にも馴染みます。",
},
{
id: "blue-chair",
url: "/blue-chair.ksplat",
position: [2, 0, 1],
scale: 1.2,
rotation: [0, -Math.PI / 6, 0],
title: "青い椅子",
description: "落ち着いた青色のモダンチェア。座り心地が良く、長時間の使用でも快適です。",
},
{
id: "brown-sofa",
url: "/brown-sofa.ksplat",
position: [0, 0, -1],
scale: 3,
rotation: [0, 0, 0],
title: "レザーソファ",
description: "高級感のあるブラウンのレザーソファ。3人掛けで、リビングの主役になる一品です。",
}
]
type ModelProps = {
data: ModelData
onClick: () => void
}
const Model = ({ data, onClick }: ModelProps) => {
const { url, scale, rotation } = data
const splat = useMemo(() => new SplatMesh({ url }), [url])
useEffect(() => {
return () => splat.dispose()
}, [splat])
return (
<primitive
object={splat}
scale={scale}
rotation={rotation}
onClick={onClick}
/>
)
}
type InfoPanelProps = {
title: string
description: string
}
const InfoPanel = ({ title, description }: InfoPanelProps) => (
<Html position={[0, 1, 0]} center style={{ whiteSpace: "nowrap" }}>
<div style={{
background: "rgba(0, 0, 0, 0.6)",
padding: "20px 24px",
borderRadius: "16px",
boxShadow: "0 10px 40px rgba(0,0,0,0.2)",
width: "280px",
backdropFilter: "blur(10px)",
whiteSpace: "normal",
}}>
<h3 style={{
margin: "0 0 12px 0",
fontSize: "18px",
fontWeight: "bold",
color: "#fafafa",
borderBottom: "2px solid #e0e0e0ff",
paddingBottom: "8px"
}}>
{title}
</h3>
<p style={{ margin: 0, fontSize: "14px", color: "#fafafa", lineHeight: "1.6" }}>
{description}
</p>
</div>
</Html>
)
const App = () => {
const [selectedId, setSelectedId] = useState<string | null>(null)
const cameraControls = useRef<CameraControlsImpl | null>(null)
const handleModelClick = (model: ModelData) => {
setSelectedId(selectedId === model.id ? null : model.id)
cameraControls.current?.setLookAt(
model.position[0] + 2, model.position[1] + 1.5, model.position[2] + 3,
model.position[0], model.position[1], model.position[2],
true
)
}
return (
<div style={{ width: "100%", height: "100vh", background: "#fff" }}>
<Canvas camera={{ position: [0, 3, 4], fov: 50 }}>
<CameraControls ref={cameraControls} />
<ambientLight intensity={0.6} />
<directionalLight position={[5, 5, 5]} intensity={0.8} />
{models.map((model) => (
<group key={model.id} position={model.position}>
<Model data={model} onClick={() => handleModelClick(model)} />
{selectedId === model.id && (
<InfoPanel title={model.title} description={model.description} />
)}
</group>
))}
<gridHelper args={[20, 20, "#020202"]} position={[0, -0.5, 0]} />
</Canvas>
</div>
)
}
export default App
まとめ
この記事では、Meta社の「SAM 3D」を使って写真1枚から3Dオブジェクトを生成し、Webブラウザで表示するまでの流れを解説しました。
| ステップ | 内容 |
|---|---|
| 1. 生成 | SAM 3Dデモサイトで画像から3Dオブジェクトを生成 |
| 2. 変換 | SuperSplatでPLY→splat形式に変換し軽量化 |
| 3. 表示 | React Three Fiber + sparkでWebブラウザに表示 |
専門知識がなくても気軽に3Dオブジェクトを作成できることもあり、ゲームのオブジェクトとして利用してみたり、バーチャル展示会などの様々なケースで使えそうだと思いました。
ぜひ皆さんも試してみてください!
あと、R3Fの仕事あったら下さい!





