使用 Three.js 创建一个 3D 人形机器人仿真系统

引言

在这篇文章中,我们将探讨如何使用 Three.js 创建一个简单但有趣的 3D 人形机器人仿真系统。这个机器人可以通过键盘控制进行行走和转向,并具有基本的动画效果。

在这里插入图片描述

技术栈

  • HTML5
  • Three.js
  • JavaScript

实现步骤

1. 基础设置

首先,我们需要创建基本的 HTML 结构并引入 Three.js 库:

<!DOCTYPE html>
<html>
<head>
    <title>3D Robot Simulation</title>
    <style>
        body { 
            margin: 0; 
            overflow: hidden;
        }
        canvas { 
            display: block; 
        }
        #info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-family: Arial;
            font-size: 14px;
            background: rgba(0,0,0,0.5);
            padding: 10px;
        }
    </style>
</head>
<body>
    <div id="info">
        使用方向键控制机器人:<br>
        ↑ - 向前移动<br>
        ← → - 左右转向
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</body>
</html>

2. 场景初始化

接下来,我们需要初始化 Three.js 的基本组件:

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB); // 天空蓝色背景

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

3. 创建机器人类

创建一个 Robot 类来管理机器人的所有组件和动作:

class Robot {
    constructor() {
        this.body = new THREE.Group();
        
        // 创建躯干
        const torsoGeometry = new THREE.BoxGeometry(2, 3, 1);
        const torsoMaterial = new THREE.MeshPhongMaterial({color: 0x999999});
        this.torso = new THREE.Mesh(torsoGeometry, torsoMaterial);
        this.torso.castShadow = true;
        
        // 创建头部
        const headGeometry = new THREE.SphereGeometry(0.5);
        const headMaterial = new THREE.MeshPhongMaterial({color: 0xcccccc});
        this.head = new THREE.Mesh(headGeometry, headMaterial);
        this.head.position.y = 2;
        this.head.castShadow = true;
        
        // ... 其他部件的创建代码 ...
    }
    
    // 创建四肢
    createLimb(width, height) {
        const geometry = new THREE.BoxGeometry(width, height, width);
        const material = new THREE.MeshPhongMaterial({color: 0x999999});
        const limb = new THREE.Mesh(geometry, material);
        limb.castShadow = true;
        return limb;
    }
    
    // 行走动画
    walk() {
        const legRotation = Math.sin(Date.now() * 0.005) * 0.5;
        this.leftLeg.rotation.x = legRotation;
        this.rightLeg.rotation.x = -legRotation;
        
        const armRotation = Math.sin(Date.now() * 0.005) * 0.25;
        this.leftArm.rotation.x = -armRotation;
        this.rightArm.rotation.x = armRotation;
    }
    
    // 转向和移动方法
    turn(angle) {
        this.body.rotation.y += angle;
    }
    
    moveForward(speed) {
        this.body.position.x += Math.sin(this.body.rotation.y) * speed;
        this.body.position.z += Math.cos(this.body.rotation.y) * speed;
    }
}

4. 添加环境元素

创建地面和光照系统:

// 创建地面
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshPhongMaterial({ 
    color: 0x808080,
    side: THREE.DoubleSide
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -2.5;
ground.receiveShadow = true;
scene.add(ground);

// 添加光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);

const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);

5. 实现控制系统

添加键盘控制和动画循环:

const keyStates = {};

document.addEventListener('keydown', (e) => {
    keyStates[e.key] = true;
});

document.addEventListener('keyup', (e) => {
    keyStates[e.key] = false;
});

function animate() {
    requestAnimationFrame(animate);
    
    if(keyStates['ArrowUp']) {
        robot.walk();
        robot.moveForward(0.1);
    }
    if(keyStates['ArrowLeft']) {
        robot.turn(0.05);
    }
    if(keyStates['ArrowRight']) {
        robot.turn(-0.05);
    }
    
    camera.position.x = robot.body.position.x;
    camera.position.z = robot.body.position.z + 10;
    camera.lookAt(robot.body.position);
    
    renderer.render(scene, camera);
}

animate();

主要特性

  1. 3D 人形机器人模型
  2. 基本的行走动画
  3. 键盘控制系统
  4. 相机跟随
  5. 阴影效果
  6. 响应式设计

控制方法

  • ↑ 向前移动
  • ← 向左转
  • → 向右转

可能的改进方向

  1. 添加更多动作(如后退、跳跃)
  2. 改进机器人模型细节
  3. 添加碰撞检测
  4. 添加物理引擎
  5. 实现更复杂的动画效果
  6. 添加声音效果
  7. 增加更多交互控制选项
  8. 优化性能

结论

通过这个项目,我们展示了如何使用 Three.js 创建一个基本的 3D 人形机器人仿真系统。虽然这是一个相对简单的实现,但它为更复杂的 3D 网页应用提供了良好的起点。

完整代码

<!DOCTYPE html>
<html>
<head>
    <title>3D Robot Simulation</title>
    <style>
        body { 
            margin: 0; 
            overflow: hidden;
        }
        canvas { 
            display: block; 
        }
        #info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-family: Arial;
            font-size: 14px;
            background: rgba(0,0,0,0.5);
            padding: 10px;
        }
    </style>
