通过Three.js中的“Hello World”-旋转的立方体来认识一下Three.js中的一些最基本的构件。
构建场景
首先,需要将Three.js库的js文件引入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
然后在<body>标签上添加一个初始化的事件init():
<body onload="init()">
页面加载完成后,回调会触发设置的init事件。init函数里一共调用了五个方法,分别是:初始化渲染器、初始化场景、初始化相机、添加模型、添加动画:
//初始化函数,页面加载完成后调用
function init(){
initRenderer();
initScene();
initCamera();
initMesh();
animate();
}
使用Three.js显示创建的内容,必须需要的三大件:渲染器、相机和场景。使用相机获取到场景内显示的内容,然后再通过渲染器渲染到画布上。
创建渲染器
//初始化渲染器
function initRenderer(){
renderer = new THREE.WebGLRenderer(); //实例化渲染器
renderer.setSize(window.innerWidth, window.innerHeight); //设置宽度和高度
document.body.appendChild(renderer.domElement);//添加到DOM
}
查看initRenderer函数内的代码:
- 第一行首先实例化了一个THREE.WebGLRenderer的渲染器,这是一个基于WebGL渲染的渲染器。【Three.js向下兼容,还有CanvasRenderer,CSS2DRenderer,CSS3DRenderer和SVGRenderer,这四个渲染器分别基于canvas2D,CSS2D,CSS3D和SVG渲染的渲染器】由于作为3D渲染,WebGL渲染的效果最好并且支持的功能更多,所以在Three.js中使用的是THREE.WebGLRenderer。
- 第二行调用了一个setSize函数,用于设置需要显示的窗口大小。案例中是基于浏览器全屏显示所以设置了浏览器窗口的宽和高。
- 第三行中的renderer.domElement是在实例化渲染器的时候生成了一个canvas画布,渲染器渲染界面生成的内容都将在这个画布上显示,所以需要将这个画布添加到dom中来显示渲染的内容。
创建相机
//初始化相机
function initCamera(){
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //实例化相机
camera.position.set(0, 0, 15);
}
Three.js中有几个不同的相机,这里使用的是THREE.PerspectiveCamera,这个相机的效果是模拟人眼看到的效果,就是具有透视的效果,近大远小。
- 第一行首先实例化一个透视相机,需要设置四个值分别是视野、宽高比、近裁面和远裁面。
视野:当前相机视野的宽度,值越大,渲染出来的内容也会更多。
宽高比:默认是按照画布显示的宽高比例来设置的,如果比例设置的不对,会发现渲染出来的画面有拉伸或者压缩的感觉。
近裁面和远裁面:这个是设置相机可以看到的场景内容的范围,如果场景内的内容位置不在这两个值内的话将不会被渲染
显示到画面中。
- 第二行设置了相机的位置,通过WebGL的坐标系统,如下图所示:
WebGL坐标系统作为3D坐标,在原来2D坐标x、y轴的基础上又多了一个z轴,而z轴的方向是坐标轴向外的方向为正轴,向里的方向为负轴。
camera.position.set函数是设置当前相机的位置,函数传的三个值分别为x轴的坐标、y轴的坐标和z轴的坐标。
创建场景
//初始化场景
function initScene(){
scene = new THREE.Scene(); //实例化场景
}
初始化场景函数中仅有一行代码就可以实例化一个场景对象。场景只是作为一个容器,将需要显示的内容放在场景对象中,如果需要将一个模型放入到场景中,则可以使用scene.add方法,如:
scene.add(mesh); //添加一个网格(模型)到场景
创建模型
当拥有了渲染器、场景和相机还不能显示对应的内容,因为场景中没有内容存在,即使渲染出来也是一片漆黑,所以此时需要往场景中添加内容,使用initMesh方法来创建一个最简单的模型:
//创建模型
function initMesh(){
geometry = new THREE.BoxGeometry(2, 2, 2); // 创建几何体
material = new THREE.MeshNormalMaterial(); // 创建材质
mesh = new THREE.Mesh(geometry, material); // 创建网格
scene.add(mesh); // 将网格添加到场景
}
创建一个网格(模型)需要有两种对象:几何体和材质
- 几何体代表模型的形状,它是由固定点的位置组成,点绘制出面,面组成了模型;
- 材质是看到当前模型显示出来的效果,如显示的颜色、质感等。
- 第一行代码中实例化了一个THREE.BoxGeometry立方体的几何体对象,实例化的三个传值分别代表着立方体的长度、宽度和高度。将三个值设置为相同的数值即可渲染出一个标准的正立方体。
- 第二行代码中实例化了一个THREE.MeshNormalMaterial材质,这种材质的特点是会根据面的朝向不同而显示不同的颜色。
- 第三行代码中通过THREE.Mesh方法实例化创建了一个网格对象,THREE.Mesh实例化需要传两个参数,分别是几何体对象和材质对象。
- 第四行代码通过场景add方法,将网格添加到了场景中,因为没有跟立方体设置位置,故而立方体会默认显示在坐标的原点上面,这个时候相机就可以拍摄到创建的立方体的画面了。
添加动画
动画即多张图片一直切换就显示出来的动画效果,为了能显示动画的效果,首先需要了解requestAnimationFrame函数,这个函数是专门为了动画而出现的一个函数,其与setInterval相比的优势在于不需要设置多长时间重新渲染,而是在当前线程内js空闲时自动渲染,并且最大帧数控制在一秒60帧。所以可以循环调用这个函数来生成动画的效果:
function animate(){
requestAnimationFrame(animate); //循环调用函数
......
}
在循环调用函数的时候,每一帧都让页面重新渲染相机拍摄下来的内容:
renderer.render( scene, camera ); //渲染界面
渲染的render方法需要两个值,第一个值是场景对象,第二个值是相机对象。这意味着可以有多个相机和多个场景,并且可以通过渲染不同的场景和相机让画布上显示不同的画面。
如果仅仅是一直在渲染则立方体在原地没有动,此时需要让立方体动起来:
mesh.rotation.x += 0.06; //每一帧网格模型沿x轴旋转0.06弧度
mesh.rotation.y += 0.02; //每一帧网格模型沿y轴旋转0.02弧度
每一个实例化的网格对象都有一个rotation的值,通过设置这个值可以让立方体旋转起来,在每一帧中让立方体沿着x轴方向旋转0.06弧度、沿着y轴旋转0.02弧度(1π弧度等于180°角度)
Three.js性能检测插件
在Three.js中需要随时检测当前Three.js的性能。现在Three.js常使用的一款插件叫stats。以下介绍stats插件在Three.js中的应用方法:
- 首先需要将插件的代码在页面中引入:
<script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
- 实例化一个stats对象,然后把对象内生成的dom添加到页面当中:
//性能检测框
function initStats(){
stats = new Stats();
document.body.appendChild(stats.dom);
}
- 最后需要在requestAnimationFrame回调里面进行更新显示每次渲染的时间:
//运行动画
function animate(){
requestAnimationFrame(animate); //循环调用函数
mesh.rotation.x += 0.06; //每一帧网格模型沿x轴旋转0.06弧度
mesh.rotation.y += 0.02; //每一帧网格模型沿y轴旋转0.02弧度
stats.update(); //更新性能检测框
renderer.render( scene, camera ); //渲染界面
}
此时在场景中可以看到画布的左上角会有一个性能检测的小框:
前面的数值代表着当前每秒的渲染帧率,括号中的值是当前场景渲染的帧率范围。
使用了性能检测插件后,附上所有的源代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>three.js-01</title>
<style>
*{
margin: 0px;
padding: 0px;
}
canvas{
width: 100%;
height: 100%;
display: block;
}
</style>
<script type="text/javascript" src="js/jquery-1.10.2.js"></script>
</head>
<body onload="init()">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<script src="http://www.wjceo.com/lib/js/libs/stats.min.js"></script>
<script>
//声明部分全局变量
var renderer, camera, scene, geometry, material, mesh;
//初始化渲染器
function initRenderer(){
renderer = new THREE.WebGLRenderer(); //实例化渲染器
renderer.setSize(window.innerWidth, window.innerHeight); //设置宽度和高度
document.body.appendChild(renderer.domElement);//添加到DOM
}
//初始化场景
function initScene(){
scene = new THREE.Scene(); //实例化场景
}
//初始化相机
function initCamera(){
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //实例化相机
camera.position.set(0, 0, 15);
}
//创建模型
function initMesh(){
geometry = new THREE.BoxGeometry(2, 2, 2); // 创建几何体
material = new THREE.MeshNormalMaterial(); // 创建材质
mesh = new THREE.Mesh(geometry, material); // 创建网格
scene.add(mesh); // 将网格添加到场景
}
//运行动画
function animate(){
requestAnimationFrame(animate); //循环调用函数
mesh.rotation.x += 0.06; //每一帧网格模型沿x轴旋转0.06弧度
mesh.rotation.y += 0.02; //每一帧网格模型沿y轴旋转0.02弧度
stats.update(); //更新性能检测框
renderer.render( scene, camera ); //渲染界面
}
//性能检测框
function initStats(){
stats = new Stats();
document.body.appendChild(stats.dom);
}
//初始化函数,页面加载完成后调用
function init(){
initRenderer();
initScene();
initCamera();
initMesh();
initStats();
animate();
}
</script>
</body>
</html>