Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。你可以在它的主页上看到许多精彩的演示。不过,这款引擎目前还处在比较不成熟的开发阶段,其不够丰富的 API 以及匮乏的文档增加了初学者的学习难度(尤其是文档的匮乏)three.js的代码托管在github上面。——百度百科

  Three.js封装了OpenGl ES 2.0 的API,使得我们更容易在浏览器上开发各种图形效果。比起使用winform,MFC等形式,这个代码库可以让我们避开一些winform和MFC的细节问题,而专注于图形的绘制。不过由于这个库也并没有封装到非常完美,所以有些部分还需要我们自己来实现。

  在此贴上Three.js中文文档的连接地址Three.js 中文文档 | 参考手册 | 使用指南 | 动画特效实例 | 踏得网

  虽然这个链接上的文档有一些错误,还有许多坑,很多东西都没有说明清楚,不过,也是可以参考的。

  如果要直接看效果,可以直接下载文章末尾的源代码,附带了操作方式。还需要使用电脑浏览器打开代码,因为没有对移动端做适配,所以移动端效果不好。

  接下来,让我们实现一个机器人的手臂运动效果。效果如下图

  

  很丑对不对,但这只是一个教程,所以美化可以自己来做,过多的美化工作反而会增加学习的复杂程度。

  我们制作这个机器人,需要解决的一些问题是:

    1.如何让我们的图形沿着任意轴转动;

    2.如何使用键盘控制其转动;

  这两个问题解决了,那么我们的机器人也就非常容易绘制了。接下来,就开始详解代码以及思路了。

  首先,我们需要生成一个canvas,因为在html5中,所有的绘图工作都是在canvas中进行。我们可以写一个canvas标签,也可以让Three.js帮我们生成一个。这里我们让Three.js帮我们生成:

  

 var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 5;
camera.position.y = 1;
camera.position.x = 1.5;
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

  为什么相机要进行位置的调整呢?这是因为,如果我们不调整位置,那么我们的视角是正对着物体的,物体的3D效果会因为视角问题而显得不那么明显。

  接下来,我们要绘制机器人的头,腿,身体。需要注意的是,如果我们为我们生成的图形选择了不合适的材质,会导致3D效果观察不明显。关于材质的选择,已经超出了要讲述的范围,所以为了演示简单,我们只使用基本材质。代码如下:

  

 //机器人身体
var geometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true //这里是为了让其变成网格状态,从而使其更容易观察,默认为false
});
var cube = new THREE.Mesh(geometry, material);
cube.position.y = -1;
//机器人头部
var head = new THREE.SphereGeometry(0.5, 32, 32);
var material = new THREE.MeshBasicMaterial({
color: 0x0909FD,
wireframe: true
});
var sp = new THREE.Mesh(head, material);
//机器人双腿
var geometry = new THREE.BoxGeometry(0.1, 1, 0.1);
var material = new THREE.MeshBasicMaterial({
color: 0xFF2E67,
wireframe: true
});
var left_leg = new THREE.Mesh(geometry, material);
var right_leg = new THREE.Mesh(geometry, material);
left_leg.position.x = -0.3;
left_leg.position.y = -2;
right_leg.position.x = 0.3;
right_leg.position.y = -2;

  我们的机器人就画成了这样:

  

  接下来,就到了最关键的地方:机器人手臂的转动。很多刚学习的同学都发现,我们的图形的旋转轴是在图形的正中心。什么意思呢?看这个图:

  

  我在图中已经标识了我们生成物体的坐标轴,可以看出,坐标轴实际上是在位置的重心。我们通过函数或者设置属性的方法,只能绕着这三个轴转。有同学问,可不可以通过先移位,再旋转,再移回原位置的方式来解决这个问题呢?我们的计算机图形学课本上是这么教的,在winfoem和MFC上也是可以做到的,但由于three.js生成的物体的坐标轴的特殊性,我们无法通过这种形式来解决(亲测)。有兴趣的同学可以自己试一试。

  那么,我们该怎么办呢?我们查找手册,发现three.js中有一个类,Object3D()。这个类生成一个3D的模型,也可以说是容器。那么,我们可不可以通过生成一个容器,然后再将我们需要的图形放入这个容器,再调整容器和图形的位置来实现图形绕任意轴旋转呢?很显然,是可以的,如果你能想到,说明你的智商还是值得称道的。不过想不到也没有任何问题,因为我们需要承认自己的平庸,才能激励自己努力学习。接下来,我们来探讨,如何进行旋转操作。

  首先,我们先生成一个容器对象,并且加入坐标轴显示,看看我们生成的容器是什么样的

 var upbox = new THREE.Object3D();
scene.add(upbox);
upbox.add(new THREE.AxisHelper(3));

  效果如下:

  

  可以看出,虽然我们无法看到这个容器,但这个容器还是有其对应的坐标位置。我们可以通过设置其position属性来移动它的位置,或者旋转它。接下来,我们把我们生成的立方体放入这个容器,同时也显示我们立方体的坐标轴。代码如下:

  

 var geometry = new THREE.BoxGeometry(1, 1, 1, 8, 8, 8);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true //这里是为了让其变成网格状态,从而使其更容易观察,默认为false
});
var cube = new THREE.Mesh(geometry, material);
cube.add(new THREE.AxisHelper(3)); var upbox = new THREE.Object3D();
scene.add(upbox);
upbox.add(new THREE.AxisHelper(3)); var render = function() {
requestAnimationFrame(render);
upbox.add(cube); //将立方体放入容器
renderer.render(scene, camera);
}

  效果如图:

  

  可以看到,新加入的这个立方体的坐标轴和这个容器的坐标轴重叠了。而此时,关键的来了,此时我们的立方体的位移已经不是相对于世界坐标了,而是相对于其父容器的坐标。也就是说,我们的立方体现在已经不是相对于世界坐标系的原点来移动了,而是相对于容器的坐标原点。由于截图来表示世界坐标系不是很直观,所以大家可以试着移动一下容器的位置,移动容器的位置,立方体的位置也会跟着容器的变化而变化。

  接下来,我们来移动立方体的位置:

 cube.position.x = 1;
cube.position.y = -1;

  效果如下:

  

  可以看出,我们的立方体已经移动了,而且是相对于容器的原点来移动的。那么,我们来试一下旋转我们的容器:

  

 upbox.rotation.z = 1;

  效果如下:

  

  我们的立方体就跟着容器转了起来。

  那么,我么就可以得到一个结论:我们可以将我们的容器的坐标轴当作我们需要移动的坐标轴,然后把我们的图形移动到适当的的位置,我们需要让立方体按x=1旋转,就把容器移动到x=1的位置;我们需要让立方体按y=1转,就把我们的容器移动到y=1的位置来旋转。不过这样也有一个问题,那就是我们的立方体的坐标已经不是相对于世界坐标了,这里需要注意。之前说道,只要把立方体移到适当的位置就可以了,那么这个适当的位置是哪里呢?文字描述非常复杂,我们用代码和效果来说明:

  

 cube.add(new THREE.AxisHelper(1));
cube.position.x = 0.5;
cube.position.y = 0.5;
cube.position.z = -0.5;

  效果图:

  

  这个位置就可以说成是适当的位置,我们直接设置好容器的坐标,再让容器旋转,就达到了我们绕任意轴旋转的功能。当然,按照个人的需求,这个适当位置是可以调整的。

  如果我们需要实现复杂的控制,那么我们就可以采用容器里套容器的方法。这个和动画中的骨骼非常像,不过也不一样。接下来,我们就可以继续接着之前的那个机器人来绘制我们的机器人手臂了。

  

                       //机器人上臂
var upbox = new THREE.Object3D();
upbox.position.x = 0.5;
upbox.position.y = -1.0;
scene.add(upbox);
upbox.add(new THREE.AxisHelper(2));
var geometry = new THREE.BoxGeometry(1, 0.1, 0.1);
var material = new THREE.MeshBasicMaterial({
color: 0xFF2E67,
wireframe: true
} );
var ge = new THREE.Mesh(geometry, material);
ge.position.x = 0.5;
ge.position.y = 0;
//机器人下臂
var box = new THREE.Object3D();
box.position.x = 1.0;
box.position.y = 0.0;
scene.add(box);
//box.add(new THREE.AxisHelper(2));
var geometry = new THREE.BoxGeometry(0.1, 1, 0.1);
var material = new THREE.MeshBasicMaterial({
color: 0xFF2E67,
wireframe: true
} );
var ge_next = new THREE.Mesh(geometry, material);
ge_next.position.y = 0.5;
box.add(ge_next);
//机器人手掌
var handbox = new THREE.Object3D();
handbox.position.x = 0;
handbox.position.y = 0.0;
scene.add(handbox);
//handbox.add(new THREE.AxisHelper(2));
var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5, 4, 4, 4);
var material = new THREE.MeshBasicMaterial({
color: 0xFF2E67,
wireframe: true
} );
var hand = new THREE.Mesh(geometry, material);
hand.position.y = 1.2;
//机器人中指
var zhongzhibox = new THREE.Object3D();
zhongzhibox.position.x = 0;
zhongzhibox.position.y = 1.5;
scene.add(zhongzhibox);
//zhongzhibox.add(new THREE.AxisHelper(2));
var geometry = new THREE.BoxGeometry(0.1, 0.5, 0.1);
var material = new THREE.MeshBasicMaterial({
color: 0xFF2E67,
wireframe: true
} );
var zhongzhi = new THREE.Mesh(geometry, material);
zhongzhi.position.y = 0.2;
scene.add(cube);
scene.add(sp);
scene.add(ge);
scene.add(ge_next);
scene.add(hand);
scene.add(zhongzhi);
scene.add(left_leg);
scene.add(right_leg);

  其中,也有容器中嵌套容器的操作。

  那么,就该解决第二个问题了。我们如何通过键盘控制我们的机器人手臂呢?

  很简单,使用事件绑定。我们可以给onkeydown这个事件绑定操作,来实现控制机器人的手臂。类似于:

  

                      onkeydown = function(event) {
if(angle > 0 || angle <= -1.5) {
angle = 0;
return false;
}
else if(event.keyCode == 38) {
angle -= 0.2;
}
}

  为什么有一个angle呢?因为我们需要判断我们的机器人的手臂运动的角度,毕竟是手臂,转动的角度需要有一个限制对吧?但是呢,由于比较复杂(其实是懒),所以我只实现了两个部位的限制。

  实现了上述的代码,我们应该就可以画出一个可以控制的机器人了。

  下面是源代码:

  机器人手臂源代码

  

