Three.js机器人与星系动态场景(三):如何实现动画

news/2024/7/8 1:41:06 标签: javascript, 机器人, 开发语言

在前面的博客中分别介绍了如何快速搭建3D交互场景以及通过坐标辅助工具加深对坐标系的理解。本文将继续探讨其中动画实现的细节。通过调整rotation加深对动画的印象。

Three.js机器人与星系动态场景:实现3D渲染与交互式控制-CSDN博客 

Three.js机器人与星系动态场景(二):强化三维空间认识-CSDN博客

 动画说白了就是一张张照片,连起来依次展示,这样就形成一个动画效果,只要帧率高,人的眼睛就感觉不到卡顿,是连续的视频效果。

 实现上述效果只需要添加如下动画,在动画中给物体旋转增加一个正向或负向的增量,在方法中调用自身达到一个循环

javascript">
  const animate = () => {
    requestAnimationFrame(animate);
    robot.rotation.y -= 0.005; //机器人旋转
    robot2.rotation.y -= 0.005;
    // 粒子旋转
    starts.rotation.y -= 0.001;
    starts.rotation.z += 0.001;
    starts.rotation.x += 0.001;
    renderer.render(scene, camera);
  };
  animate(); //添加动画

requestAnimationFrame 动画循环

requestAnimationFrame 是一个循环机制,它会在浏览器准备好绘制下一帧时重复调用提供的函数。这样,您可以创建一个平滑且连续的动画效果。在 animate 函数内部,requestAnimationFrame(animate) 被调用,这确保了 animate 函数会在每一帧都被执行。这是创建无限动画循环的关键。

  1. 浏览器优化:当使用 requestAnimationFrame 时,浏览器会优化动画过程,减少页面闪烁和重绘,确保动画流畅运行。它会根据显示器的刷新率(通常是 60Hz)来调用动画函数,但实际的调用次数可能会根据电脑的性能和浏览器的工作负载动态调整。

  2. 时间间隔requestAnimationFrame 不接受任何时间间隔参数,它会在浏览器认为合适的时候调用函数,这通常与屏幕的刷新率同步。与 setInterval 或 setTimeout 不同,您不需要担心设置一个固定的时间间隔,这有助于避免动画在不同设备上出现不同步的问题。

  3. 暂停和恢复:当用户切换到另一个标签或最小化浏览器窗口时,requestAnimationFrame 会自动暂停,这有助于节省 CPU 和 GPU 资源。当用户返回到页面时,动画会自动恢复。

rotation旋转

rotation旋转,在前面的示例中机器人围绕y轴逆时针旋转。代码如下:

javascript">  const animate = () => {
    requestAnimationFrame(animate);
    robot.rotation.y -= 0.005; //机器人旋转
    robot2.rotation.y -= 0.005;
    ...

    renderer.render(scene, camera);
  };

通过rotation属性给robot.rotation.y一个负值,每帧robot绕着y轴逆时针旋转0.005个弧度单位。通过requestAnimationFrame进行动画循环,不断的帧连续播放就形成了动画的效果 

X轴旋转 

让处在原点的机器人让x轴转

javascript">  const animate = () => {
    requestAnimationFrame(animate);
    robot.rotation.x -= 0.005; //原点机器人旋转
    robot2.rotation.y -= 0.005;
    renderer.render(scene, camera);
  };

Z轴旋转 

绕z轴旋转,这种感觉演我们上班的状态,要死不活

javascript">  const animate = () => {
    requestAnimationFrame(animate);
    robot.rotation.z -= 0.005; //机器人旋转
    robot2.rotation.y -= 0.005;
    ...
    renderer.render(scene, camera);
  };

 完整代码