</head>
<body>
    <div id="info">
        使用方向键控制机器人:<br>- 向前移动<br>
        ← → - 左右转向
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // 初始化场景、相机和渲染器
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x87CEEB); // 天空蓝色背景

        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        document.body.appendChild(renderer.domElement);

        // 创建机器人类
        class Robot {
            constructor() {
                // 机器人主体
                this.body = new THREE.Group();
                
                // 躯干
                const torsoGeometry = new THREE.BoxGeometry(2, 3, 1);
                const torsoMaterial = new THREE.MeshPhongMaterial({color: 0x999999});
                this.torso = new THREE.Mesh(torsoGeometry, torsoMaterial);
                this.torso.castShadow = true;
                
                // 头部
                const headGeometry = new THREE.SphereGeometry(0.5);
                const headMaterial = new THREE.MeshPhongMaterial({color: 0xcccccc});
                this.head = new THREE.Mesh(headGeometry, headMaterial);
                this.head.position.y = 2;
                this.head.castShadow = true;
                
                // 眼睛
                const eyeGeometry = new THREE.SphereGeometry(0.1);
                const eyeMaterial = new THREE.MeshPhongMaterial({color: 0x000000});
                this.leftEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
                this.rightEye = new THREE.Mesh(eyeGeometry, eyeMaterial);
                this.leftEye.position.set(-0.2, 2, 0.4);
                this.rightEye.position.set(0.2, 2, 0.4);
                
                // 手臂
                this.leftArm = this.createLimb(0.3, 2);
                this.leftArm.position.set(-1.2, 1, 0);
                this.rightArm = this.createLimb(0.3, 2);
                this.rightArm.position.set(1.2, 1, 0);
                
                // 腿部
                this.leftLeg = this.createLimb(0.4, 2);
                this.leftLeg.position.set(-0.6, -1.5, 0);
                this.rightLeg = this.createLimb(0.4, 2);
                this.rightLeg.position.set(0.6, -1.5, 0);
                
                // 组装机器人
                this.body.add(this.torso);
                this.body.add(this.head);
                this.body.add(this.leftEye);
                this.body.add(this.rightEye);
                this.body.add(this.leftArm);
                this.body.add(this.rightArm);
                this.body.add(this.leftLeg);
                this.body.add(this.rightLeg);
            }
            
            createLimb(width, height) {
                const geometry = new THREE.BoxGeometry(width, height, width);
                const material = new THREE.MeshPhongMaterial({color: 0x999999});
                const limb = new THREE.Mesh(geometry, material);
                limb.castShadow = true;
                return limb;
            }
            
            walk() {
                // 腿部摆动
                const legRotation = Math.sin(Date.now() * 0.005) * 0.5;
                this.leftLeg.rotation.x = legRotation;
                this.rightLeg.rotation.x = -legRotation;
                
                // 手臂摆动
                const armRotation = Math.sin(Date.now() * 0.005) * 0.25;
                this.leftArm.rotation.x = -armRotation;
                this.rightArm.rotation.x = armRotation;
            }
            
            turn(angle) {
                this.body.rotation.y += angle;
            }
            
            moveForward(speed) {
                this.body.position.x += Math.sin(this.body.rotation.y) * speed;
                this.body.position.z += Math.cos(this.body.rotation.y) * speed;
            }
        }

        // 创建地面
        const groundGeometry = new THREE.PlaneGeometry(100, 100);
        const groundMaterial = new THREE.MeshPhongMaterial({ 
            color: 0x808080,
            side: THREE.DoubleSide
        });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        ground.position.y = -2.5;
        ground.receiveShadow = true;
        scene.add(ground);

        // 创建机器人实例
        const robot = new Robot();
        scene.add(robot.body);

        // 添加光源
        const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
        directionalLight.position.set(5, 10, 5);
        directionalLight.castShadow = true;
        directionalLight.shadow.camera.near = 0.1;
        directionalLight.shadow.camera.far = 100;
        directionalLight.shadow.camera.left = -50;
        directionalLight.shadow.camera.right = 50;
        directionalLight.shadow.camera.top = 50;
        directionalLight.shadow.camera.bottom = -50;
        scene.add(directionalLight);

        const ambientLight = new THREE.AmbientLight(0x404040);
        scene.add(ambientLight);

        // 设置相机位置
        camera.position.set(0, 5, 10);
        camera.lookAt(robot.body.position);

        // 键盘状态
        const keyStates = {};

        // 键盘事件监听
        document.addEventListener('keydown', (e) => {
            keyStates[e.key] = true;
        });

        document.addEventListener('keyup', (e) => {
            keyStates[e.key] = false;
        });

        // 窗口大小调整
        window.addEventListener('resize', onWindowResize, false);

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            
            // 更新机器人动作
            if(keyStates['ArrowUp']) {
                robot.walk();
                robot.moveForward(0.1);
            }
            if(keyStates['ArrowLeft']) {
                robot.turn(0.05);
            }
            if(keyStates['ArrowRight']) {
                robot.turn(-0.05);
            }
            
            // 相机跟随
            camera.position.x = robot.body.position.x;
            camera.position.z = robot.body.position.z + 10;
            camera.lookAt(robot.body.position);
            
            renderer.render(scene, camera);
        }

        animate();
    </script>
