在这个分步指南中,我们将使用一个基于 WebGL 的 3D 图形的框架 three.js, 创建一个 3D 版本的 Treehouse 徽标。你可以通过点击或者拖拽鼠标使相机旋转!你也可以使用鼠标滚轮进行缩放。
注意:你需要有桌面版的 Chrome,Firefox,或者 Safari 浏览器。请参阅下文的浏览器兼容性
3D 图形可能会很难,尤其是在浏览器中的 3D。像 three.js 这样的框架使 3D 变得容易一些,但官网还在建设中,并且存在一些怪癖,可能使初学者学习受到阻碍。如果你刚开始学习 3D,这篇指南将会帮助你开始使用。
虽然起初 three.js 可能看起来很复杂,但实际上,同样的东西用 WebGL 编写会需要更多的代码,主要是因为我们需要编写一个渲染引擎。所有的这些繁重的工作 three.js 都已经做好了,而又不缺灵活性。
对于本教程,你将需要桌面版的 Chrome,Firefox或者 Safari。不幸的是,WebGL 依然不能在移动版浏览器上使用,并且 IE 11 以下也不能使用。(译者注:本文写于2013年9月,浏览器兼容性可查看 caniuse.com)。
同样的,如果你使用的是 Safari,你需要先启用 WebGL。以下是在 Safari 下启用 WebGL 的方法:
这里是 caniuse.com 关于 WebGL 兼容性的矩阵图(http://caniuse.com/#search=webgl)。希望 WebGL 在将来能够得到更大的支持,因为这是非常酷的技术!
打开 http://threejs.org/。点击屏幕左边的 “下载” 链接。一旦压缩包下载完成后,打开进入 build 文件夹。在里面,你会找到一个名为 three.min.js 的文件,将它复制到你的本地开发目录下。
本教程,你还需要一个名为 OrbitControls.js 的文件,它包含在 three.js 中。文件路径如下:
threejs folder>**examples**>**js**>**controls**>**OrbitControls.js**
如果你只想获取这两个文件,本教程的示例代码中有。
JavaScript 具有称为同源策略的安全功能,意味着你不能在 JavaScript 中跨域获取资源。这会有一些问题,因为 three.js 需要加载几何,纹理和其它文件。为了规避这些问题,你需要一个本地 http 服务器,使文件来自相同的源。直接打开 index.html 是不会有效果的。 幸运的是,在 three.js FAQ中有一个很棒的关于如何使用 Python,Ruby 或者改变浏览器设置在本地运行 three.js 的指南。操作起来很简单,所以如果你正在抓狂于为何文件无法加载,请查看这一指南。
我已经创建了一个 3D 版的 Treehouse 徽标,欢迎您以学习为目的使用它(你可以在 code download 中获取模型),但如果你希望创建自己的网格,我建议你使用 Blender。这是一个很棒的 3D 建模和渲染包,免费,开源且跨平台。还有相当多的学习教材(免费或者付费的),帮助你学习建模。我第一次使用 Blender,在 1 小时内完成了我的网格。这个网格还有优化的空间(网格结构有点凌乱)但可以用于这个 demo。
为了让 Blender 中导出的网格能够在 three.js 中使用,你需要在 three.js 中安装导出器。这里是如何从 Blender 导出到 three.js的说明。
好的。一旦你有了文件夹,搭建好了本地环境,是时候开始编码了。让我们先从 HTML 开始,因为这部分简单。你只需要如下的基础模板。同时假设你的 JavaScript 存储在 js
文件夹中,所以检查你的文件路径以防万一。
<!doctype html>
`<script src="js/three.min.js">`</script>
`<script src="js/OrbitControls.js">`</script>
<script>
// <![CDATA[
// Our 3D code will go here...
// ]]>
</script>
正如我所说的,这里没有特别的地方。真正神奇的是在 script 标签中。
我们可以在外部编写 JavaScript,但由于这里的 body 中没有任何 HTML 元素,我认为使用内联 script 标签会使这个例子更加清晰。
在 script 标签中,我们希望建立一些全局变量,调用一些函数,所有这些都会在后面定义:
// Set up the scene, camera, and renderer as global variables.
// 创建全局变量场景,相机,渲染器。
var scene, camera, renderer;
init();
animate();
Three.js 使用场景来定义可以放置的事物,如几何体,灯光,相机等的区域。下面的代码中,我们开始编写初始化函数,创建一个场景。然后,将浏览器的宽高用变量 WIDTH
和 HEIGHT
保存,我们将会不止一次的需要使用它们,因此最好获取一次并保存它们。
// 上一步的全局变量在这里设置
// 创建场景。
function init() {
// 创建场景并设置场景尺寸。
scene = new THREE.Scene();
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
// 更多步骤在这里
}
接下来,我们创建 three.js 渲染器。我们可以使用 SVG 或者 canvas 渲染器,但我们希望使用 WebGL 渲染器,因为它能够利用 GPU,这会使性能有几个数量级的提升。创建渲染器之后,我们通过 body 将其添加到 DOM 中。这一步会使 three.js 在 body 中创建一个用于渲染场景的 canvas
。
// Sets up the scene.
function init() {
// 上一步代码在这里
// 创建一个渲染器,添加到 DOM 中。
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(WIDTH, HEIGHT);
document.body.appendChild(renderer.domElement);
// More code goes here next...
}
有了创景和渲染器之后,我们可以创建相机了。 [透视相机](http://threejs.org/docs/#Reference/Cameras/PerspectiveCamera)
需要几个参数。如下:
创建相机之后,我们使用 XYZ 坐标设置位置。默认为 0,0,0
但我将 Y 值设置为 6,为了让视图与网格之间有一些距离。
最后,我们需要将相机添加到场景中。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
// 创建一个相机,将模型放大一点,并将它添加到场景中。
camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 0.1, 20000);
camera.position.set(0,6,0);
scene.add(camera);
// More code goes here next...
}
目前为止一切都很好,但当网站访问者调整浏览器窗口大小时会发生什么呢?为此,我们需要添加一个事件监听器。当浏览器调整大小时,会发生几件事。首先,我们要重新获取浏览器窗口宽高,将它们保存在当前函数作用域内的变量中。然后,我们使用这些值重新设置渲染器的尺寸,并且重新计算相机的宽高比。另外,我们需要调用相机对象的 updateProjectionMatrix()
方法,以便场景能够用这些新参数进行更新。这些计算对于 3D 实时渲染环境是很昂贵的,但浏览器重新调整尺寸之后,一切都会回到正常的帧速率。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
// 创建事件监听器,将渲染器大小重新调整为浏览器窗口大小。
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
// More code goes here next...
}
|
现在是时候创建我们的场景了。通过调用 [WebGLRenderer](http://threejs.org/docs/#Reference/Renderers/WebGLRenderer)
对象的 setClearColorHex
方法,我们能够为 Treehouse 设置背景颜色为 不透明的十六进制的灰色。
接着,我们需要一个灯光才能看到我们的 3D 对象,因此我们将在场景中添加一个 [点光线](http://threejs.org/docs/#Reference/Lights/PointLight)
,设置它的位置。还有其他几种灯可以添加到场景中,因此请务必查看链接的文档。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
// 设置场景的背景颜色
renderer.setClearColorHex(0x333F47, 1);
// 创建一个灯光,设置它的位置,并添加到场景中。
var light = new THREE.PointLight(0xffffff);
light.position.set(-100,200,100);
scene.add(light);
// More code goes here next...
}
|
我们已经使用 three.js 的 JSON 导出器从 Blender 中导出网格,因此我们需要使用 [JSON加载器](http://threejs.org/docs/#Reference/Loaders/JSONLoader)
获取几何体到场景中。在加载器中使用回调函数设置网格的材质。这里,我们使用一个基础的 [LambertMaterial](http://threejs.org/docs/#Reference/Materials/MeshLambertMaterial)
将网格设置为 Treehouse 的绿色。为了保证完整性,我应该在这里指出,你看到的最终渲染出来的绿色与 Treehouse 徽标的绿色是不一样的。这是因为点光线的灯光是稍微倾斜的,但本案例中我们不需要担心。
在回调函数中,我们将几何体和材质作为参数,创建一个新的网格,并将网格添加到场景中。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
// 加载网格并添加到场景中。
var loader = new THREE.JSONLoader();
loader.load( "models/treehouse_logo.js", function(geometry){
var material = new THREE.MeshLambertMaterial({color: 0x55B663});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
// More code goes here next...
}
|
初始化函数中的最后一件事就是我们先前提到的轨道控制。这并不总是必要的,但它们是我们能够通过鼠标拖拽网格环视它。也允许通过鼠标滚轮缩放网格。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
// 添加 OrbitControl,以便我们通过鼠标左右移动。
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
// More code goes here next...
初始化函数之后,我们需要完成动画函数。这里似乎没有传统意义上的“动画”,但当相机焕然网格时我们的确需要重新绘制。
requestAnimationFrame()
是浏览器一个新的 API,将重绘委托给浏览器。它有许多的有点,但主要的优点是它能够确保不在当前选项卡时浏览器不会绘制不必要的动画。Paul Irish 写了一篇关于 requestAnimationFrame 的很棒的博客,更详细的解释了这一点。
之后,我们需要通过先前添加的相机渲染场景,然后更新轨道控制。
// Sets up the scene.
function init() {
// Code from previous steps goes here...
}
// Renders the scene and updates the render as needed.
// 在需要的时候渲染场景并更新。
function animate() {
// Read more about requestAnimationFrame at http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
// 更多requestAnimationFrame 的有关信息请阅读 http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
requestAnimationFrame(animate);
// Render the scene.
renderer.render(scene, camera);
controls.update();
}
希望查看最终的效果,请务必下载代码。尝试改变一些参数,看看会发生什么!
我认为 three.js 是一个非常棒的项目,为不是 3D 图形专家(像我)的人 开启了 WebGL 的力量。浏览器对它的支持仍在增长,但我觉得 WebGL 最实用的应用程序是用于产品展示:想象以下你舒服的从浏览器全 3D 环境下探索一辆新的汽车。
然而,低的进入门槛使得创作出酷炫的徽标或者制作音乐视频而不必花费几年时间编写渲染器或者复杂的代码成为可能。如果你更加专注,你甚至可以制作游戏并搭建世界。它使浏览器成为一个更具体验的地方,我认为这非常的棒。谁想做文档?我想制作点什么。
往期精选文章 |
---|
使用虚拟dom和JavaScript构建完全响应式的UI框架 |
扩展 Vue 组件 |
使用Three.js制作酷炫无比的无穷隧道特效 |
一个治愈JavaScript疲劳的学习计划 |
全栈工程师技能大全 |
WEB前端性能优化常见方法 |
一小时内搭建一个全栈Web应用框架 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
让你分分钟理解 JavaScript 闭包 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。