THREE.JS入门教程(1)THREE.JS使用前了解


Posted in Javascript onJanuary 24, 2013

Three.js是一个伟大的开源WebGL库,WebGL允许JavaScript操作GPU,在浏览器端实现真正意义的3D。但是目前这项技术还处在发展阶段,资料极为匮乏,爱好者学习基本要通过Demo源码和Three.js本身的源码来学习。

国外网站 aerotwist.com 有六篇较为简单的入门教程,我尝试着将其翻译过来,与大家分享。
我在一些实验项目中使用了Three.js,我发现它对快速上手浏览器3D编程确实很有帮助。通过Three.js,你不仅可以创建相机、物体、光线、材质等等,还可以选择着色器,可以决定使用何种技术(WebGL、Canvas或SVG)在网页上渲染你的3D图形。Three.js是开源的,你甚至可以参与到这个项目中来。但现在,我将把重点放在基础的介绍上,我将向你展示如何使用这个引擎上。

尽管Three.js如此奇妙,但有时候它也会令人抓狂。比如,你将花费大量时间阅读例程,做一些逆向工程(在我的情形下)来确定某个函数的作用,有时还要去GitHub上提问。如果你需要提问,Mr. doob和AlteredQualia是极好的选择。

1.基础
我假定你的三维图形学知识过关,而且也在一定程度上掌握了JavaScript。如果不是这样,那先去学一点吧,否则直接看这篇教程,也许会感到困惑。

在我们的三维世界里,我们有以下这些东西。我会带你一步一步创建它们。
1.场景
2.渲染器
3.相机
4.物体(带有材质的)
当然,你也可以创造些其他的什么东西,我也希望你如此做。
2.浏览器支持
简单地看一下浏览器的支持情况吧。Google家的Chrome浏览器支持Three.js,在我的实验里,无论是对渲染器的支持程度还是JavaScript解释器的运行速度,Chrome都是做得最好的:它支持Canvas、WebGL和SVG,而且运行得非常快。FireFox浏览器排在第二位,它的JavaScript引擎的速度比Chrome慢了半拍,但是对渲染器的支持也很棒,而且FireFox的速度,随着版本更新也越来越快。Opera浏览器正在逐渐增加对WebGL的支持,Mac上的Safari浏览器有一个开启WebGL的选项。总体上,这两个浏览器仅仅支持Canvas渲染。微软家的IE9现在只支持Canvas渲染,而且微软似乎并不乐意支持WebGL这个新特性,所以我们现在肯定不会用IE9来做实验。
3.设置场景
假定你已经选择了一个支持所有渲染技术的浏览器,而且你准备通过Canvas或WebGL来渲染场景(这是更标准化的选择)。Canvas比WebGL有着更广泛地支持,但是WebGL可以直接在GPU上操作,这意味着你的CPU可以专注地处理非渲染类的工作,比如物理引擎或与用户交互等。

无论你选择何种渲染器,你都必须牢记在心的是:JavaScript代码需要优化。三维显示对浏览器来说不是一项轻松的工作(现在能够这样做就很伟大了),所以如果你的渲染太慢了,你需要知道你代码的瓶颈在何处,如果可能,改善它。

说了这么多,我想你已经下载好Three.js源代码,而且将它引入了你的html文档了。那么如何开始创建一个场景呢?就像这样:

// 设置场景大小 
var WIDTH = 400, 
HEIGHT = 300; 
// 设置一些相机参数 
var VIEW_ANGLE = 45, 
ASPECT = WIDTH / HEIGHT, 
NEAR = 0.1, 
FAR = 10000; 
// 获取DOM结构中的元素 
// - 假设我们使用了JQuery 
var $container = $('#container'); 
// 创建渲染器、相机和场景 
var renderer = new THREE.WebGLRenderer(); 
var camera = 
new THREE.PerspectiveCamera( 
VIEW_ANGLE, 
ASPECT, 
NEAR, 
FAR); 
var scene = new THREE.Scene(); 
// 将相机加入场景 
scene.add(camera); 
// 相机的初始位置为原点 
// 将相机拉回来一些(译者注:这样才能看到原点) 
camera.position.z = 300; 
// 启动渲染器 
renderer.setSize(WIDTH, HEIGHT); 
// 将渲染器加到DOM结构中 
$container.append(renderer.domElement);

你看,简单吧!
4.构建网格表面
现在我们有了一个场景,一个相机和一个渲染器(在我的例子里,当然是一个WebGL渲染器),但我们事实上什么还没画呢。事实上,Three.js提供了载入某几种标准格式3D文件的支持,如果你在Blender,Maya,Cinema4D或是什么其他工具中建模,这简直太棒了。为了简单(毕竟这才刚开始呢!)我们先来考虑基元。基元就是基本的几何表面,比如最基本的球体、平面、立方体、圆柱体。利用Three.js可以很方便地创建这些基元:
// 设置球体参数(译者注:球体被划分为16×16的网格,如果后两个参数取4、2,则生成一个八面体,请想象) 
var radius = 50, 
segments = 16, 
rings = 16; 
// material覆盖在geometry上,生成mesh 
var sphere = new THREE.Mesh( 
new THREE.SphereGeometry( 
radius, 
segments, 
rings), 
sphereMaterial); 
// 将mesh加入到场景中 
scene.add(sphere);