javascript">import { useEffect, useRef } from "react";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
//机器人脑袋
function createHead() {
  //SphereGeometry创建球形几何体
  const head = new THREE.SphereGeometry(4, 32, 16, 0, Math.PI * 2, 0, Math.PI * 0.5);
  const headMaterial = new THREE.MeshStandardMaterial({
    color: 0x43b988,
    roughness: 0.5,
    metalness: 1.0,
  });
  const headMesh = new THREE.Mesh(head, headMaterial);
  return headMesh;
}
//触角
function generateHorn(y: number, z: number, angle: number) {
  //触角 CapsuleGeometry 创建胶囊形状的几何体。胶囊形状可以看作是一个圆柱体两端加上半球体
  const line = new THREE.CapsuleGeometry(0.1, 2);
  const lineMaterial = new THREE.MeshStandardMaterial({
    color: 0x43b988,
    roughness: 0.5,
    metalness: 1.0,
  });
  const lineMesh = new THREE.Mesh(line, lineMaterial);
  lineMesh.position.y = y;
  lineMesh.position.z = z;
  lineMesh.rotation.x = angle;
  return lineMesh;
}
//机器人眼睛
function generateEye(x: number, y: number, z: number) {
  //SphereGeometry创建球形几何体
  const eye = new THREE.SphereGeometry(0.5, 32, 16, 0, Math.PI * 2, 0, Math.PI * 2);
  const eyeMaterial = new THREE.MeshStandardMaterial({
    color: 0x212121,
    roughness: 0.5,
    metalness: 1.0,
  });
  const eyeMesh = new THREE.Mesh(eye, eyeMaterial);
  eyeMesh.position.x = x;
  eyeMesh.position.y = y;
  eyeMesh.position.z = z;
  return eyeMesh;
}
//机器人身体
function generateBody() {
  //CylinderGeometry第一个参数是上部分圆的半径,第二个参数是下部分圆的半径,第三个参数是高度,材质使用的跟腿一样
  const body = new THREE.CylinderGeometry(4, 4, 6);
  const bodyMaterial = new THREE.MeshStandardMaterial({
    color: 0x43b988,
    roughness: 0.5,
    metalness: 1.0,
  });
  const bodyMesh = new THREE.Mesh(body, bodyMaterial);
  return bodyMesh;
}
//胳膊、腿
function generateLegs(y: number, z: number) {
  const leg1 = new THREE.CapsuleGeometry(1, 4);
  const legMaterial1 = new THREE.MeshStandardMaterial({
    color: 0x43b988,
    roughness: 0.5,
    metalness: 1.0,
  });
  const leg1Mesh = new THREE.Mesh(leg1, legMaterial1);
  leg1Mesh.position.y = y;
  leg1Mesh.position.z = z;
  return leg1Mesh;
}
//创建机器人
function generateRobot() {
  // 创建一个Three.js对象,用于存放机器人
  const robot = new THREE.Object3D();
  const headMesh = createHead();
  headMesh.position.y = 6.5;
  robot.add(headMesh);
  //眼睛
  const leftEye = generateEye(3, 8, -2);
  const rightEye = generateEye(3, 8, 2);
  robot.add(leftEye);
  robot.add(rightEye);
  const leftHorn = generateHorn(11, -1, (-Math.PI * 30) / 180);
  const rightHorn = generateHorn(11, 1, (Math.PI * 30) / 180);
  robot.add(leftHorn);
  robot.add(rightHorn);
  const body = generateBody();
  body.position.y = 4;
  robot.add(body);

  // 生成机器人左腿
  robot.add(generateLegs(0, -2));
  // 生成机器人右腿
  robot.add(generateLegs(0, 2));
  //胳膊
  robot.add(generateLegs(3, 5));

  robot.add(generateLegs(3, -5));
  //物体缩放
  robot.scale.x = 0.3;
  robot.scale.y = 0.3;
  robot.scale.z = 0.3;
  return robot;
}
//创建粒子星星
function generateStarts(num: number) {
  //制作粒子特效
  const starts = new THREE.Object3D();
  const obj = new THREE.SphereGeometry(0.2, 3, 3);
  const material = new THREE.MeshStandardMaterial({
    color: 0x43b988,
    roughness: 0.5,
    metalness: 5,
  });
  const mesh = new THREE.Mesh(obj, material);
  for (let i = 0; i < num; i++) {
    const target = new THREE.Mesh();
    target.copy(mesh);
    target.position.x = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
    target.position.y = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
    target.position.z = Math.floor(Math.random() * 18 + Math.floor(Math.random() * -18));
    starts.add(target);
  }
  return starts;
}
//创建文本
function createText(content: string, font: any) {
  const textGeometry = new TextGeometry(content, {
    font: font,
    size: 1,
    height: 0.1,
    curveSegments: 1,
  });
  textGeometry.center();
  const textMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }); // front
  const mesh = new THREE.Mesh(textGeometry, textMaterial);
  return mesh;
}
/**
 * 创建一个Three.js场景,包括相机和渲染器
 */
