Three.js开发指南-场景的基本组件

创建场景

首先要创建一个three.scene()对象,它是所有组件的容器。一个场景想要显示任何东西需要三种类型的组件:

  1. 相机:决定哪些东西将要在屏幕上渲染;
  2. 光源:它们会对材质如何显示,以及生成阴影时材质如何使用产生影响;
  3. 物体:它们是在相机透视图丽主要渲染的对象(方块,球体等);

1.场景的基本功能

上面的例子,查看JS可以看到:已经用THREE.Scene()对象的Scene.add(object)函数添加了一个plane(平面对象),一个Spotlight(聚光灯光源),一个AmbientLighe(环境光),renderer(渲染器),camera(摄像机)。 先用控件在场景中添加几个方块,方法在浏览器右上角,点击Addcube就可以了。现在看看它的源代码:

this.addCube = function() {
 var cubeSize = Math.ceil((Math.random() * 3));
 var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);//大小
 var cubeMaterial = new THREE.MeshLambertMaterial({
 color: Math.random() * 0xffffff
 });//随机颜色和材质
 var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
 cube.castShadow = true;//打开光阴
 cube.name = "cube-" + scene.children.length;
 // position the cube randomly in the scene
 cube.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
 cube.position.y = Math.round((Math.random() * 5));
 cube.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));
 // add the cube to the scene
 scene.add(cube);
 this.numberOfObjects = scene.children.length;
 };

这段代码很容易就读懂。方块的名字是在cube-后面加上当前场景中对象的数量。使用Scene.getChildByName(name)函数,就可以直接获取指定对象,然后执行一些操作,如移动位置等。下一个函数removeCube。顾名思义,点击就移除一个方块。这里就不多说了。代码也很容易:

this.removeCube = function() {
 var allChildren = scene.children;
 var lastObject = allChildren[allChildren.length - 1];
 if (lastObject instanceof THREE.Mesh) {
 scene.remove(lastObject);
 this.numberOfObjects = scene.children.length;
 }
 };

最后是outputObject函数,就是在控制台上显示当前对象的数量以及对象的属性。如下

__webglActive: true
__webglInit: true
_listeners: Object
_modelViewMatrix: THREE.Matrix4
_normalMatrix: THREE.Matrix3
castShadow: true
children: Array[0]
eulerOrder: (...)
frustumCulled: true
geometry: THREE.BoxGeometry
id: 12
material: THREE.MeshLambertMaterial
matrix: THREE.Matrix4
matrixAutoUpdate: true
matrixWorld: THREE.Matrix4
matrixWorldNeedsUpdate: false
name: "cube-9"
parent: THREE.Scene
position: THREE.Vector3
quaternion: THREE.Quaternion
receiveShadow: false
renderOrder: 0
rotation: THREE.Euler
rotationAutoUpdate: true
scale: THREE.Vector3
type: "Mesh"
up: THREE.Vector3
useQuaternion: (...)
userData: Object
uuid: "AE91C7B0-D3B1-4BD3-85B5-EA1D611F3F44"
visible: true
__proto__: THREE.Mesh

到目前位置,跟场景相关的函数:

  1. Scene.Add():从场景中添加物体;
  2. Scene.Remove():从场景中移除物体;
  3. Scene.children():获取场景中所有子对象列表;
  4. Scene.getChildByName():利用name属相,获取指定对象的列表。
  5. Scene.traverse():Scene.traverse()函数,这是一个辅助函数,我们可以将一个函数作为参数传递给它,这个传递来的函数将会在场景的每一个子对象上调用一次。如下更新每一个方块的旋转弧度。
    scene.traverse(function(e) {
     if (e instanceof THREE.Mesh && e != plane) {
     e.rotation.x += controls.rotationSpeed;
     e.rotation.y += controls.rotationSpeed;
     e.rotation.z += controls.rotationSpeed;
     }
     });

场景中的雾化效果

打开雾化的效果,只需要在定义完的场景中添加下面的代码即可:

scene.fog = new THREE.Fog(0xffffff,0.015,100);

