如何使用 Three.js 和 React 渲染你自己的 3D 模型
本文将介绍如何在 React 项目中使用 react-three-fiber 渲染和配置在 Blender 或 Maya 等 3D 软件程序中创建的 3D 资源。读完本文后,您将能够在自己的网站上渲染 3D 模型 (gltf / glb)。
获取您自己的 3D 模型
为了获得定制的 3D 模型,我们将使用Ready Player Me,这是一款来自 Wolf3D 的免费 3D 头像创建工具,任何人都可以在几分钟内创建自己的数字形象,无需任何 3D 建模经验。您只需快速自拍一张,然后等待程序根据您的喜好自动生成定制的 3D 头像即可。
然后,您可以自由地使用各种发型、肤色、面部特征、服装选项和其他可自定义的属性来对角色进行调整。
登录Ready Player Me后,您需要按照以下步骤操作即可。
选择体型
上传你自己的照片
定制您的外观
下载你的模型
在 React 中渲染模型
为了在我们的 React 应用程序中渲染模型,我们将使用React-three-fiber,一个用于 Threejs 的React 渲染器。
设置项目
首先,让我们使用 Create React App创建一个新的React项目:
npx create-react-app my-3d-model
#or
yarn create react-app my-3d-model
然后,使用以下命令安装@react-three/fiber和@react-three/drei :
npm install three @react-three/fiber @react-three/drei
#or
yarn add three @react-three/fiber @react-three/drei
将模型转换为可重用的 React 组件
完成后,继续运行以下命令,使用gltfjsx创建一个 javascript 文件,该文件以 React 功能组件的格式绘制出所有资产内容。
npx gltfjsx model.glb
该文件的内容类似于以下代码:
import React, { useRef } from "react";
import { useGLTF } from "@react-three/drei";
export default function Model({ ...props }) {
const group = useRef();
const { nodes, materials } = useGLTF("/model.glb");
return (
<group ref={group} {...props} dispose={null}>
<primitive object={nodes.Hips} />
<skinnedMesh
geometry={nodes.Wolf3D_Body.geometry}
material={materials.Wolf3D_Body}
skeleton={nodes.Wolf3D_Body.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Glasses.geometry}
material={materials.Wolf3D_Glasses}
skeleton={nodes.Wolf3D_Glasses.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Hair.geometry}
material={materials.Wolf3D_Hair}
skeleton={nodes.Wolf3D_Hair.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Bottom.geometry}
material={materials.Wolf3D_Outfit_Bottom}
skeleton={nodes.Wolf3D_Outfit_Bottom.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Footwear.geometry}
material={materials.Wolf3D_Outfit_Footwear}
skeleton={nodes.Wolf3D_Outfit_Footwear.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Top.geometry}
material={materials.Wolf3D_Outfit_Top}
skeleton={nodes.Wolf3D_Outfit_Top.skeleton}
/>
<skinnedMesh
name="EyeLeft"
geometry={nodes.EyeLeft.geometry}
material={nodes.EyeLeft.material}
skeleton={nodes.EyeLeft.skeleton}
morphTargetDictionary={nodes.EyeLeft.morphTargetDictionary}
morphTargetInfluences={nodes.EyeLeft.morphTargetInfluences}
/>
<skinnedMesh
name="EyeRight"
geometry={nodes.EyeRight.geometry}
material={nodes.EyeRight.material}
skeleton={nodes.EyeRight.skeleton}
morphTargetDictionary={nodes.EyeRight.morphTargetDictionary}
morphTargetInfluences={nodes.EyeRight.morphTargetInfluences}
/>
<skinnedMesh
name="Wolf3D_Head"
geometry={nodes.Wolf3D_Head.geometry}
material={materials.Wolf3D_Skin}
skeleton={nodes.Wolf3D_Head.skeleton}
morphTargetDictionary={nodes.Wolf3D_Head.morphTargetDictionary}
morphTargetInfluences={nodes.Wolf3D_Head.morphTargetInfluences}
/>
<skinnedMesh
name="Wolf3D_Teeth"
geometry={nodes.Wolf3D_Teeth.geometry}
material={materials.Wolf3D_Teeth}
skeleton={nodes.Wolf3D_Teeth.skeleton}
morphTargetDictionary={nodes.Wolf3D_Teeth.morphTargetDictionary}
morphTargetInfluences={nodes.Wolf3D_Teeth.morphTargetInfluences}
/>
</group>
);
}
useGLTF.preload("/model.glb");
创造场景
import React, { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
export default function App() {
return (
<Canvas
camera={{ position: [2, 0, 12.25], fov: 15 }}
style={{
backgroundColor: "#111a21",
width: "100vw",
height: "100vh",
}}
>
<ambientLight intensity={1.25} />
<ambientLight intensity={0.1} />
<directionalLight intensity={0.4} />
<Suspense fallback={null}>// your model here</Suspense>
<OrbitControls />
</Canvas>
);
}
将模型添加到场景
首先将模型(glb文件)添加到公共文件夹,对于gltfjsx生成的javascript文件,您可以将其添加到src文件夹或components文件夹。
import React, { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import Model from "./Model"; /* highlight-line */
export default function App() {
return (
<Canvas
camera={{ position: [2, 0, 12.25], fov: 15 }}
style={{
backgroundColor: "#111a21",
width: "100vw",
height: "100vh",
}}
>
<ambientLight intensity={1.25} />
<ambientLight intensity={0.1} />
<directionalLight intensity={0.4} />
<Suspense fallback={null}>
<Model position={[0.025, -0.9, 0]} /> /* highlight-line */
</Suspense>
<OrbitControls />
</Canvas>
);
}
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
结果:
https://codesandbox.io/s/bold-wing-9w9n3i?file=/src/Model.js
为模型添加动画
为了能够向您的 3D 模型添加动画,您需要在您的机器上安装搅拌机。
将模型导入到搅拌机
Blender 是一款免费开源的 3D 创作套件。它支持完整的 3D 流程建模、绑定、动画、模拟、渲染、合成和运动追踪,甚至视频编辑和游戏创作。
了解更多
创建新的搅拌机项目
清除场景中的所有物体
将 glb 文件导入到 blender
选择您的型号并点击Import glTF 2.0
将模型转换为fbx格式
在向我们的模型添加任何动画之前,我们需要先将其转换为FBX格式。
选择型号
要在搅拌机中选择 3D 模型,您只需单击字母a
或使用鼠标即可。
将模型导出为 FBX
确保设置Path Mode
为Copy
,并选中该Embed textures
选项。
使用mixamo添加动画
Mixamo是一款免费的在线服务,用于自动绑定和制作 3D 角色动画。它由 Mixamo Incorporated 开发,该公司于 2015 年被 Adobe 收购。Mixamo 允许用户上传 FBX、OBJ 或 Zip 文件,然后该网站会在两分钟内自动完成角色绑定。绑定流程最适合人形角色。
将模型上传至 mixamo
选择动画并下载动画模型
将动画模型转换回 glb 格式
为了在我们的 React 应用程序中使用该模型,我们需要将其更改回glb格式。
将动画模型导入到搅拌机
将动画模型导出为 glb
在 React 中渲染动画模型
在公共文件夹model.glb
中用动画模型替换文件,并将以下更改添加到src/Model.js
文件中。
import React, { useRef, useEffect } from "react"; /* highlight-line */
import { useGLTF, useAnimations } from "@react-three/drei"; /* highlight-line */
export default function Model({ ...props }) {
const group = useRef();
const { nodes, materials, animations } = useGLTF("/model.glb");
const { actions } = useAnimations(animations, group); /* highlight-line */
// 'Armature|mixamo.com|Layer0' is the name of the animation we need to run.
// console.log(actions);
useEffect(() => {
/* highlight-line */
actions["Armature|mixamo.com|Layer0"].play(); /* highlight-line */
}); /* highlight-line */
return (
<group ref={group} {...props} dispose={null}>
<primitive object={nodes.Hips} />
<skinnedMesh
geometry={nodes.Wolf3D_Body.geometry}
material={materials.Wolf3D_Body}
skeleton={nodes.Wolf3D_Body.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Glasses.geometry}
material={materials.Wolf3D_Glasses}
skeleton={nodes.Wolf3D_Glasses.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Hair.geometry}
material={materials.Wolf3D_Hair}
skeleton={nodes.Wolf3D_Hair.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Bottom.geometry}
material={materials.Wolf3D_Outfit_Bottom}
skeleton={nodes.Wolf3D_Outfit_Bottom.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Footwear.geometry}
material={materials.Wolf3D_Outfit_Footwear}
skeleton={nodes.Wolf3D_Outfit_Footwear.skeleton}
/>
<skinnedMesh
geometry={nodes.Wolf3D_Outfit_Top.geometry}
material={materials.Wolf3D_Outfit_Top}
skeleton={nodes.Wolf3D_Outfit_Top.skeleton}
/>
<skinnedMesh
name="EyeLeft"
geometry={nodes.EyeLeft.geometry}
material={nodes.EyeLeft.material}
skeleton={nodes.EyeLeft.skeleton}
morphTargetDictionary={nodes.EyeLeft.morphTargetDictionary}
morphTargetInfluences={nodes.EyeLeft.morphTargetInfluences}
/>
<skinnedMesh
name="EyeRight"
geometry={nodes.EyeRight.geometry}
material={nodes.EyeRight.material}
skeleton={nodes.EyeRight.skeleton}
morphTargetDictionary={nodes.EyeRight.morphTargetDictionary}
morphTargetInfluences={nodes.EyeRight.morphTargetInfluences}
/>
<skinnedMesh
name="Wolf3D_Head"
geometry={nodes.Wolf3D_Head.geometry}
material={materials.Wolf3D_Skin}
skeleton={nodes.Wolf3D_Head.skeleton}
morphTargetDictionary={nodes.Wolf3D_Head.morphTargetDictionary}
morphTargetInfluences={nodes.Wolf3D_Head.morphTargetInfluences}
/>
<skinnedMesh
name="Wolf3D_Teeth"
geometry={nodes.Wolf3D_Teeth.geometry}
material={materials.Wolf3D_Teeth}
skeleton={nodes.Wolf3D_Teeth.skeleton}
morphTargetDictionary={nodes.Wolf3D_Teeth.morphTargetDictionary}
morphTargetInfluences={nodes.Wolf3D_Teeth.morphTargetInfluences}
/>
</group>
);
}
useGLTF.preload("/model.glb");
结果:
https://codesandbox.io/s/3d-model-animation-d41e9u?file=/src/Model.js:271-281
另请阅读:
React 最佳实践 - 2022 年编写更优质代码的方法
10 个 JavaScript 单行代码 - 提升生产力的方法
我的博客
我的网站
在 Upwork 上找到
我 在 twitter 上找到
我 在 linkedin 上找到我
在 github 上找到我