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

内容摘要:使用Three.js库构建了一个交互式的3D场景。组件中创建了一个机器人模型,包括头部、眼睛、触角、身体和四肢,以及两个相同的机器人实例以实现动态效果。场景中还加入了粒子效果,模拟星系环境,增强了视觉效果。通过OrbitControls,用户可以对机器人进行旋转控制。组件在渲染时会根据用户界面的变化动态调整渲染,并在指定的div容器中显示。整体上,这个组件提供了一个基础的3D动画展示和用户交互的框架。

  1. 在React中集成Three.js库,创建一个动态渲染的3D场景。
  2. 定义和渲染多个Three.js几何体,如机器人身体、胳膊、腿、眼睛和触角。
  3. 添加交互性,如旋转和轨道控制器,以提供更好的用户体验。
  4. 创建粒子特效,模拟星系环境,与机器人形成对比。

 一、项目搭建react+three.js

实现这样的效果需要安装three.js包;至于使用vue还是react框架都行,因为three.js只需要一个div作为挂载点即可。也用不到框架的细节。本文以react为例实现。

 依次执行以下命令,完成react项目的初始化和threejs的安装

 

 

良好的编码习惯要求我们,在views里新增一个robot文件夹,根据react组件的特效,用.tsx后缀表示是组件。定义一个方法名为Robot的function并将其默认导出。react的特点,函数式组件。方法名就是组件名,这个是react内部进行编译处理的。跟vue差别很大。

 ​​​

 在App.tsx中引入robot组件

 npm run start 运行即可看到效果

 

 二、实现细节

实现机器人及星空特效,其中机器人构建可以是批量的,机器人身体又可以拆分为脑袋、触角、眼睛、身体、胳膊、腿等细节。每个部分单独用有方法实现,逻辑拆分清晰。

 对单个3D模型来说,需要三个东西:mesh=geometry(几何)+material(材料)

  1. 几何体(Geometry):

    • THREE.SphereGeometry: 用于创建球形几何体。它接受几个参数,包括半径(radius)、宽度分段(widthSegments)、高度分段(heightSegments)、水平起始角度(phiStart)、水平扫描角度(phiLength)、垂直起始角度(thetaStart)和垂直扫描角度(thetaLength)。
    • THREE.CapsuleGeometry: 用于创建胶囊形状的几何体,可以看作是一个圆柱体两端加上半球体。它接受两个参数,分别是半径(radius)和高度(height)。
    • THREE.CylinderGeometry :创建圆柱体。圆柱体由两个圆形底面和一个侧面组成。这个类的作用是定义一个圆柱形状的3D几何体,它可以在 Three.js 的场景中被渲染。
  2. 材质(Material):

    • THREE.MeshStandardMaterial: 用于创建标准网格材质,它提供了多种物理渲染特性,如颜色(color)、粗糙度(roughness)和金属度(metalness)等。
  3. 网格(Mesh):

    • THREE.Mesh: 网格是几何体和材质的组合,可以通过它将几何体渲染到场景中。它接受一个几何体(geometry)和一个材质(material)作为参数。

这里方便解耦,单个方法只生成模型,在调用方法的地方确定模型的位置。

 机器人脑袋

//机器人脑袋
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;
}

 主方法

Scene场景

scene是一个THREE.Scene对象,它是Three.js中场景的容器,用于组织和管理3D对象,如几何体、材质、相机和灯光等。

  • add(object): 这个方法用于将对象(例如机器人、光源、粒子等)添加到场景中。

示例代码中使用了多次:

scene.add(robot);
scene.add(robot2);
scene.add(straightLight);
scene.add(starts);

PerspectiveCamera透视相机

3D场景中的相机视角。一般都是使用透视相机PerspectiveCamera,第一个参数是fov,表示相机所成的一个四棱台远面与近面之间的夹角,夹角越小,看见的东西越少,夹角越大,看见的东西就越多,但是周围会显的比较模糊,一般取值以45~75最佳,第二个参数aspect是近裁面的一个宽高比,我们用窗口的宽除以窗口的高就可以了,第三个值near与第四个值far分别是与近面和远面的距离,这里设置的值分别为0.1与1000,调用camera.positon.set表示设置相机的位置,默认都是在(0,0,0)的位置,我们这里给相机设置的位置为(15,12,8),并且让相机正对(0,0,0)的位置

  • PerspectiveCamera是Three.js中的透视投影相机,文中使用的参数解释如下:
    • 75:视角(Field of View,FOV),决定了视场的宽度,单位是度。
    • window.innerWidth / window.innerHeight:纵横比,根据浏览器窗口的宽度和高度计算,确保相机适应窗口大小。
    • 0.1:近裁剪面(Near clipping plane),即相机能看到的最近物体的距离。
    • 1000:远裁剪面(Far clipping plane),即相机能看到的最远物体的距离。