意思是白色的雾化效果(Oxffffff),0.015是near(近处)属相的值,100是far(远处)属相的值。当然也可以只设置两个值,这时第二个值就不表示nearl了,而是表示浓度。

场景中的材质覆盖属性

场景的overrideMaterial属性,该属性用来设置所有物体的材质。

scene.overrideMaterial = new THREE.MeshLambertMaterial({color:0xffffff});

用这种材质,可以创建一些不发光的物体,而且这些物体可以对你添加到场景的光源作出反应。
总结
函数/属性:描述

  • add(object):在场景中添加对象。或者创建对象组。
  • remove(object):如果你在场景中引用了一个对象,那me就可以用这么方法从场景中删除它。
  • children:返回一个场景中所有子对象,包括相机和光源。
  • getChildrenByName(name):创建对象时,可以通过name属性指定对象的名字,这个方法就是返回指定的对象。
  • traverse(function):children属相返回场景中所有子列表。通过traverse函数可以传入一个回调函数访问这些子对象。
  • fog:通过该属性可以设置场景的雾化效果。
  • overrideMaterial :通过这个属性,强制场景中的所有物体都使用相同的材质。

2.使用几何和网络对象

几何对象的属性和函数
three.js库中的geometry和其他大多数三维库中的一样,基本上都是三维空间的点集,以及有这些点连接起来的面。举例来说,一个方块:8个顶点,每个顶点可以定义为x,y,z坐标的组合。一个方块有6个面,称之为face(有三个点构成),因此一个面由两个face。如下:

var vertices = [
 new THREE.Vector3(1, 3, 1),
 new THREE.Vector3(1, 3, -1),
 new THREE.Vector3(1, -1, 1),
 new THREE.Vector3(1, -1, -1),
 new THREE.Vector3(-1, 3, -1),
 new THREE.Vector3(-1, 3, 1),
 new THREE.Vector3(-1, -1, -1),
 new THREE.Vector3(-1, -1, 1)
 ];
 var faces = [
 new THREE.Face3(0, 2, 1),
 new THREE.Face3(2, 3, 1),
 new THREE.Face3(4, 6, 5),
 new THREE.Face3(6, 7, 5),
 new THREE.Face3(4, 5, 1),
 new THREE.Face3(5, 0, 1),
 new THREE.Face3(7, 6, 2),
 new THREE.Face3(6, 3, 2),
 new THREE.Face3(5, 7, 0),
 new THREE.Face3(7, 2, 0),
 new THREE.Face3(1, 3, 4),
 new THREE.Face3(3, 6, 4),
 ];

我们可以用顶点和面来创建直接的几何体,并用它来创建网络。当改变一个顶点的位置的时候,这个方块会立马正确的渲染出来。这归结于如下代码:

function render() {
 stats.update();
 var vertices = [];
 for (var i = 0; i < 8; i++) {
 vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
 }
 mesh.children.forEach(function(e) {
 e.geometry.vertices = vertices;
 e.geometry.verticesNeedUpdate = true;
 e.geometry.computeFaceNormals();
 });
 // render using requestAnimationFrame
 requestAnimationFrame(render);
 renderer.render(scene, camera);
 }

将geometry的verticesNeedUpdate属相设置为true,就是告诉顶点需要更新。最后调用computeFaceNormals函数重新计算侧面,从而完成整个模型的更新。

最后通过clone函数我们可以创建一个geomerty对象的副本,并赋予不同的材质。现在下面代码

var materials = [
 //设置面的颜色和透明度
 new THREE.MeshLambertMaterial({
 opacity: 0.6,
 color: 0x44ff44,
 transparent: true
 }),
//设置线框和它颜色
 new THREE.MeshBasicMaterial({
 color: 0x000000,
 wireframe: true
 })
 ];
//然后调用下面方法达到目的
 var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);
//为所有子对象添加阴影
 mesh.children.forEach(function(e) {
 e.castShadow = true
 });

