前言
最近在学习Three.js,就记录一下自己的学习过程,做做笔记。本次笔记主要介绍如何使用three.js实现一个三维场景。主要实现的功能有:
- 创建一个Three.js场景
- 使用材质、光源、动画来完善第一个场景
- 引入辅助库以统计和控制场景
一、 创建一个Three.js场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Three 1-1</title>
<!-- 引入three.js -->
<script src="Three/three.js"></script>
<style>
/* 使得场景占满全屏 */
body{
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- 承载输出的div -->
<div id="WebGL-output">
</div>
<script>
var scene;
var camera;
var renderer;
function init(){
//场景
var scene = new THREE.Scene();
//相机
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
//渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
//添加平面
var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
var planeMaterial = new THREE.MeshBasicMaterial({
color:0xcccccc});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
//设置平面的位置
plane.rotation.x = -0.5*Math.PI;
plane.position.set(15,0,0);
scene.add(plane);
//添加一个立方体
var cubeGeometry = new THREE.BoxGeometry(4,4,4,4,4,4);
var cubeMaterial = new THREE.MeshBasicMaterial({
color:0xff0000, wireframe: true});
var cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
cube.position.set(-10,10,5);
scene.add(cube);
//添加一个球
var sphereGeometry = new THREE.SphereGeometry(4,20,20);
var sphereMaterial = new THREE.MeshBasicMaterial({
color:0x7777ff, wireframe: true});
var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
sphere.position.set(20,5,2);
scene.add(sphere);
camera.position.set(-30,40,20);
camera.lookAt(scene.position);
document.getElementById('WebGL-output').appendChild(renderer.domElement);
renderer.render(scene,camera);
}
window.onload = init;
</script>
</body>
</html>
在创建一个三维场景时,场景(scene)、相机(camera)、渲染器(renderer)是缺一不可的。
var scene = new THREE.Scene();
定义了场景,场景是一个容器,其实更多的时候我将其想象成一个舞台。没有舞台也就不会有光鲜亮丽的演员呈现在我们眼前。
var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
定义了相机,相机则决定了我们能看到场景中的什么东西,我会将其想象为你在电视前看一场直播,摄像机拍到了什么你便在电视荧幕前看到了什么。
var renderer = new THREE.WebGLRenderer();
定义渲染器,渲染器则会根据相机的参数设置来计算对象在浏览器中会渲染成什么样子。我会将其想象成不同的设置的摄像机拍出来的照片效果自然不同。
以上的想象比喻只是为了自己可以更加直观的理解,如有什么不妥之处,望大家提出来一起进步。以上的代码运行起来效果如下:
二、添加材质、光源和阴影效果并且让物体动起来
上一个实例中只是简单的添加的在一个平面上添加了一个立方体和一个球。并没有任何的效果,看起来比较单调。接下来我们将物体的材质进行修改,并添加光源使其产生阴影效果,并在渲染器进行渲染时让立方体和球体动起来。先上代码吧:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Three 1-2</title>
<!-- 引入three.js-->
<script src="Three/three.js"></script>
<style>
/* 使得场景占满全屏 */
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- 承载输出的div -->
<div id="WebGL-output"></div>
<script>
var scene;
var camera;
var renderer;
var stats;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.shadowMapEnabled = true; //告诉渲染器渲染阴影
renderer.setSize(window.innerWidth, window.innerHeight);
//添加一个平面、球、立方体
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0xcccccc
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
//设置平面的位置
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(15, 0, 0);
scene.add(plane);
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
var cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
cube.position.set(-4, 3, 0);
cube.castShadow = true;
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777ff
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(20, 0, 2);
sphere.castShadow = true;
scene.add(sphere);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
// 添加点电源
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
document.getElementById('WebGL-output').appendChild(renderer.domElement);
renderScene();
var step = 0;
function renderScene() {
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
step+=0.04;
sphere.position.x = 10+(5*(Math.cos(step)));
sphere.position.y = 10+(5*Math.abs(Math.sin(step)));
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
}
window.onload = init;
</script>
</body>
</html>
-
我们将物体的材质从MeshBasicMaterial换成了MeshLambertMaterial。因为在Three.js中MeshLambertMaterial
和MeshPhongMaterial这两种材质在渲染时才会对光源产生反应。 -
改变材质之后还需要添加光源才能产生阴影,在此示例中我们添加了一个聚光灯光源
var spotLight = new THREE.SpotLight(0xffffff);
随着之后的学习也会学习Three.js中的光源,到时候再进行详细的介绍。
但是改变了材质,添加了光源之后此时运行程序你会发现并没有阴影效果;我们还需要进行以下操作:
1.告诉渲染器在渲染物体的时候渲染阴影:renderer.shadowMapEnabled = true;
2.指定哪一个物体接收阴影,在本示例中,接收阴影的即为平面:plane.receiveShadow = true;
3.指定哪一个物体产生阴影,在本示例中,产生阴影的即为立方体和球体:cube.castShadow = true; sphere.castShadow = true;
这时就会有了阴影的效果了。
接下来我们将会给立方体和球添加一个简单的动画效果。我们需要引入requestAnimationFrame() 方法,该方法可以使浏览器在特定的时间间隔重新渲染场景。我们在不断的进行渲染requestAnimationFrame(renderScene);
,在渲染时不断地改变物体的位置就可以产生一种动画效果。
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
step+=0.04;
sphere.position.x = 10+(5*(Math.cos(step)));
sphere.position.y = 10+(5*Math.abs(Math.sin(step)));
本示例中让立方体不断的旋转,小球则是改变其x,y的值让其产生弹跳的效果。
三、引用stats和dat.gui辅助库对场景进行统计和控制
stats主要用于检测动画运行时的帧数,在动画运行时,该库可以在一个图片中显示画面每秒传输帧数。使用步骤如下:
- 引入stats
<script src="dat.GUI/dat.gui/build/dat.gui.js"></script>
- 添加div元素用于显示统计图形
<div id="stasOut"></div>
- 初始化统计对象,并添加到div中
function initStats(){
stats = new Stats();
stats.setMode(0);
stats.domElement.style.position='absolute';
stats.domElement.style.top='0px';
stats.domElement.style.left='0px';
document.getElementById('stasOut').appendChild(stats.domElement);
return stats;
}
- 在init中调用initStats();并在渲染时更新
stats.updata();
dat.GUI可以使得我们能够在运行期间修改属性,找到最适合的值。其使用步骤如下:
-
引入dat.gui
<script src="dat.GUI/dat.gui/build/dat.gui.js"></script>
-
定义一个对象,申明可以通过dat.gui改变的属性值
var controls = new function(){
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.04;
}
- 将这个javaScript对象传给dat,gui对象,并设置属性的取值范围
var gui = new dat.GUI();
gui.add(controls,'rotationSpeed',0,0.5).step(0.01);
gui.add(controls,'bouncingSpeed',0,0.5).step(0.01);
在渲染时直接引用这些属性。当改变这些属性值是,就会产生不同的效果
function renderScene() {
stats.update();
cube.rotation.x += controls.rotationSpeed;
cube.rotation.y += controls.rotationSpeed;
cube.rotation.z += controls.rotationSpeed;
step+= controls.bouncingSpeed;
sphere.position.x = 10+(5*(Math.cos(step)));
sphere.position.y = 10+(5*Math.abs(Math.sin(step)));
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
按照使用步骤将代码添加到第二部分的代码中,效果如下: