React + three.js 3D模型面部表情控制

系列文章目录

  1. React 使用 three.js 加载 gltf 3D模型 | three.js 入门
  2. React + three.js 3D模型骨骼绑定
  3. React + three.js 3D模型面部表情控制

示例项目(github):https://github.com/couchette/simple-react-three-facial-expression-demo
示例项目(gitcode):https://gitcode.com/qq_41456316/simple-react-three-facial-expression-demo

文章目录

  • 系列文章目录
  • 前言
  • 一、实现步骤
    • 1、创建项目配置环境
    • 2. 创建组件
    • 3. 使用组件
    • 4. 运行项目
  • 总结


前言

在本系列的上一篇文章中,我们已经探讨了如何在 React 中利用 three.js 来渲染 3D 模型,现在,我们将深入研究如何运用 three.js 控制这些模型的表情。让我们一同探索如何赋予你的 3D 模型更加生动和丰富的表情吧!


一、实现步骤

1、创建项目配置环境

使用 create-reacte-app 创建项目

npx create-react-app simple-react-three-facial-expression-demo
cd simple-react-three-facial-expression-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 <div ref={containerRef} />;
}

export default ThreeContainer;

接着将three.js自动创建渲染元素添加到return组件中为子元素(可见container.appendChild(renderer.domElement);),相关逻辑代码在useEffect中执行,完整代码内容如下

import * as THREE from "three";

import WebGPU from "three/addons/capabilities/WebGPU.js";
import WebGL from "three/addons/capabilities/WebGL.js";

import WebGPURenderer from "three/addons/renderers/webgpu/WebGPURenderer.js";

import Stats from "three/addons/libs/stats.module.js";

import { OrbitControls } from "three/addons/controls/OrbitControls.js";

import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
import { MeshoptDecoder } from "three/addons/libs/meshopt_decoder.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;
      init();
    }

    async function init() {
      if (
        WebGPU.isAvailable() === false &&
        WebGL.isWebGL2Available() === false
      ) {
        containerRef.current.appendChild(WebGPU.getErrorMessage());

        throw new Error("No WebGPU or WebGL2 support");
      }

      let mixer;

      const clock = new THREE.Clock();

      const container = containerRef.current;

      const camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        1,
        20
      );
      camera.position.set(-1.8, 0.8, 3);

      const scene = new THREE.Scene();
      scene.add(new THREE.HemisphereLight(0xffffff, 0x443333, 2));

      const renderer = new WebGPURenderer({ antialias: true });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.setAnimationLoop(animate);

      container.appendChild(renderer.domElement);

      const ktx2Loader = await new KTX2Loader()
        .setTranscoderPath("/basis/")
        .detectSupportAsync(renderer);

      new GLTFLoader()
        .setKTX2Loader(ktx2Loader)
        .setMeshoptDecoder(MeshoptDecoder)
        .load("models/facecap.glb", (gltf) => {
          const mesh = gltf.scene.children[0];

          scene.add(mesh);

          mixer = new THREE.AnimationMixer(mesh);

          mixer.clipAction(gltf.animations[0]).play();

          // GUI

          const head = mesh.getObjectByName("mesh_2");
          const influences = head.morphTargetInfluences;

          //head.morphTargetInfluences = null;

          // WebGPURenderer: Unsupported texture format. 33776
          head.material.map = null;

          const gui = new GUI();
          gui.close();

          for (const [key, value] of Object.entries(
            head.morphTargetDictionary
          )) {
            gui
              .add(influences, value, 0, 1, 0.01)
              .name(key.replace("blendShape1.", ""))
              .listen();
          }
        });

      scene.background = new THREE.Color(0x666666);

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.minDistance = 2.5;
      controls.maxDistance = 5;
      controls.minAzimuthAngle = -Math.PI / 2;
      controls.maxAzimuthAngle = Math.PI / 2;
      controls.maxPolarAngle = Math.PI / 1.8;
      controls.target.set(0, 0.15, -0.2);

      const stats = new Stats();
      container.appendChild(stats.dom);

      function animate() {
        const delta = clock.getDelta();

        if (mixer) {
          mixer.update(delta);
        }

        renderer.render(scene, camera);

        controls.update();

        stats.update();
      }

      window.addEventListener("resize", () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);
      });
    }
  }, []);
  return <div ref={containerRef} />;
}

