Unityで作成したシーンをエクスポートして、Three.jsで使用してみます。
手順は次のようになります。
- Unityでシーンを作成する
- FBX ExporterでFBXをエクスポートする
- FBX2glTFでFBXをglTFに変換する
- Three.jsでglTFを読み込む
UnityからエクスポートしたFBXを直接Three.jsで使用してもよいのですが、FBXよりもglTFの方が扱いが楽なのでglTFに変換しています。
この記事で使用するUnity、three.jsのバージョンは次のとおりです。
- Unity: 2021.2.10f1
- Three.js: r139
Unityでシーンを作成する
まず、エクスポートするシーンをUnityで作成します。今回は複数のメッシュとカメラを配置します。カメラにはアニメーションをつけておきます。エクスポート対象を一つのゲームオブジェクト(Root
)にまとめておきます。
カメラアニメーションはこんな感じです。
FBX ExporterでFBXをエクスポートする
UnityにFBX Exporterをインストールします。FBX ExporterはPackage ManagerのUnity Registryに存在しています。
FBX Exporterをインストール後、エクスポート対象をまとめたゲームオブジェクトを選択状態にしてMenu > GameObject > Export to FBX...
をクリック(またはゲームオブジェクトを右クリックして、Export to FBX...
をクリック)します。Export Options
が表示されるので、次のように設定してExport
ボタンを押すと、指定したパスにFBXがエクスポートされます。
FBX2glTFでFBXをglTFに変換する
UnityからエクスポートしたFBXをFBX2glTFでglTFに変換します。
自分でビルドしてもいいですが、Releaseページから各OSごとにビルド済みの実行ファイルを取得することができるので、今回はこちらを使用します。
Macの場合はターミナルから次のようにglTFに変換します。FBX2glTFではdroco圧縮などの多数のオプションがありますが、今回は単純にバイナリ形式(GLB)に変換するだけにします。実行するとUnityToThreejs.glb
が同じディレクトリに生成されます。
$ chmod +x ./FBX2glTF-darwin-x64
$ ./FBX2glTF-darwin-x64 --binary ./UnityToThreejs.fbx
Three.jsでglTFを読み込む
作成したglTFをThree.jsで読み込みます。読み込みにはGLTFLoaderを使用します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Unity to Three.js</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="./lib/threejs/three.min.js"></script>
<script src="./lib/threejs/examples/loaders/GLTFLoader.js"></script>
<script>
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x999999);
const dirLight = new THREE.DirectionalLight(0xffffff, 1.0);
dirLight.position.set(-1.0, 3.0, -1.0);
scene.add(dirLight);
const ambLight = new THREE.AmbientLight(0x333333);
scene.add(ambLight);
let camera = null;
let mixer = null;
const loader = new THREE.GLTFLoader();
loader.load('./assets/UnityToThreejs.glb', (gltf) => {
// アニメーションの再生
mixer = new THREE.AnimationMixer(gltf.scene);
gltf.animations.forEach((animation) => {
mixer.clipAction(animation).play();
});
const gltfCamera = gltf.cameras[0];
camera = new THREE.PerspectiveCamera(
gltfCamera.fov,
window.innerWidth / window.innerHeight,
// カメラのnearとfarはFBXに変換したときにセンチメートル単位になり、
// FBX2glTFではメートルに変換してくれないので、ここでメートル単位に変換する
gltfCamera.near * 0.01,
gltfCamera.far * 0.01
);
// カメラアニメーションに存在する謎回転に対処するために、
// 相殺する回転をかけてglTFカメラの子要素に追加する
camera.rotation.set(0, -0.5 * Math.PI, 0);
gltfCamera.add(camera);
scene.add(gltf.scene);
});
function resize() {
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', resize);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
// アニメーションの更新
if (mixer) {
mixer.update(clock.getDelta());
}
if (camera) {
renderer.render(scene, camera);
}
}
animate();
</script>
</body>
</html>
カメラアニメーションがなぜかY軸に対して90度回転するので、glTFで読み込んだカメラを直接使わずに、回転を相殺するようにrotation
を設定したカメラを子要素に追加して、そのカメラで描画するようにしています。
(カメラがY軸90度回転する問題はFBXを読み込んだときにも発生し、FBXの場合はアニメーションの有無に関わらず発生します。glTFの場合はアニメーションがなければ発生しません。おそらくFBX2glTFがアニメーションがない場合の回転は修正するが、アニメーションの回転は修正しないのではないかと思っています。)
Three.jsの描画は次のようになります。これでUnityのシーンをエクスポートしてThree.jsで描画することができました。
比較
上がUnity、下がThree.jsの描画です。ライティングやマテリアルなどの違いがあるので全く同じようには描画できていませんが、モデルの配置やカメラワークは同じようにすることができました。