function Robot() {
  // 创建一个div容器,用于存放渲染的Three.js场景
  const containerRef = useRef<HTMLDivElement>(null);
  const scene = new THREE.Scene();
  // 创建一个Three.js相机,包括透视投影、宽高比、近裁剪面和远裁剪面
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(15, 12, 8);
  camera.lookAt(0, 0, 0);
  // 创建一个Three.js渲染器,包括抗锯齿
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);

  //添加坐标系
  const axisHelper = new THREE.AxesHelper(150);
  scene.add(axisHelper);
  //坐标系添加文字
  const loader = new FontLoader();
  let meshX = new THREE.Mesh();
  let meshY = new THREE.Mesh();
  let meshZ = new THREE.Mesh();
  loader.load("fonts/optimer_regular.typeface.json", function (font) {
    meshX = createText("X", font);
    meshY = createText("Y", font);
    meshZ = createText("Z", font);
    meshX.position.x = 12;
    meshY.position.y = 12;
    meshZ.position.z = 12;
    scene.add(meshX);
    scene.add(meshY);
    scene.add(meshZ);
  });

  const robot = generateRobot();
  const robot2 = generateRobot();
  robot2.position.x = 6;
  robot2.position.z = 6;
  // 将机器人身体添加到场景中
  scene.add(robot);
  scene.add(robot2);
  // 创建一个Three.js方向光,包括颜色、强度
  const straightLight = new THREE.DirectionalLight(0xffffff, 5);
  // 设置方向光的位置
  straightLight.position.set(20, 20, 20);
  // 将方向光添加到场景中
  scene.add(straightLight);

  const starts = generateStarts(200);
  scene.add(starts);

  //轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.update();

  const animate = () => {
    requestAnimationFrame(animate);
    robot.rotation.z -= 0.005; //机器人旋转
    robot2.rotation.y -= 0.005;
    // 粒子旋转
    starts.rotation.y -= 0.001;
    starts.rotation.z += 0.001;
    starts.rotation.x += 0.001;
    //
    meshX.lookAt(camera.position);
    meshY.lookAt(camera.position);
    meshZ.lookAt(camera.position);
    renderer.render(scene, camera);
  };
  animate(); //添加动画

  // 监听组件挂载和卸载
  useEffect(() => {
    // 如果div存在,将渲染器dom元素添加到div中
    if (containerRef.current) {
      containerRef.current.appendChild(renderer.domElement);
      // 渲染场景
      renderer.render(scene, camera);
    }
  }, [containerRef]);

  // 返回div容器,用于存放渲染的Three.js场景
  return <div ref={containerRef} style={{ width: "100vw", height: "100vh" }}></div>;
}

// 导出Robot组件
export default Robot;


http://www.niftyadmin.cn/n/5535941.html

相关文章

【Python机器学习】模型评估与改进——二分类指标

目录 1、错误类型 2、不平衡数据集 3、混淆矩阵 与精度的关系。 准确率、召回率与f-分数 分类报告 4、考虑不确定性 5、准确率-召回率曲线 6、受试者工作特征&#xff08;ROC&#xff09;与AUC 二分类可能是实践中最常见的机器学习应用&#xff0c;也是概念最简单的应…

c++习题04-忙碌的工人

目录 一&#xff0c;问题 二&#xff0c;思路 1&#xff0c;图形 2&#xff0c;分析 3&#xff0c;伪代码 三&#xff0c;代码 一&#xff0c;问题 二&#xff0c;思路 1&#xff0c;图形 根据题目&#xff0c;绘制出来的图形如下&#x1f447; 之后再绘制甲经过楼梯…

华为HCIP Datacom H12-821 卷26

1.单选题 在VRRP中&#xff0c;同一备份组的设备在进行VRRP报文认证时&#xff0c;以下哪一参数不会影响Master设备和Backup设备认证协商结果 A、认证字 B、优先级 C、认证方式 D、VRRP版本 正确答案&#xff1a; B 解析&#xff1a; 优先级只会影响谁是主谁是备&…

vscode 工程中 c_cpp_properties.json文件作用

在 Visual Studio Code&#xff08;VSCode&#xff09;开发C或C项目时&#xff0c;c_cpp_properties.json 文件是一个非常重要的配置文件&#xff0c;主要由微软提供的 C/C 扩展&#xff08;C/C extension from Microsoft&#xff09;使用。它主要用于配置 IntelliSense&#x…

2024亚太杯中文赛数学建模选题建议及各题思路来啦!

大家好呀&#xff0c;2024年第十四届APMCM亚太地区大学生数学建模竞赛&#xff08;中文赛项&#xff09;开始了&#xff0c;来说一下初步的选题建议吧&#xff1a; 首先定下主基调&#xff0c; 本次亚太杯推荐大家选择B题目。C题目难度较高&#xff0c;只建议用过kaiwu的队伍…

构建大数据生态:Sqoop、Hadoop、IDEA和Maven的完整安装与数据预处理指南【实训Day03】

一、Sqoop安装 1 上传安装包并解压缩(在hadoop101上) # cd /opt/software 点击xftp上传sqoop的安装文件sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz # tar -zxvf sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz -C /opt/module/ # cd /opt/module/ # mv s…

数据结构 —— 图的遍历

数据结构 —— 图的遍历 BFS&#xff08;广度遍历&#xff09;一道美团题DFS&#xff08;深度遍历&#xff09; 我们今天来看图的遍历&#xff0c;其实都是之前在二叉树中提过的方法&#xff0c;深度和广度遍历。 在这之前&#xff0c;我们先用一个邻接矩阵来表示一个图&#…

针对某客户报表系统数据库跑批慢进行性能分析及优化

某客户报表系统数据库跑批时间过长&#xff0c;超出源主库较多&#xff0c;故对其进行了分析调优&#xff0c;目前状态如下&#xff1a; 1、业务连接的rac的scanip&#xff0c;因为负载均衡将跑批的连接连接到了多个计算节点导致节点间通讯成本较高&#xff0c;故速率缓慢&…