export default ThreeContainer;

3. 使用组件

修改App.js的内容如下

import "./App.css";
import ThreeContainer from "./components/ThreeContainer";

function App() {
  return (
    <div>
      <ThreeContainer />
    </div>
  );
}

export default App;


4. 运行项目

运行项目npm start最终效果如下
请添加图片描述


总结

通过本文的介绍,相信读者对于在 React 中实现 3D 模型表情控制有了初步的了解。如果你对此感兴趣,不妨动手尝试一下,可能会有意想不到的收获。同时,也欢迎大家多多探索,将 React 和 Three.js 的强大功能发挥到极致,为网页应用增添更多的乐趣和惊喜。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/535411.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux网络名称空间和cgroup的关系

在Linux系统中&#xff0c;网络名称空间&#xff08;Network Namespaces&#xff09;和控制组&#xff08;cgroups&#xff09;是两种重要的资源管理和隔离技术。虽然它们在功能和应用场景上有所不同&#xff0c;但二者共同为Linux容器技术&#xff0c;如Docker和Kubernetes&am…

编程规范(保姆级教程)

文章目录 为什么需要编程规范&#xff1f;&#x1f4a1;代码检测工具 ESLint&#x1f4a1;代码格式化 Prettier&#x1f4a1;ESLint 与 Prettier 配合解决代码格式问题eslint支持ts约定式提交规范Commitizen助你规范化提交代码什么是 Git Hooks使用 husky commitlint 检查提交…

wsl初步使用记录

wsl介绍 WSL是windows平台下Linux环境的子系统&#xff08;Windows Subsyetem for Linux&#xff09;&#xff0c;可以让Windows下方便的安装Linux系统&#xff0c;而无需安装其他虚拟机软件。 wsl使用 Windows操作系统支持 Windows 10 版本 2004 及更高版本&#xff08;内…

OpenHarmony南向开发案例:【智能保险柜】

样例简介 智能保险柜实时监测保险柜中振动传感器&#xff0c;当有振动产生时及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程接收智能保险柜的报警信息。后续可扩展摄像头等设备&#xff0c;实现对危险及时报警&#xff0c;及时处理&a…

缝合的作品(并查集/逆序)

、思路&#xff1a;首先是并查集来做&#xff0c;首先给给每个单词一个id&#xff0c;然后把它放到ans[i]处。 对于操作1&#xff1a;把a单词换为单词b&#xff0c;就相当于a、b两个集合结合。然后再给a单词赋一个新的id&#xff0c;用来进行操作2&#xff0c;因为之后的操作2…

HarmonyOS实战开发-证书管理、如何实现对签名数据进行校验功能。

介绍 本示例使用了ohos.security.certManager相关接口实现了对签名数据进行校验的功能。 实现场景如下&#xff1a; 1&#xff09;使用正确的原始数据和签名数据进行签名校验场景&#xff1a;模拟服务端对签名数据进行校验&#xff0c;验证客户端身份和原始数据完整性。 2&…

车载摄像头图像及画质增强解决方案

车载摄像头作为汽车智能化、安全化的关键组件&#xff0c;其图像质量直接影响着驾驶者的视觉感知和行车安全。美摄科技凭借其在图像处理和AI算法领域的深厚积累&#xff0c;推出了一款专为车载摄像头打造的图像及画质增强解决方案&#xff0c;助力企业实现摄像头画面的实时优化…

基于“遥感+”蓝碳储量估算、红树林信息提取实践技术应用与科研论文写作

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

电商技术揭秘十九:电商平台的智能化与自动化技术

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

✔ ★Java项目——设计一个消息队列(二)

Java项目——设计一个消息队列 四. 项⽬创建五. 创建核⼼类创建 Exchange&#xff08;名字、类型、持久化&#xff09;创建 MSGQueue&#xff08;名字、持久化、独占标识&#xff09;创建 Binding&#xff08;交换机名字、队列名字、bindingKey用于与routingKey匹配&#xff09…

WPS的JS宏如何批量实现文字的超链接

表格中需要对文字进行超链接&#xff0c;每个链接指引到不同的地址。例如&#xff1a; 实现如下表格中&#xff0c;文件名称超级链接到对应的文件路径上&#xff0c;点击对应的文件名称&#xff0c;即可打开对应的文件。 序号文件名称文件路径1变更申请与处理表.xls文档\系统…

HTTPS证书是什么?申请方法是什么?

HTTPS证书是互联网上由权威证书颁发机构&#xff08;CA&#xff09;签发的数字文件&#xff0c;用于证明网站的身份&#xff0c;并通过其中包含的公钥为网站启用HTTPS加密连接&#xff0c;确保用户与网站间的通信数据安全且不可被第三方窃取或篡改。 怎么申请&#xff1f; 一&…

实验案例一:交换机的初始配置

1、实验环境 实验用具包括一台 Cisco 交换机&#xff0c;一台 PC&#xff0c;一根 Console 线缆。 2、需求描述 如图 5.17 所示&#xff0c;实验案例一的配置需求如下。 通过 PC 连接并配置一台 Cisco 交换机在交换机的各个配置模式之间切换将交换机主机的名称改为 BDON 3、…

文心一言 VS 讯飞星火 VS chatgpt (234)-- 算法导论17.2 2题

二、用核算法重做练习17.1-3。练习17.1-3的内容是&#xff1a;假定我们对一个数据结构执行一个由 n 个操作组成的操作序列&#xff0c;当 i 严格为 2 的幂时第 i 个操作的代价为 i &#xff0c;否则代价为1。使用聚合分析确定每个操作的摊还代价。 文心一言&#xff1a; 练习…

多线程的学习

多线程编辑&#xff1a; 可以简单理解进程是一个软件 而线程就是一个软件中多个可以同时运行的功能 实现多线程的第一种方式&#xff1a;使用Thead类我们再自己创造一个类继承于这个类我们在对Thead方法进行重写&#xff0c;注意我们再重写的时候一定要加上Override这行 我犯下…

19 文件接口

文件概念 文件指的是文件内容属性&#xff0c;对文件的操作无外乎就是对内容或者属性的操作 为什么平时不用文件接口 我们运行程序访问文件&#xff0c;本质是进程在访问文件&#xff0c;向硬件写入内容&#xff0c;只有操作系统有这个权限。普通用户想写入内容呢&#xff1…

【canvas】canvas基础使用(六):图形阴影

简言 学习使用canvas的阴影属性。 阴影 shadowBlur 阴影模糊 CanvasRenderingContext2D.shadowBlur 是 Canvas 2D API 描述模糊效果程度的属性&#xff1b;它既不对应像素值也不受当前转换矩阵的影响。默认值是 0。 语法&#xff1a; ctx.shadowBlur level; 选项值&#x…

【寒假集训营总结笔记——7道优质好题】

牛客寒假集训营总结笔记——7道优质好题 一、Trie树的应用&#xff1a; 题目链接&#xff1a;Tokitsukaze and Min-Max XOR 1、题意 2、题解 1、首先这道题的答案和元素本身的顺序是无关的&#xff0c;因为假如你选择了一些数字&#xff0c;它是默认必须排好序才能记作是答案…

瑞芯微RK3568/RK3588+鸿蒙,矿鸿工控屏、矿鸿工控板、矿鸿网关,推动矿业数智化变革

4月10日至12日&#xff0c;以“绿色智能创新&#xff0c;携手共赢未来”为主题的第二届中国国际矿业装备与技术展览会在西安举行。信迈科技携矿鸿解决方案及产品亮相&#xff0c;赋能矿山行业数智化升级和国产化改造进程全面提速。 作为华为矿山军团矿鸿生态使能合作伙伴&#…

【LeetCode刷题笔记】LeetCode 1365.有多少小于当前数字的数字

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…