现在看clone函数就明白多了。如下:

 this.clone = function() {
 var clonedGeometry = mesh.children[0].geometry.clone();
 var materials = [
 new THREE.MeshLambertMaterial({
 opacity: 0.6,
 color: 0xff44ff,
 transparent: true
 }),
 new THREE.MeshBasicMaterial({
 color: 0x000000,
 wireframe: true
 })
 ];
 var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry, materials);
 mesh2.children.forEach(function(e) {
 e.castShadow = true
 });
 mesh2.translateX(5);
 mesh2.translateZ(5);
 mesh2.name = "clone";
 scene.remove(scene.getChildByName("clone"));
 scene.add(mesh2);
 }

网络对象的函数和属性
函数/属性:描述

  • position(位置):决定该对象相对其父对象的位置。多数情况下父对象为THREE.Scene()对象。
  • rotation(旋转):通过这个属性你可以设置对象绕任何一个轴的旋转弧度。
  • scale(比例):通过这个属性你可以沿着x,y,z轴缩放对象。
  • translateX:沿X轴将对象平移指定距离。
  • translateY:沿Y轴将对象平移指定距离。
  • translateZ:沿Z轴将对象平移指定距离。

设置对象的x,y,z坐标的方法有3种:如下

//第一种
 a.position.x = 0;
 a.position.y = 0;
 a.position.z = 0;
 //第二种
 a.position.set(10,3,1);
 //第三种
 a.position = new THREE.Vector3(10,3,1);

前面说到THREE.SceneUtils.createMultiMaterialObject函数创建一个多材质对象,这个函数返回的不是一个单独的网络对象,而是一个对象组,其中每个网格的几何体都是一样的,但是材质不一样。

如上dome。设置旋转,缩放等其他属性和设置坐标一样有3种方法。举例:

//第一种
 a.rotation.x = 0.5*Math.PI;
 //第二种
 a.rotation.set(0.5*Math.PI);
 //第三种
 a.rotation= new THREE.Vector3(0.5*Math.PI);

3.选择合适的相机

three.js库里有两种相机:正投影相机和透视相机。 透视相机:透视视图,也就是最自然的视图。举例相机越远的物体越小。 正投影相机:网络中所有网格渲染的尺寸是一样大小。

点击switchCamera就可以切换相机,其原理如下:

this.switchCamera = function () {
 if (camera instanceof THREE.PerspectiveCamera) {
 camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16, -200, 500);
 camera.position.x = 120;
 camera.position.y = 60;
 camera.position.z = 180;
 camera.lookAt(scene.position);
 this.perspective = "Orthographic";
 } else {
 camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
 camera.position.x = 120;
 camera.position.y = 60;
 camera.position.z = 180;
 camera.lookAt(scene.position);
 this.perspective = "Perspective";
 }
 };

首先,先看THREE.PerspectiveCamera对象。它接受如下参数:

参数:描述

fov(视场):fov表示视场filed of view。这是从相机位置能够看到部分场景。推荐默认值为45。

aspect(长宽比):这是渲染街结果输出区的横向长度和纵向长度的比值。推荐默认值:window.innerWidth/window.innerHeight

near(近面):near属性定义是three.js库从距离相机多近的地方开始渲染场景。推荐默认值:0.1

far(远面):far属性定义是相机可以从它所处的位置看多远。推荐默认值:1000

其次,再看THREE.OrthographicCamera对象。它接受如下参数:

参数:描述

left(左边界):可视范围的左平面,也就是可渲染部分的左侧边。如果把这个值设置为-100,那么你不会看到任何比这个左侧边界更远的物体。

right(右边界):跟left一样,界面的右侧,比这个右侧边界更远的对象不会渲染。

top(上边界):可被渲染空间的最上面。

bottom(下边界):可被渲染空间的最下面。

near(近面):基于相机所在的位置,从这一点开始渲染场景。

far(远面):基于相机所在的位置,一直渲染到场景中的这一点。

一般来讲,相机会指向场景的中心,用坐标表示就是(0,0,0),我们可以通过lookAt()来设置相机所看的位置,如下:

camera.lookAt(new THREE.Vevtor3(x,y,z))

上面的例子,场景并不是真的在移动,而是相机在拍摄不同的地方(屏幕中央的那个红点),其效果就是场景在从左向右移动。