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 相关文章推荐
兼容ie和firefox js关闭代码
Dec 11 Javascript
jMessageBox 基于jQuery的窗口插件
Dec 09 Javascript
JavaScript高级程序设计 学习笔记 js高级技巧
Sep 20 Javascript
对jQuery的事件绑定的一些思考(补充)
Apr 20 Javascript
jQuery动态设置form表单的enctype值(实现代码)
Jul 04 Javascript
js星星评分效果
Jul 24 Javascript
再分享70+免费的jquery 图片滑块效果插件和教程
Dec 15 Javascript
JavaScript判断是否为数字的4种方法及效率比较
Apr 01 Javascript
AngularJS Toaster使用详解
Feb 24 Javascript
使用node.js搭建服务器
May 20 Javascript
微信小程序自定义底部弹出框
Nov 16 Javascript
vue实现防抖的实例代码
Jan 11 Vue.js
(跨浏览器基础事件/浏览器检测/判断浏览器)经验代码分享
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与MySQL开发的8个技巧小结
2010/12/17 PHP
php中常量DIRECTORY_SEPARATOR用法深入分析
2014/11/14 PHP
PHP 与 UTF-8 的最佳实践详细介绍
2017/01/04 PHP
laravel5.4利用163邮箱发送邮件的步骤详解
2017/09/22 PHP
PHP实现验证码校验功能
2017/11/16 PHP
PHP使用DOM对XML解析处理操作示例
2019/07/04 PHP
基于jquery DOM写的类似微博发布的效果
2012/10/20 Javascript
jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween
2015/12/03 Javascript
AngularJS 过滤与排序详解及实例代码
2016/09/14 Javascript
react-native 圆弧拖动进度条实现的示例代码
2018/04/12 Javascript
Array数组对象中的forEach、map、filter及reduce详析
2018/08/02 Javascript
JS中的两种数据类型及实现引用类型的深拷贝的方法
2018/08/12 Javascript
VUE在for循环里面根据内容值动态的加入class值的方法
2018/08/12 Javascript
vue基础之模板和过滤器用法实例分析
2019/03/12 Javascript
微信小程序上传图片到php服务器的方法
2019/05/23 Javascript
通过vue写一个瀑布流插件代码实例
2019/09/07 Javascript
[59:48]DOTA2-DPC中国联赛 正赛 VG vs Magma BO3 第一场 1月26日
2021/03/11 DOTA
跟老齐学Python之Import 模块
2014/10/13 Python
Python的Scrapy爬虫框架简单学习笔记
2016/01/20 Python
python 字典 按key值大小 倒序取值的实例
2018/07/06 Python
Python实现的各种常见分布算法示例
2018/12/13 Python
解决py2exe打包后,总是多显示一个DOS黑色窗口的问题
2019/06/21 Python
python IDLE 背景以及字体大小的修改方法
2019/07/12 Python
Python中print函数简单使用总结
2019/08/05 Python
python requests抓取one推送文字和图片代码实例
2019/11/04 Python
python pygame实现挡板弹球游戏
2019/11/25 Python
Python统计时间内的并发数代码实例
2019/12/28 Python
python、PyTorch图像读取与numpy转换实例
2020/01/13 Python
细数nn.BCELoss与nn.CrossEntropyLoss的区别
2020/02/29 Python
全球性的在线商店:Vogca
2019/05/10 全球购物
应届生会计电算化求职信
2013/10/03 职场文书
求职简历自荐信
2013/10/20 职场文书
幼儿园亲子活动总结
2014/04/26 职场文书
家庭困难证明
2014/10/12 职场文书
2014年党员发展工作总结
2014/12/02 职场文书
php 获取音视频时长,PHP 利用getid3 获取音频文件时长等数据
2021/04/01 PHP