DirectionalLight光源

在Three.js中,DirectionalLight 是一种光源,它模拟从特定方向发出的平行光,类似于太阳光。这种光源的特点是所有从光源发出的光线都是平行的,不会随着距离的增加而发散。

DirectionalLight 在这里的作用包括:

  1. 照亮场景:它为场景提供光照,使得场景中的物体可以被看到,并根据材质属性产生不同的光照效果。

  2. 产生阴影DirectionalLight 可以产生阴影效果,使得场景更加真实。物体遮挡光线的地方会形成阴影,有助于表现物体的立体感和空间位置。

  3. 定义光照方向:通过设置 DirectionalLight 的位置和方向,可以定义光线照射到场景的角度,从而影响场景的整体光照效果。

  4. 调整光照强度:可以通过设置 intensity 属性来调整光线的亮度。

OrbitControls 控制器

Three.js 中的 OrbitControls 是一个控制器,它允许用户通过鼠标或触摸事件来控制相机的移动,从而实现对场景的旋转、缩放和平移操作。这个控制器使得用户可以更自然地与3D场景交互。

在使用 OrbitControls 时,需要注意以下几点:

  • 控制器需要与相机和渲染器的DOM元素一起被初始化。
  • 在动画循环中调用 controls.update() 方法来确保控制器可以更新相机状态。
  • 可以通过修改 OrbitControls 的各种属性来自定义控制行为,例如最小/最大缩放距离、旋转限制等。

OrbitControls 是Three.js中非常实用的一个工具,它大大简化了3D场景的用户交互开发过程。

以下是 OrbitControls 的一些主要作用:

  1. 旋转(Rotate):用户可以通过拖动鼠标来旋转相机,从而改变视角。

  2. 缩放(Zoom):通过滚动鼠标滚轮或触摸屏幕进行捏合操作,可以放大或缩小场景。

  3. 平移(Pan):通过按下鼠标右键并拖动,或者在某些触摸设备上通过特定的手势,可以在场景中平移相机。

以下是 OrbitControls 的基本用法:

// 引入OrbitControls
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// 设置相机位置
camera.position.set(0, 0, 5);// 创建OrbitControls实例并传入相机和渲染器
const controls = new OrbitControls(camera, renderer.domElement);// 创建一些对象添加到场景中
// ...// 渲染场景
function animate() {requestAnimationFrame(animate);controls.update(); // 更新控制器renderer.render(scene, camera);
}
animate();

position 位置信息

在 Three.js 中,position 属性的作用是用于确定 3D 对象在场景中的位置, 属性对于实现 3D 场景中对象的布局、动画和交互等方面都非常重要。通过设置对象的 position 属性,可以精确地指定该对象在三维空间中的坐标。这使得能够将对象放置在所需的位置,从而构建出具有特定布局和结构的 3D 场景。

例如,如果要将一个立方体放置在场景的特定点(比如 x 坐标为 10,y 坐标为 20,z 坐标为 30),可以这样设置:

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);cube.position.set(10, 20, 30);scene.add(cube);

这样,立方体就会出现在指定的位置上。 

rotation旋转属性

在 Three.js 中,rotation 属性的作用是用于确定 3D 对象在场景中的旋转状态。通过设置对象的 rotation 属性,可以精确地指定该对象在三维空间中的旋转角度。这使得能够将对象旋转到所需的方向,从而构建出具有特定朝向和视角的 3D 场景。

rotation 属性使用的是欧拉角(Euler angles),它包括三个分量:xy 和 z,分别表示绕 X 轴、Y 轴和 Z 轴的旋转角度(以弧度为单位)。

例如,如果要将一个立方体绕 Y 轴旋转 45 度(即 π/4 弧度),可以这样设置:

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);cube.rotation.y = Math.PI / 4; // 45 degrees in radiansscene.add(cube);

requestAnimationFrame实现动画 

使用requestAnimationFrame: requestAnimationFrame是浏览器提供的API,用于在每一帧绘制动画。Three.js中的动画通常通过这个API来实现。

