ThreeJS DOC CN

创建场景

这个章节的目标是对 Three.js 的简明介绍。我们将由设置一个旋转的立方体的场景开始。在学习的过程中,如果你觉得遇到困难,需要帮助,在结尾处有一个完整的可以运行的例子可以参考。

什么是 Three.js?

如果你正在读这篇文档,你很可能对 Three.js 有自己的理解,并且知道它会帮你干什么,但是,不管怎样,让我们试着简洁的描述一下它吧。

Three.js 是一个可以让你在浏览器里很简单的使用 WebGL 3D 的一个类库。举个栗子,要实现一个简单的立方体,用原生 WebGL 会写成百行的 javascriptshader(着色器) 代码,然而用 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 的浏览器提供向后兼容。 // 这里貌似有问题,gettersetter 都不支持的浏览器,怎么兼容

创建渲染器,我们需要设置画布的宽高。使用我们想要展示的区域的大小,是个好主意。在这个例子中我们使用 window 的大小。对于性能密集型的游戏,可以设置(setSize)小一点的值,例如:window.innerWidth / 2window.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(绿色),这和 CSSPS 中的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!