使用three.js实现机器人手臂的运动效果的更多相关文章

  1. js模拟抛出球运动

    js练手之模拟水平抛球运动 -匀加速运动 -匀减速运动 模拟运动有些基本的思路,当前所在点的坐标,元素的长宽是多少,向右/向下运动x/y增加,向上/向左运动x/y减少,运动的路程是多少,用什么方程进行 ...

  2. ScrollReveal.js – 帮助你实现超炫的元素运动效果

    ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力.只需要给元素增加 data-scrollreveal 属性,当元素进入可视区域的时候会自动被触发设置 ...

  3. [js高手之路]封装运动框架实战左右与上下滑动的焦点轮播图

    在这篇文章[js高手之路]打造通用的匀速运动框架中,封装了一个匀速运动框架,我们在这个框架的基础之上,加上缓冲运动效果,然后用运动框架来做幻灯片(上下,左右),效果如下: 1 2 3 4 5 // 0 ...

  4. 基于canvas实现物理运动效果与动画效果(一)

    一.为什么要写这篇文章 某年某月某时某种原因,我在慕课网上看到了一个大神实现了关于小球的抛物线运动的代码,心中很是欣喜,故而写这篇文章来向这位大神致敬,同时也为了弥补自己在运动效果和动画效果制作方面的 ...

  5. javascript学习-原生javascript的小特效(多物体运动效果)

    前些日子看了个视频所以就模仿它的技术来为大家做出几个简单的JS小特效 今天为大家做的是多个物体的运动效果, 1:HTML <body> <ul>       <li> ...

  6. javascript学习-原生javascript的小特效(简单的运动效果)

    前些日子看了个视频所以就模仿它的技术来为大家做出几个简单的JS小特效 一:运动特效(主要是通过改变元素的left,right,height,width,opacity来达到运动的效果) 我们今天做一个 ...

  7. Js实现京东无延迟菜单效果(demo)

    一个端午节,外面人山人海,又那么热,我认为宅在家里看看慕课网,充实自己来的实际... 这是一个js实现京东无延迟菜单效果,感觉很好,分享给大家... 1.开发基本的菜单结构 2.开发普通的二级菜单效果 ...

  8. js鼠标滚轮滚动图片切换效果

    效果体验网址:http://keleyi.com/keleyi/phtml/image/12.htm HTML文件代码: <!DOCTYPE html PUBLIC "-//W3C// ...

  9. 原生JS、CSS实现的转盘效果(目前仅支持webkit)

    这是一个原生JS.CSS实现的转盘效果(题目在这:http://www.cnblogs.com/arfeizhang/p/turntable.html),花了半个小时左右,准备睡觉,所以先贴一段代码, ...

随机推荐

  1. 开涛spring3(3.4) - DI 之 3.4 Bean的作用域

    3.4  Bean的作用域 什么是作用域呢?即“scope”,在面向对象程序设计中一般指对象或变量之间的可见范围.而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围. ...

  2. java虚拟机学习-JVM调优总结-分代垃圾回收详述(9)

    为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...

  3. PHP实现记录日志(文件)

    PHP实现记录日志(文件) php php 记录日志 项目中经常会记录些操作信息,或是打印些关键变量,或者是导入excel文件,提现记录,都需记录.经常遇到,封装一个方法,有不好的地方或补充请留言. ...

  4. 微信小程序封装http访问网络库实例代码

    之前都是使用LeanCloud为存储,现在用传统API调用时做如下封装 var HOST = 'http://localhost/lendoo/public/index.php/'; // 网站请求接 ...

  5. 小K的H5之旅-实战篇(一)

    一.前言 本K在经过两个星期的html和css学习之后,第一次去尝试完成一个网站主页的制作.在四天之后,本K也终于完成了杰瑞教育主页的html和css部分,至于部分涉及js的部分,因为本K还没有学习过 ...

  6. (转)Nginx的启动、停止与重启

    启动 启动代码格式:nginx安装目录地址 -c nginx配置文件地址 例如: [root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /us ...

  7. 高性能队列Disruptor系列1--传统队列的不足

    在前一篇文章Java中的阻塞队列(BlockingQueue)中介绍了Java中的阻塞队列.从性能上我们能得出一个结论:数组优于链表,CAS优于锁.那么有没有一种队列,通过数组的方式实现,而且采用无锁 ...

  8. HTML标签类型及特点

    关键词:块级元素  行级元素 行内块元素   一. 概述           HTML(Hyper Text Markup Language )作为一种标记语言,网页所有的内容均书写在标签内部,标签是 ...

  9. java 多线程基础

    线程是进程内的执行单元,进程当中都有若干个线程. 通常主线程或进程是阻塞式的按顺序执行的,如果希望异步执行些子任务,而不要阻塞当前线程的执行,即需要创建子线程,子线程创建后主线程可以等待它们的结果,得 ...

  10. JavaScript中for循环的使用详解

    or循环是循环最紧凑的形式,并包含有以下三个重要部分组成: 循环初始化计数器的初始值.初始化语句执行循环开始之前. 测试语句,将测试如果给定的条件是真还是假.如果条件为真,那么将要执行的循环中给定的代 ...