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 相关文章推荐
JavaScript CSS菜单功能 改进版
Dec 20 Javascript
ExtJS 2.0 GridPanel基本表格简明教程
May 25 Javascript
判断滚动条到底部的JS代码
Nov 04 Javascript
控制台报错object is not a function的解决方法
Aug 24 Javascript
jQuery实现简易的天天爱消除小游戏
Oct 16 Javascript
利用Angularjs和bootstrap实现购物车功能
Aug 31 Javascript
Bootstrap页面缩小变形的快速解决办法
Feb 03 Javascript
详解Angular路由 ng-route和ui-router的区别
May 22 Javascript
Vue手把手教你撸一个 beforeEnter 钩子函数
Apr 24 Javascript
vue项目实现github在线预览功能
Jun 20 Javascript
Vue面试题及Vue知识点整理
Oct 07 Javascript
浅谈Vue.js 关于页面加载完成后执行一个方法的问题
Apr 01 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
Godaddy空间Zend Optimizer升级方法
2010/05/10 PHP
PHP中文件缓存转内存缓存的方法
2011/12/06 PHP
浅谈Eclipse PDT调试PHP程序
2014/06/09 PHP
PHP实现简单实用的分页类代码
2016/04/08 PHP
Zend Framework框架实现类似Google搜索分页效果
2016/11/25 PHP
TP5框架实现上传多张图片的方法分析
2020/03/29 PHP
自定义右键属性覆盖浏览器默认右键行为实现代码
2013/02/02 Javascript
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
2013/04/07 Javascript
AngularJS HTML编译器介绍
2014/12/06 Javascript
javascript实现图像循环明暗变化的方法
2015/02/25 Javascript
JS使用oumousemove和oumouseout动态改变图片显示的方法
2015/03/31 Javascript
简单谈谈axios中的get,post方法
2017/06/25 Javascript
浅谈Express异步进化史
2017/09/09 Javascript
在 Node.js 中使用 async 函数的方法
2017/11/17 Javascript
layui 根据后台数据动态创建下拉框并同时默认选中的实例
2019/09/02 Javascript
vue-cli3.X快速创建项目的方法步骤
2019/11/14 Javascript
原生JavaScript实现换肤
2021/02/19 Javascript
python实现排序算法
2014/02/14 Python
Python的Flask框架与数据库连接的教程
2015/04/20 Python
python中range()与xrange()用法分析
2016/09/21 Python
python之Character string(实例讲解)
2017/09/25 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
python 实现读取一个excel多个sheet表并合并的方法
2019/02/12 Python
python 定时器,轮询定时器的实例
2019/02/20 Python
python中import与from方法总结(推荐)
2019/03/21 Python
用python实现一个简单计算器(完整DEMO)
2020/10/14 Python
Python类型转换的魔术方法详解
2020/12/23 Python
python opencv实现直线检测并测出倾斜角度(附源码+注释)
2020/12/31 Python
应届毕业生应聘自荐信
2013/12/07 职场文书
水污染治理工程专业求职信
2014/06/14 职场文书
教师群众路线心得体会
2014/11/04 职场文书
5.12护士节活动总结
2015/02/10 职场文书
入党自荐书范文
2015/03/05 职场文书
个人维稳承诺书
2015/05/04 职场文书
2016公司年会主持词
2015/07/01 职场文书
使用Python获取字典键对应值的方法
2022/04/26 Python