React + three.js 3D模型骨骼绑定 | 您所在的位置:网站首页 › 3d模型怎么和骨骼绑定的视频 › React + three.js 3D模型骨骼绑定 |
系列文章目录
React 使用 three.js 加载 gltf 3D模型 | three.js 入门React + three.js 3D模型骨骼绑定React + three.js 3D模型面部表情控制React + three.js 实现人脸动捕与3D模型表情同步结合 react-webcam、three.js 与 electron 实现桌面人脸动捕应用
项目代码(github):https://github.com/couchette/simple-react-three-skeleton-demo 项目代码(gitcode):https://gitcode.com/qq_41456316/simple-react-three-skeleton-demo.git 文章目录 系列文章目录前言一、3D 模型骨骼绑定是什么?二、为什么选择 React 和 Three.js?三、如何在 React 中实现 3D 模型骨骼绑定?四、具体实现步骤1、创建项目配置环境2. 创建组件3. 使用组件4. 运行项目 结语程序预览 前言在当今互联网世界中,网页技术的发展已经超越了以往的想象。其中,三维图形技术在网页中的应用日益普遍,而 React 和 Three.js 正是其中的两个热门选择。本文将介绍如何将 Three.js 中的 3D 模型骨骼绑定示例迁移到 React 中,为读者提供一个简单易懂的入门指南。 一、3D 模型骨骼绑定是什么?在三维计算机图形学中,骨骼绑定是一种常用的技术,用于将一个三维模型与其骨骼系统相连接。这样的连接使得模型能够进行动画表现,仿佛具有生命一般。通过控制骨骼系统的姿势和运动,可以实现模型的各种动作,比如行走、跳跃、转身等。 二、为什么选择 React 和 Three.js?React 是一个流行的 JavaScript 库,用于构建用户界面。它的组件化和声明式的特性使得构建交互式 UI 变得更加简单。而 Three.js 则是一个用于创建 3D 图形的 JavaScript 库,它提供了丰富的功能和 API,使得在网页中展示三维模型变得轻而易举。 将这两者结合起来,可以让开发者在 React 的基础上轻松地添加复杂的三维图形功能,为用户提供更加丰富的交互体验。 三、如何在 React 中实现 3D 模型骨骼绑定?要在 React 中实现 3D 模型骨骼绑定,我们可以借助 Three.js 提供的示例来进行学习和实践。具体地,我们可以参考 Three.js 官方示例中的 webgl_animation_skinning_ik 示例,该示例展示了一个带有骨骼动画的人物模型,并且可以通过鼠标交互来控制模型的运动。 通过将这个示例移植到 React 中,我们可以使得代码更具可维护性和可扩展性,同时也能够让 React 开发者轻松地使用 Three.js 的功能。在移植过程中,我们需要注意将 Three.js 的相关代码封装成 React 组件,并正确处理 React 的生命周期以及状态管理等方面的问题。 四、具体实现步骤 1、创建项目配置环境使用 create-reacte-app 创建项目 npx create-react-app simple-react-three-skeleton-demo cd simple-react-three-skeleton-demo安装three.js npm i three 2. 创建组件在src目录创建components文件夹,在components文件夹下面创建ThreeContainer.js文件。 首先创建组件,并获取return 元素的ref import * as THREE from "three"; import { useRef, useEffect } from "react"; function ThreeContainer() { const containerRef = useRef(null); const isContainerRunning = useRef(false); return OrbitControls } from "three/addons/controls/OrbitControls.js"; import { TransformControls } from "three/addons/controls/TransformControls.js"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js"; import { CCDIKSolver, CCDIKHelper, } from "three/addons/animation/CCDIKSolver.js"; import Stats from "three/addons/libs/stats.module.js"; import { GUI } from "three/addons/libs/lil-gui.module.min.js"; import { useRef, useEffect } from "react"; function ThreeContainer() { const containerRef = useRef(null); const isContainerRunning = useRef(false); useEffect(() => { if (!isContainerRunning.current && containerRef.current) { isContainerRunning.current = true; let scene, camera, renderer, orbitControls, transformControls; let mirrorSphereCamera; const OOI = {}; let IKSolver; let stats, gui, conf; const v0 = new THREE.Vector3(); init().then(animate); async function init() { conf = { followSphere: false, turnHead: true, ik_solver: true, update: updateIK, }; scene = new THREE.Scene(); scene.fog = new THREE.FogExp2(0xffffff, 0.17); scene.background = new THREE.Color(0xffffff); camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.001, 5000 ); camera.position.set( 0.9728517749133652, 1.1044765132727201, 0.7316689528482836 ); camera.lookAt(scene.position); const ambientLight = new THREE.AmbientLight(0xffffff, 8); // soft white light scene.add(ambientLight); renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true, }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); containerRef.current.appendChild(renderer.domElement); stats = new Stats(); containerRef.current.appendChild(stats.dom); orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.minDistance = 0.2; orbitControls.maxDistance = 1.5; orbitControls.enableDamping = true; const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath("/draco/"); // dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/"); const gltfLoader = new GLTFLoader(); gltfLoader.setDRACOLoader(dracoLoader); const gltf = await gltfLoader.loadAsync("/models/kira.glb"); gltf.scene.traverse((n) => { if (n.name === "head") OOI.head = n; if (n.name === "lowerarm_l") OOI.lowerarm_l = n; if (n.name === "Upperarm_l") OOI.Upperarm_l = n; if (n.name === "hand_l") OOI.hand_l = n; if (n.name === "target_hand_l") OOI.target_hand_l = n; if (n.name === "boule") OOI.sphere = n; if (n.name === "Kira_Shirt_left") OOI.kira = n; }); scene.add(gltf.scene); orbitControls.target.copy(OOI.sphere.position); // orbit controls lookAt the sphere OOI.hand_l.attach(OOI.sphere); // mirror sphere cube-camera const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(1024); mirrorSphereCamera = new THREE.CubeCamera(0.05, 50, cubeRenderTarget); scene.add(mirrorSphereCamera); const mirrorSphereMaterial = new THREE.MeshBasicMaterial({ envMap: cubeRenderTarget.texture, }); OOI.sphere.material = mirrorSphereMaterial; transformControls = new TransformControls(camera, renderer.domElement); transformControls.size = 0.75; transformControls.showX = false; transformControls.space = "world"; transformControls.attach(OOI.target_hand_l); scene.add(transformControls); // disable orbitControls while using transformControls transformControls.addEventListener( "mouseDown", () => (orbitControls.enabled = false) ); transformControls.addEventListener( "mouseUp", () => (orbitControls.enabled = true) ); OOI.kira.add(OOI.kira.skeleton.bones[0]); const iks = [ { target: 22, // "target_hand_l" effector: 6, // "hand_l" links: [ { index: 5, // "lowerarm_l" rotationMin: new THREE.Vector3(1.2, -1.8, -0.4), rotationMax: new THREE.Vector3(1.7, -1.1, 0.3), }, { index: 4, // "Upperarm_l" rotationMin: new THREE.Vector3(0.1, -0.7, -1.8), rotationMax: new THREE.Vector3(1.1, 0, -1.4), }, ], }, ]; IKSolver = new CCDIKSolver(OOI.kira, iks); const ccdikhelper = new CCDIKHelper(OOI.kira, iks, 0.01); scene.add(ccdikhelper); gui = new GUI(); gui.add(conf, "followSphere").name("follow sphere"); gui.add(conf, "turnHead").name("turn head"); gui.add(conf, "ik_solver").name("IK auto update"); gui.add(conf, "update").name("IK manual update()"); gui.open(); window.addEventListener("resize", onWindowResize, false); } function animate() { if (OOI.sphere && mirrorSphereCamera) { OOI.sphere.visible = false; OOI.sphere.getWorldPosition(mirrorSphereCamera.position); mirrorSphereCamera.update(renderer, scene); OOI.sphere.visible = true; } if (OOI.sphere && conf.followSphere) { // orbitControls follows the sphere OOI.sphere.getWorldPosition(v0); orbitControls.target.lerp(v0, 0.1); } if (OOI.head && OOI.sphere && conf.turnHead) { // turn head OOI.sphere.getWorldPosition(v0); OOI.head.lookAt(v0); OOI.head.rotation.set( OOI.head.rotation.x, OOI.head.rotation.y + Math.PI, OOI.head.rotation.z ); } if (conf.ik_solver) { updateIK(); } orbitControls.update(); renderer.render(scene, camera); stats.update(); // fps stats requestAnimationFrame(animate); } function updateIK() { if (IKSolver) IKSolver.update(); scene.traverse(function (object) { if (object.isSkinnedMesh) object.computeBoundingSphere(); }); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } } }, []); return return ( ); } export default App; 4. 运行项目运行项目 npm start最终效果如下 通过本文的介绍,相信读者对于在 React 中实现 3D 模型骨骼绑定有了初步的了解。如果你对此感兴趣,不妨动手尝试一下,可能会有意想不到的收获。同时,也欢迎大家多多探索,将 React 和 Three.js 的强大功能发挥到极致,为网页应用增添更多的乐趣和惊喜。 程序预览 |
CopyRight 2018-2019 实验室设备网 版权所有 |