</body>
</html>

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

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

相关文章

【c++高阶DS】最小生成树

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 目录 01.最小生成树Kruskal算法Prim算法 01.最小生成树 连通图中的每一棵生成树&#xff0c;都是原图的一个极大无环子图&#xff0c;即&#xff1a;从其中删去任何一条边&#xff0c;生成…

自学记录鸿蒙API 13:实现人脸比对Core Vision Face Comparator

完成了文本识别和人脸检测的项目后&#xff0c;我发现人脸比对是一个更有趣的一个小技术玩意儿。我决定整一整&#xff0c;也就是对HarmonyOS Next最新版本API 13中的Core Vision Face Comparator API的学习&#xff0c;这项技术能够对人脸进行高精度比对&#xff0c;并给出相似…

2024/12/29 黄冈师范学院计算机学院网络工程《路由期末复习作业一》

一、选择题 1.某公司为其一些远程小站点预留了网段 172.29.100.0/26&#xff0c;每一个站点有10个IP设备接到网络&#xff0c;下面那个VLSM掩码能够为该需求提供最小数量的主机数目 &#xff08; &#xff09; A./27 B./28 C./29 D./30 -首先审题我们需要搞清楚站点与网…

redis cluster集群

华子目录 什么是redis集群redis cluster的体系架构什么是数据sharding&#xff1f;什么是hash tag集群中删除或新增节点&#xff0c;数据如何迁移&#xff1f;redis集群如何使用gossip通信?定义meet信息ping消息pong消息fail消息&#xff08;不是用gossip协议实现的&#xff0…

PrimeVue菜单模块(Menu),看api的重要性

以下是对PrimeVue菜单模块&#xff08;Menu&#xff09;的API属性的中文详解&#xff1a; 一、整体概述 PrimeVue的菜单&#xff08;Menu&#xff09;是一个支持动态和静态定位的导航/命令组件&#xff0c;其API通过定义一些辅助的属性&#xff08;props&#xff09;、事件等&…

STM32中断详解

STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1&#xff1a;时钟源标号 2&#xff1a;控制器标号 3&#xff1a;时基单元 代码实现 NVIC 中断系统 STM…

从零开始开发纯血鸿蒙应用之逻辑封装

从零开始开发纯血鸿蒙应用 一、前言二、逻辑封装的原则三、实现 FileUtil1、统一的存放位置2、文件的增删改查2.1、文件创建与文件保存2.2、文件读取2.2.1、读取内部文件2.2.2、读取外部文件 3、文件删除 四、总结 一、前言 应用的动态&#xff0c;借助 UI 响应完成&#xff0…

《机器学习》——线性回归模型

文章目录 线性回归模型简介一元线性回归模型多元线性回归模型误差项分析一元线性模型实例完整代码 多元线性模型实例完整代码 线性回归模型简介 线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。 相关关系&…