好了,但是球体上的材质呢?在代码中我们使用了一个sphereMaterial变量,我们还没定义它呢。那我们就先来看看怎么创建材质吧。
5.材质
毫无疑问,这是Three.js最有用的部分了。这部分提供了几个非常易用的通用材质模型:
1.Basic材质:表示一种不考虑光照的材质,现在只能这么说了。
2.Lambert材质:(译者注:朗伯面,各向同性反射)。
3.Phong材质:(译者注:冯氏面,有光泽的表面,介于镜面反射和朗伯反射之间的反射,描述真实世界的反射)。

除此之外,还有一些其他类型材质,简单起见,就留给你自己探索。事实上,在使用WebGL类型的渲染器时,材质实在太好用了。为什么呢?因为在原生WebGL种你必须亲自为每个渲染编写着色器,而着色器本身就是个巨大的工程:简单地说着色器是使用GLSL语言(OpenGL的着色器语言)写的,用来操作GPU的程序,这意味着你要在数学上模拟光照,反射等等,这很快就变成一项极为复杂的工作。多亏有了Three.js你才可以不必去自己编写着色器,当然,如果你想亲自编写的话,你可以使用MeshShaderMaterial,可见这是很灵活的设定。

现在,让我们用朗伯面材质覆盖球体:

// 创建球体表面的材质 
var sphereMaterial = 
new THREE.MeshLambertMaterial( 
{ 
color: 0xCC0000 
});

值得指出的是,创建材质的时候,除了颜色还有很多其他参数可以指定,比如光滑度和环境贴图。你可以需要检索这个Wiki页面来确认哪些是哪些属性可以设置在材质上,或Three.js引擎提供的任何对象上。
6.光
如果你现在就想渲染场景,你会看到一个红色的圆。虽然我们在球体上覆盖了朗伯面材质,但场景里没有光。所以按照默认设定,Three.js会恢复到满环境光,物体的看上去的颜色就是物体表面的颜色。让我们添加一个简单的点光源:
// 创建一个点光源 
var pointLight = 
new THREE.PointLight(0xFFFFFF); 
// 设置点光源的位置 
pointLight.position.x = 10; 
pointLight.position.y = 50; 
pointLight.position.z = 130; 
// 将点光源加入场景 
scene.add(pointLight);

7.渲染循环
显然,关于渲染器的一切都设置好了。万事俱备,我们现在只需要:
// 画! 
renderer.render(scene, camera);

你很可能像多次渲染,而不是只渲染一次,所以如果你要去做一个循环,你应该使用requestAnimationFrame。这是目前最好的,在浏览器中处理动画的方法,虽然还没有得到最全面的支持,但我强烈建议你去看一看Paul Irish的博客。
8.通用的对象属性
如果你花点时间去浏览一遍Three.js的源代码,你会发现很多对象都继承自Object3D。这个基类包含了很多有用的属性,比如位置、旋转和缩放的信息。特别的,我们的球体是一个Mesh对象,而Mesh对象继承自Object3D对象,但是又增加了些自己的属性:geometry和material。为什么要说这些?因为你一定不会只满足于屏幕中一个什么都不做的圆球,而这些(译者注:基类中的)属性允许你操作Mesh对象更底层的细节和各种各样的材质。
// sphere是一个mesh对象 
sphere.geometry 
// sphere包含了一些点和面的信息 
sphere.geometry.vertices // 一个数组 
sphere.geometry.faces // 另一个数组 
// mesh对象继承自object3d对象 
sphere.position // 包含x,y,z 
sphere.rotation // 同上 
sphere.scale // ... 同上

9.讨厌的秘密
我希望这样说你能很快弄明白:就是如果你修改了,比如说,一个mesh对象的顶点属性vertices,你会发现在渲染循环中,什么都没变。为什么?因为Three.js将mesh对象的信息缓存为某种优化结构了。你真正要做的是给Three.js一个标识,告诉它如果什么东西改变了,需要重新计算缓存中的结构:
// 设置geometry为动态的,这样才允许改变其中的顶点 
sphere.geometry.dynamic = true; 
// 告诉Three.js,需要重新计算顶点 
sphere.geometry.__dirtyVertices = true; 
// 告诉Three.js,需要重新计算顶点 
sphere.geometry.__dirtyNormals = true;

