关于Unity导出场景用于Three.js的研究
前言
主要流程构思:
-
在 Unity 中创建场景
-
使用 FBX Exporter 导出 FBX
-
使用 FBX2glTF 将 FBX 转换为 glTF
-
使用 Three.js 加载 glTF
你可以在 Three.js 中直接使用从 Unity 导出的 FBX,但是 glTF 比 FBX 更容易处理,所以这里我将它转换为 glTF。
本文使用的Unity和three.js的版本分别是:
-
Unity: 2021.2.10f1
-
Three.js: r139
在 Unity 中创建场景
首先,在 Unity 中创建要导出的场景。这次我们将放置多个网格和相机、动画相机,将导出目标合并为一个游戏对象 ( Root)。
相机动画看起来像这样:
使用 FBX Exporter 导出 FBX
在 Unity 中安装 FBX 导出器。FBX 导出器驻留在包管理器的 Unity 注册表中。
About FBX Exporter
安装 FBX Exporter 后,选择要导出的游戏对象Menu > GameObject > Export to FBX...并单击(或右键单击游戏对象并Export to FBX...)单击Export Options显示,设置如下,并按下Export按钮将FBX导出到指定路径。
使用 FBX2glTF 将 FBX 转换为 glTF
使用 FBX2glTF 将从 Unity 导出的 FBX 转换为 glTF。
https://github.com
在Mac平台,使用终端命令来转换为 glTF,如下所示。FBX2glTF 有很多选项,例如 droco 压缩,但这次我将简单地将其转换为二进制格式(GLB)。执行时会在同一目录下生成UnityToThreejs.glb。
$ chmod +x ./FBX2glTF-darwin-x64 $ ./FBX2glTF-darwin-x64 --binary ./UnityToThreejs.fbx
使用 Three.js 加载 glTF
使用 Three.js 加载创建的 glTF。使用 GLTFLoader 进行加载。
<!DOCTYPE html> <html lang="en"> <head> <title>Unity to Three.js</title> <style> body { margin: 0; overflow: hidden; } </style> </head> <body> //使用three.js库 <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 实现的方案。由于照明和材质的不同,无法渲染出完全相同的效果,但这里尽可能以相同的方式安排模型和相机运动。
文献参考