function animate() {requestAnimationFrame(animate);// 更新对象属性,例如位置、旋转等cube.rotation.x += 0.01;cube.rotation.y += 0.01;// 渲染场景renderer.render(scene, camera);
}
animate();

useEffect 

在React组件中,useEffect 钩子用于处理副作用操作,比如数据获取、订阅、手动更改DOM等。useEffect 钩子的主要作用是在组件挂载后将 Three.js 渲染器的 DOM 元素添加到指定的容器中,并在组件卸载时进行清理。

  1. 组件挂载时添加渲染器 DOM 元素

    • useEffect 钩子在组件挂载时执行,确保 containerRef.current 已经指向了实际的 DOM 元素。
    • 通过 containerRef.current.appendChild(renderer.domElement),将 Three.js 渲染器的 DOM 元素添加到指定的容器中。
  2. 确保渲染器 DOM 元素正确添加

    • 如果不使用 useEffect,直接在组件渲染时添加渲染器 DOM 元素,可能会导致 containerRef.current 为 null,因为此时 DOM 元素可能还未挂载到页面上。
/*** 创建一个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 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(5, 5, 10);// 将方向光添加到场景中scene.add(straightLight);const starts = generateStarts(200);scene.add(starts);//轨道控制器const controls = new OrbitControls(camera, renderer.domElement);controls.update();const update = () => {requestAnimationFrame(update);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);};update(); //自动更新// 监听组件挂载和卸载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>;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设备调试上位机GUI

C Fast Qt C 前端 原来真的不需要在 design 上画来画去&#xff0c;有chat-gpt 那里不知道问哪里 全是组件拼起来的,不需要画,最后发现其实也是定式模式,跟着AI 学套路

JavaScript将参数传递给事件处理程序

本篇文件我们将实现导航栏中&#xff0c;选中时候&#xff0c;会将您选中的进行高亮显示&#xff1b; ● 首先我们来获取我们想要的HTML元素 const nav document.querySelector(.nav);● 接着我们来写选中的高亮显示 nav.addEventListener(mouseover, function (e) { //鼠…

Python系统教程01

Python 是一门解释性语言&#xff0c;相对更简单、易学&#xff0c;它可以用于解决数学问题、获取与分 析数据、爬虫爬取网络数据、实现复制数学算法等等。 1、print()函数&#xff1a; print()书写时注意所有的符号都是英文符号。print()输出内容时&#xff0c;若要输出字符…

安卓实现微信聊天气泡

一搜没一个能用的&#xff0c;我来&#xff1a; 布局文件&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xml…

【MySQL】数据库事务详解

文章目录 前言1. 事务的定义2. 事务的四个特性2.1 原子性2.2 一致性2.3 隔离性2.4 持久性 3. 事务的并发问题3.1 脏读3.2 不可重复读3.3 幻读3.4 更新丢失 4. 事务的隔离级别5. 事务的使用结语 前言 假设我们现在需要操作数据库进行转账&#xff0c;A 给 B 转账 100 块钱&…

掌握React与TypeScript:从零开始绘制中国地图

最近我需要使用reactts绘制一个界面&#xff0c;里面需要以中国地图的形式展示区块链从2019-2024年这五年的备案以及注销情况&#xff0c;所以研究了一下这方面的工作&#xff0c;初步有了一些成果&#xff0c;所以现在做一些分享&#xff0c;希望对大家有帮助&#xff01; 在这…

【Kotlin】Kotlin 基础语法指南

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

基于TCP/QT/C++的网盘系统测试报告

目录 一、项目介绍 1、项目描述 2、项目组成模块 3、项目技术要点 二、用户功能测试 1、查看在线用户测试 1.1、运行服务器 1.2、登录两个账号 1.3、点击显示在线用户&#xff0c;可以看到jack和lucy 2、搜索用户测试 2.1、打开服务器&#xff0c;登录两个账号jack,lucy 2.2、在…

嵌入式学习——硬件(IIC、ADC)——day56

1. IIC 1.1 定义&#xff08;同步串行半双工通信总线&#xff09; IIC&#xff08;Inter-Integrated Circuit&#xff09;又称I2C&#xff0c;是是IICBus简称&#xff0c;所以中文应该叫集成电路总线。是飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备…

Linux高并发服务器开发(八)Socket和TCP

文章目录 1 IPV4套接字结构体2 TCP客户端函数 3 TCP服务器流程函数代码粘包 4 三次握手5 四次挥手6 滑动窗口 1 IPV4套接字结构体 2 TCP客户端 特点&#xff1a;出错重传 每次发送数据对方都会回ACK&#xff0c;可靠 tcp是打电话的模型&#xff0c;建立连接 使用连接 关闭连接…

【代码随想录】【算法训练营】【第49天】 [300]最长递增子序列 [674]最长连续递增序列 [718]最长重复子数组

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 49&#xff0c;周二&#xff0c;坚持不了一点~ 题目详情 [300] 最长递增子序列 题目描述 300 最长递增子序列 解题思路 前提&#xff1a;最大递增子序列的长度 思路&#xff1a;动态规划 d…

RedHat9 | podman容器-续集

一、管理容器存储和网络资源 使用容器来运行简单的进程&#xff0c;然后退出。可以配置容连续运行特定服务&#xff0c;如数据库服务。如果持续运行服务&#xff0c;需要向容器添加更多的资源&#xff0c;如持久存储或对其他网络的访问权限。 针对企业容器平台上的大型部署&a…

汽车零部件材料耐候性测试氙光太阳辐射系统试验箱

概述 汽车零部件等领域的材料耐候性测试是一项关键的质量控制环节&#xff0c;它关乎汽车部件在各种气候条件下的性能表现和寿命。塑料件光照老化实验箱&#xff0c;即氙灯老化试验箱&#xff0c;在其中扮演着至关重要的角色。通过模拟自然环境中的光照、温度、湿度等条件&…

遇到多语言跨境电商系统源码问题?这里有解决方案!

从手机到电脑&#xff0c;从线下到线上&#xff0c;如今&#xff0c;跨境电商正在打破地域界限&#xff0c;成为全球贸易的新引擎。在这个全球化的背景下&#xff0c;跨境电商平台的运营也面临着一系列的挑战&#xff0c;其中之一就是多语言问题。如果你遇到了多语言跨境电商系…

【HALCON】如何实现hw窗口自适应相机拍照成像的大小

前言 在开发一个喷码检测软件的时候碰到相机成像和hw窗体的大小不一致&#xff0c;hw太小显示不完全成像的图片&#xff0c;这使得成像不均匀&#xff0c;现场辨别起来比较不直观&#xff0c;因此需要对其进行一个调整。 解决 省略掉读取图片的环节&#xff0c;我们只需要将…

全国产化飞腾模块BIOS下修复系统启动文件

1、背景介绍 全国产飞腾模块采用麒麟信安操作系统&#xff0c;当系统下面的grub.cfg文件被用户误操作导致无法启动时&#xff0c;可以在BIOS下通过U盘中备份的grub.cfg替换硬盘上原来的grub.cfg文件&#xff0c;从而实现启动。 2、操作步骤 首先进入BIOS命令行模式&#xff…

2.3章节Python中的数值类型

1.整型数值 2.浮点型数值 3.复数   Python中的数值类型清晰且丰富&#xff0c;主要分为以下几种类型&#xff0c;每种类型都有其特定的用途和特性。 一、整型数值 1.定义&#xff1a;整数类型用于表示整数值&#xff0c;如1、-5、100等。 2.特点&#xff1a; Python 3中的…

Ubuntu(通用)—网络加固—ufw+防DNS污染+ARP绑定

1. ufw sudo ufw default deny incoming sudo ufw deny in from any to any # sudo ufw allow from any to any port 5353 protocol udp sudo ufw enable # 启动开机自启 # sudo ufw reload 更改后的操作2. 防ARP欺骗 华为云教程 arp -d删除dns记录arp -a显示arp表 ipconfi…

拆分盘投资策略解析:机制、案例与风险考量

一、引言 随着互联网技术的迅猛发展和金融市场的不断创新&#xff0c;拆分盘这一投资模式逐渐崭露头角&#xff0c;成为投资者关注的焦点。它基于特定的拆分策略&#xff0c;通过调整投资者持有的份额和单价&#xff0c;实现了看似稳健的资产增长。本文旨在深入探讨拆分盘的运…

MySQL-数据操作类型的角度理解 S锁 X锁

文章目录 1、S锁和S锁互相兼容2、S锁和X锁互斥3、X锁和X锁也互斥4、X锁和S锁也互斥5、select * from account for update;6、select * from account for update nowait;7、select * from account for update skip locked; 1、S锁和S锁互相兼容 2、S锁和X锁互斥 3、X锁和X锁也互…