【深度学习环境】NVIDIA Driver、Cuda和Pytorch(centos9机器,要用到显示器)

文章目录 一 、Anaconda install二、 NIVIDIA driver install三、 Cuda install四、Pytorch install 一 、Anaconda install Step 1 Go to the official website: https://www.anaconda.com/download Input your email and submit. Step 2 Select your version, and click i…

在HTML中使用Vue如何使用嵌套循环把集合中的对象集合中的对象元素取出来(我的意思是集合中还有一个集合那种)

在 Vue.js 中处理嵌套集合&#xff08;即集合中的对象包含另一个集合&#xff09;时&#xff0c;使用多重 v-for 指令来遍历这些层次结构。每个 v-for 指令可以用于迭代一个特定级别的数据集&#xff0c;并且可以在模板中嵌套多个 v-for 来访问更深层次的数据。 例如&#xff…

ip归属地是什么意思?ip归属地是实时定位吗

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识符&#xff0c;不仅关乎设备间的通信&#xff0c;还涉及到用户的网络身份与位置信息。其中&#xff0c;IP归属地作为IP地址的地理位置信息&#xff0c;备受用户关注。本文将详细解析IP归属地的含义&#xff0c;并探讨其是…

基于BP训练深度学习模型(用于回归)以及验证误差值

用原生Python训练了一个BP网络&#xff0c;适合没有pytorch等环境的电脑&#xff0c;并用训练的模型对原始数据进行了预测&#xff0c;拿来估测比较误差值了&#xff0c;可以直接拿去用&#xff08;需根据个人数据来调训练次数、学习效率&#xff09;&#xff0c;代码在文章末。…

C#冒泡排序

一、冒泡排序基本原理 冒泡排序是一种简单的排序算法。它重复地走访要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;也就是说该数列已经排序完成。 以一个简单的整数数…

折腾日记:如何让吃灰笔记本发挥余热——搭建一个相册服务

背景 之前写过&#xff0c;我在家里用了一台旧的工作站笔记本做了服务器&#xff0c;连上一个绿联的5位硬盘盒实现简单的网盘功能&#xff0c;然而&#xff0c;还是觉的不太理想&#xff0c;比如使用filebrowser虽然可以备份文件和图片&#xff0c;当使用手机使用网页&#xf…

从0入门自主空中机器人-2-1【无人机硬件框架】

关于本课程&#xff1a; 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程&#xff0c;包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程&#xff0c;带你从0开始&#xff0c;组装属于自己的自主无人机&#xff0c;并让…

剑指Offer|LCR 013. 二维区域和检索 - 矩阵不可变

LCR 013. 二维区域和检索 - 矩阵不可变 给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的左上角为 (row1, col1) &#xff0c;右下角为 (row2, col2) 。 实现 NumMatrix 类&#xff1a; NumMatrix(…

接口Mock技术介绍

相信学习过程序设计的读者朋友们&#xff0c;一定对“桩&#xff08;Stub&#xff09;”这个概念并不陌生。它是指用来替换一部分功能的程序代码段。桩程序代码段可以用来模拟已有程序的某些功或者是将实现的系统代码的一种临时替代方法。插桩方法被广泛应用于开发和测试工作中…

深入解析C#异步编程:await 关键字背后的实现原理

在C#中&#xff0c;async 和 await 关键字用于编写异步代码。本文将详细介绍 await 的实现原理&#xff0c;包括状态机的生成、回调函数的注册和触发等关键步骤。 1. 异步方法的基本概念 在C#中&#xff0c;async 关键字标记一个方法为异步方法&#xff0c;而 await 关键字用于…

【机器学习】SVM支持向量机(一)

介绍 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种监督学习模型&#xff0c;广泛应用于分类和回归分析。SVM 的核心思想是通过找到一个最优的超平面来划分不同类别的数据点&#xff0c;并且尽可能地最大化离该超平面最近的数据点&#xff08;支持向量…

Unity功能模块一对话系统(1)前置准备

也许你也曾被游戏中的对话系统深深吸引&#xff0c;那些精心设计的对白、鲜活的角色配音、甚至是简单的文字对话&#xff0c;往往能让玩家产生强烈的代入感和情感共鸣。如果你正在开发一款游戏&#xff0c;或者计划为你的项目加入一个引人入胜的对话系统&#xff0c;那么 Unity…