这个章节的目标是对 Three.js 的简明介绍。我们将由设置一个旋转的立方体的场景开始。在学习的过程中,如果你觉得遇到困难,需要帮助,在结尾处有一个完整的可以运行的例子可以参考。
如果你正在读这篇文档,你很可能对 Three.js
有自己的理解,并且知道它会帮你干什么,但是,不管怎样,让我们试着简洁的描述一下它吧。
Three.js
是一个可以让你在浏览器里很简单的使用 WebGL 3D
的一个类库。举个栗子,要实现一个简单的立方体,用原生 WebGL
会写成百行的 javascript
和 shader(着色器)
代码,然而用 Three.js
,你只需要写原生代码量的一小部分代码,就可以实现。
在我们开始之前,我们需要一个地方来显示它。把下面的代码,保存成计算机上的一个 HTML
文件,顺便把 three.min.js 保存到 js/
文件夹中,然后在浏览器中打开它。
<html>
<head>
<title>My first Three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script>
// Our Javascript will go here.
</script>
</body>
</html>
就这样,接下来的代码,都会在空的 <script>
标签中写。
为了使用 Three.js
显示一些东西,我们需要具备三个元素:场景(scene)、照相机(camera)、渲染器(renderer),然后我们就可以使用照相机来渲染场景了。
var scene = new THREE.Scene(); // 创建场景
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 创建透视相机,又叫远景相机(此外,还有正交相机)
var renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
下面,让我们花时间解释一下代码,我们现在创建了场景、相机、渲染器。其中相机在 Three.js
里,有透视相机(远景相机)、正交相机等多种相机,后面的文档会详细讲解。上面代码我们使用透视相机。
第一个参数是视角大小。
第二个是纵横比,就是宽度比上高度。例如使用现代的宽屏电视去看老电影,人物都被压扁了。
接下来的两个参数是近截面和远截面,定义了相机朝向方向上的可视区域,即只有在这两个值之间的区域才会被渲染,必须为正值,因为正值才是有意义的。现在你不必担心,除非在游戏制作中,调整这两个值来达到更好的性能。
然后就是渲染器。WebGLRenderer
就是黑魔法所在,为少量使用旧浏览器和由于某些原因不支持的 WebGL
的浏览器提供向后兼容。 // 这里貌似有问题,getter
和 setter
都不支持的浏览器,怎么兼容
创建渲染器,我们需要设置画布的宽高。使用我们想要展示的区域的大小,是个好主意。在这个例子中我们使用 window
的大小。对于性能密集型的游戏,可以设置(setSize)小一点的值,例如:window.innerWidth / 2
和 window.innerHeight / 2
。这并不意味着你的游戏只能填充 window
的一半,只是会变得模糊,因为放大了一倍。
// 这里有问题,除非修改css为:
html, body {
margin: 0;
height: 100%;
}
canvas {
display: block;
height: 100% !important;
width: 100% !important;
}
最后,添加渲染器的容器到 HTML
,渲染器使用 <canvas>
标签,把渲染后的场景,呈现给我们。
“一切进行的都很顺利,那么说好的立方体呢?” 下面来创建:
var geometry = new THREE.BoxGeometry( 1, 1, 1 ); // 由原来的 CubeGeometry 变为 BoxGeometry
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
创建立方体,我们需要实例化 BoxGeometry
这个类,这个对象包含一个立方体的所有点和面。后面的章节会详细介绍。
接下来我们需要材质(material)去给这个几何体(geometry)上色。Three.js
包含许多材质,这里我们使用 MeshBasicMaterial
材质,知道是材质的一种就可以了。所有材质的构造方法都以一个普通的对象(plain object)作为参数(其实就是当前材质的配置项)。为了演示简单,这里只写一个 color
属性:0x00ff00
(绿色),这和 CSS
或 PS
中的16进制颜色原理一样。
第三个要素是,我们需要一个 Mesh
,那么 Mesh
是什么东西呢?它是一个接收几何体(geometry)、材质(material),并把材质应用到几何体上的对象。我们可以实例化这个对象,并把它插入到场景中,可以任意移动它在场景中的位置。
默认情况下,我们调用 scene.add()
,我们把东西插入到了坐标系的 (0, 0, 0)
位置。这会引起照相机和立方体的重合,为了避免重合,我们可以把照相机,沿着 Z 轴向外移一些。
// 关于 Three.js
的坐标系,以及 CSS
的坐标系,是什么样的,有什么不同,后续会讲
如果你把上面的代码拷贝到我们先前创建的 HTML
文件里面,在浏览器中你什么也看不见,一篇漆黑。这是因为我们还没有渲染任何东西,为了制作旋转的动画,我们需要做一个渲染的轮询。
function render() {
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();
这段代码会每秒渲染60次我们的场景。如果你没有在浏览器中写过游戏,你会说“为什么我们不使用 setInterval
?”。事实上,我们也可是使用 setInterval
,但是 requestAnimationFrame
有许多优点。也许最大的一个优点是,当用户切到浏览器的另外一个 tab
上的时候,动画会停止,因此不会浪费宝贵的处理能力和电池寿命。
加上上面的代码,你可以在一片漆黑中看见一个绿色的立方体了。让我们旋转一下,让它变得更有意思一些。
把下面的代码加入到上面 render
函数里, renderer.render
调用之前。
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
旋转角度的更新会在每一帧执行,大概是 16.7ms 执行一次(每秒60帧)。这样,我们就得到了一个很 nice 的旋转动画。基本上,当游戏或应用正在运行的时候,你想移动和改变任何东西,都要通过这个渲染循环。当然,你也可以在 render
函数中,调用其他函数,这样就不会使 render
函数包含成百行代码了,也使程序更清晰一些。
恭喜!你已经用 Three.js
完成了第一个小应用。这个小程序很简单,但是我们必须从某个地方开始我们的 Three.js
之旅。
下面是例子的全部代码。运行调试它,使我们对它的工作原理有一个更好的理解。
<html>
<head>
<title>My first Three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var render = function () {
requestAnimationFrame( render );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
render();
</script>
</body>
</html>
Best regards!