还有更多的标识,但我发现这两个是最有用的。你应该仅仅标识那些确实需要实时计算的属性来避免无谓的运算开销。
10.小结
我希望这篇简单的介绍对你有所帮助。没什么能比得上卷起袖子亲手实践了,我强烈建议你这样做。在浏览器里面运行3D程序很有意思,而且使用像Three.js这样一个引擎免去了很多麻烦,让你一开始就能专注于那些真正cool的事情。

我将这篇教程的源码打包了,你可以下载下来作为一份参考。

Javascript 相关文章推荐
jquery append()方法与html()方法的区别及使用介绍
Aug 01 Javascript
前端性能优化及技巧
May 06 Javascript
浅谈js和css内联外联注意事项
Jun 30 Javascript
Ajax 加载数据 练习代码
Jan 05 Javascript
COM组件中调用JavaScript函数详解及实例
Feb 23 Javascript
JS实现多级菜单中当前菜单不随页面跳转样式而发生变化
May 30 Javascript
ajax前台后台跨域请求处理方式
Feb 08 Javascript
实现Vue的markdown文档可以在线运行的方法示例
Dec 11 Javascript
仿vue-cli搭建属于自己的脚手架的方法步骤
Apr 17 Javascript
原生JS与JQ获取元素的区别详解
Feb 13 Javascript
JavaScript undefined及null区别实例解析
Jul 21 Javascript
JS将指定的某个字符全部转换为其他字符实例代码
Oct 13 Javascript
(跨浏览器基础事件/浏览器检测/判断浏览器)经验代码分享
Jan 24 #Javascript
jQuery ajax(复习)—Baidu ajax request分离版
Jan 24 #Javascript
javascript游戏开发之《三国志曹操传》零部件开发(五)可移动地图的实现
Jan 23 #Javascript
javascript游戏开发之《三国志曹操传》零部件开发(四)用地图块拼成大地图
Jan 23 #Javascript
javascript游戏开发之《三国志曹操传》零部件开发(三)情景对话中仿打字机输出文字
Jan 23 #Javascript
javascript游戏开发之《三国志曹操传》零部件开发(二)人物行走的实现
Jan 23 #Javascript
javascript游戏开发之《三国志曹操传》零部件开发(一)让静态人物动起来
Jan 23 #Javascript
You might like
PHP面向对象编程快速入门
2006/12/14 PHP
PHP中判断变量为空的几种方法分享
2013/08/26 PHP
phpQuery占用内存过多的处理方法
2013/11/13 PHP
php中的静态变量的基本用法
2014/03/20 PHP
PHP获取服务器端信息的方法
2014/11/28 PHP
ext 代码生成器
2009/08/07 Javascript
JavaScript起点(严格模式深度了解)
2013/01/28 Javascript
变量声明时命名与变量作为对象属性时命名的区别解析
2013/12/06 Javascript
jQuery中delegate与on的用法与区别示例介绍
2013/12/20 Javascript
javascript实现阻止iOS APP中的链接打开Safari浏览器
2014/06/12 Javascript
详解JavaScript中的4种类型识别方法
2015/09/14 Javascript
详解nodejs微信jssdk后端接口
2017/05/25 NodeJs
基于js原生和ajax的get和post方法以及jsonp的原生写法实例
2017/10/16 Javascript
AngularJS使用ng-repeat遍历二维数组元素的方法详解
2017/11/11 Javascript
浅谈webpack4.x 入门(一篇足矣)
2018/09/05 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
2018/11/29 Javascript
javascript实现计算指定范围内的质数示例
2018/12/29 Javascript
简单了解前端渐进式框架VUE
2020/07/20 Javascript
Python编写屏幕截图程序方法
2015/02/18 Python
PyQt5每天必学之工具提示功能
2018/04/19 Python
深入浅析Python的类
2018/06/22 Python
Django-Rest-Framework 权限管理源码浅析(小结)
2018/11/12 Python
python反编译学习之字节码详解
2019/05/19 Python
numpy.random模块用法总结
2019/05/27 Python
Python 解决火狐浏览器不弹出下载框直接下载的问题
2020/03/09 Python
python Matplotlib数据可视化(2):详解三大容器对象与常用设置
2020/09/30 Python
Django项目在pycharm新建的步骤方法
2021/03/02 Python
C#面试题
2016/05/06 面试题
2014年教师培训的自我评价
2014/01/03 职场文书
公司办公室岗位职责
2014/03/19 职场文书
空气的环保标语
2014/06/12 职场文书
八年级数学教学反思
2016/02/17 职场文书
MySQL8.0.24版本Release Note的一些改进点
2021/04/22 MySQL
python 如何做一个识别率百分百的OCR
2021/05/29 Python
python内置进制转换函数的操作
2021/06/02 Python
浅谈Python3中datetime不同时区转换介绍与踩坑
2021/08/02 Python