赞
踩
场景是我们每个Three.js
项目里面放置内容的容器,我们也可以拥有多个场景进行切换展示,你可以在场景内放置你的模型,灯光和照相机。还可以通过调整场景的位置,让场景内的所有内容都一起跟着调整位置。
之前在刚刚开始学JavaScript
的基础时,我们总免不了去操作dom
对象,而且我们都知道dom
的结构都是树形结构的,Three.js
也遵循了这样的理念,将所有可以添加到场景内的结构梳理成了一种树形的结构,方便我们能够更好的理解Three.js
。
我们可以把scene
想象成一个body
,body
内可以添加dom
对象,scene
内也可以添加它的3d
对象,这样一层层的嵌套出来,组成了我们现在需要的项目。所以,在Three.js
中,为了方便操作,将所有3d
对象共同的内容抽象成了一个基类,就是THREE.Object3D
。
为了方便操作,Three.js
将每个能够直接添加到场景内的对象都继承至一个基类-THREE.Object3D
,以后我们将继承至这个基类的对象称为3d
对象,判断一个对象是否是继承至THREE.Object3D
,我们可以这么判断:
obj instanceof THREE.Object3D
//继承至返回 true 否则返回false
这个基类上封装了一些我们常用的方法,下面我们分别介绍相关的内容:
3d
对象:scene.add(mesh); //将网格添加到场景
这是我们上一节使用的将一个立方体添加到场景内显示。
这个方法不光能够在场景内使用,而且也可以将一个3d
对象添加到另一个3d
对象里面:
parent.add(child);
3d
对象:获取一个3d
对象可以使用getObjectByName
通过3d
对象的name
值进行获取,在获取前我们首先要设置当前3d
对象的name
值:
object3D.name = "firstObj";
scene.add(object3D);
scene.getObjectByName("firstObj"); //返回第一个匹配的3d对象
另一种方式就是通过使用getObjectById
通过3d
对象的id
值进行获取,3d
对象的id
值是只能读取的,它是在添加到场景时,按1,2,3,4,5....
的顺序默认生成的一个值,无法自定义:
scene.getObjectById(1); //返回id值为1的3d对象
3d
对象如果我们想隐藏一个3d
对象,而不让它显示,可以通过设置它的visible
的值:
mesh.visible = false; //设置为false,模型将不会被渲染到场景内
remove
方法进行删除:scene.add(mesh); //将一个模型添加到场景当中
scene.remove(mesh); //将一个模型从场景中删除
每一个3d
对象都有一个children
属性,这是一个数组,里面包含所有添加的3d
对象:
scene.add(mesh1);
scene.add(mesh2);
console.log(scene.children);
// [mesh1, mesh2]
如果想获取3d
对象下面所有的3d
对象,我们可以通过traverse
方法进行获取:
mesh1.add(mesh2); //mesh2是mesh1的子元素
scene.add(mesh1); //mesh1是场景对象的子元素
scene.traverse(fucntion(child){
console.log(child);
});
//将按顺序分别将mesh1和mesh2打印出来
3d
对象的父元素每个3d
对象都有一个父元素,可以通过parent
属性进行获取:
scene.add(mesh); //将模型添加到场景
console.log(mesh.parent === scene); //true
3d
对象的位置,大小和转向前面介绍了一下场景的结构以及场景的3d
对象添删查,下面,我们接着介绍对场景内的模型的一些操作。
我们可以通过设置模型的position
属性进行修改模型的当前位置,具体方法有以下几种:
mesh.position.x = 3; //将模型的位置调整到x正轴距离原点为3的位置。
mesh.position.y += 5; //将模型的y轴位置以当前的位置向上移动5个单位。
mesh.position.z -= 6;
mesh.position.set(3, 5, -6); //直接将模型的位置设置在x轴为3,y轴为5,z轴为-6的位置
Three.js
的模型的位置属性是一个THREE.Vector3
(三维向量)的对象(后期教程会讲解相关对象),我们可以直接重新赋值一个新的对象:mesh.position = new THREE.Vector3(3, 5, -6); //上面的设置位置也可以通过这样设置。
模型导入后,很多情况下都需要调整模型的大小。我们可以通过设置模型的scale
属性来调整大小:
mesh.scale.x = 2; //模型沿x轴放大一倍
mesh.scale.y = 0.5; //模型沿y轴缩小一倍
mesh.scale.z = 1; //模型沿z轴保持不变
mesh.scale.set(2, 2, 2); //每个方向等比放大一倍
mesh.scale.set(0.5, 0.5, 0.5); //每个方向等比缩小一倍
scale
属性也是一个三维向量,我们可以通过赋值的方式重新修改:mesh.scale = new THREE.Vector3(2, 2, 2); //每个方向都放大一倍
很多情况下,我们需要对模型进行旋转,以达到将模型显示出它需要显示的方位,我们可以通过设置模型的rotation
属性进行旋转(注意:旋转Three.js
使用的是弧度不是角度):
mesh.rotation.x = Math.PI; //模型沿x旋转180度
mesh.rotation.y = Math.PI * 2; //模型沿y轴旋转360度,跟没旋转一样的效果。。。
mesh.rotation.z = - Math.PI / 2; //模型沿z轴逆时针旋转90du
set
方法重新赋值:mesh.rotation.set(Math.PI, 0, - Math.PI / 2); //旋转效果和第一种显示的效果相同
正常模型的旋转方式是按照XYZ
依次旋转的,如果你想先旋转其他轴,我们可以添加第四项修改,有可能的情况为:YZX
,ZXY
,XZY
,YXZ'
和 ZYX
。
mesh.rotation.set(Math.PI, 0, - Math.PI / 2, "YZX"); //先沿y轴旋转180度,再沿z轴旋转0度,最后沿x轴逆时针旋转90度
rotation
属性其实是一个欧拉角对象(THREE.Euler
)欧拉角后面会讲解到,我们可以通过重新赋值一个欧拉角对象来实现旋转调整:mesh.rotation = new THREE.Euler(Math.PI, 0, - Math.PI / 2, "YZX");
有些时候,我们需要调整模型的位置或者大小什么的需要每次都去场景内进行调试,现在我推荐一种常用的插件dat.GUI
,接下来,我们将一起看看如何使用这一款插件:
cdn
的连接:<script src="https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js"></script>
controls = {
positionX:0,
positionY:0,
positionZ:0
};
dat.GUI
对象,将需要修改的配置添加对象中,并监听变化回调:gui = new dat.GUI();
gui.add(controls, "positionX", -1, 1).onChange(updatePosition);
gui.add(controls, "positionY", -1, 1).onChange(updatePosition);
gui.add(controls, "positionZ", -1, 1).onChange(updatePosition);
function updatePosition() {
mesh.position.set(controls.positionX, controls.positionY, controls.positionZ);
}
这样,只要我们每次都修改对象里面的值以后,都会触发updatePosition
回调,来更新模型的位置。我们就实现了一个简单的案例,接下来,我列出一下经常会使用到一些方式和方法:
dat.GUI
能够根据controls
值的不同生成不同的操作方法,如果值的类型为字符串
或者数字类型
,则可以生成一个默认的输入框:
gui.add(controls, "positionX");
gui.add(controls, "positionY");
gui.add(controls, "positionZ");
使用gui.add()
方法如果值为数字类型传入第三个值(最小值)和第四个值(最大值),也就是限制了值的能够取值的范围,就能够生成可以滑动的滑块:
gui.add(controls, "positionX", -1, 1); //设置了最小值和最大值,可以生成滑块
gui.add(controls, "positionY").max(1); //只设置了最大值,无法生成滑块
gui.add(controls, "positionZ").min(-1); //只设置了最小值,也无法生成滑块
我们还可以通过step()
方法来限制每次变化的最小值,也就是你增加或者减少,必须都是这个值的倍数:
gui.add(controls, "positionX", -10, 10).step(1); //限制必须为整数
gui.add(controls, "positionY", -10, 10).step(0.1); //每次变化都是0.1的倍数
gui.add(controls, "positionZ", -10, 10).step(10); //每次变化都是10的倍数
只要按规则在gui.add()
的第三个值传入一个对象或者数组,dat.GUI
就能够自动匹配生成一个下拉框:
controls = {
positionX:0,
positionY:false,
positionZ:"middle"
};
gui = new dat.GUI();
gui.add(controls, "positionX", {left:-10, middle:0, right:10}); //数字类型的下拉框
gui.add(controls, "positionY", [true, false]); //布尔值类型的下拉框
gui.add(controls, "positionZ", ["left", "middle", "right"]); //字符串类型的下拉框
只要controls
的值是一个布尔类型的,使用gui.add()
方法就可以生成一个复选框:
controls = {
positionX:true,
positionY:false,
positionZ:false
};
gui = new dat.GUI();
gui.add(controls, "positionX");
gui.add(controls, "positionY");
gui.add(controls, "positionZ");
如果controls
的值为一个函数function,dat.GUI
会自动生成一个可以点击的按钮,当按下时就会触发这个函数事件:
controls = {
positionX:function () {},
positionY:function () {},
positionZ:function () {}
};
gui = new dat.GUI();
gui.add(controls, "positionX");
gui.add(controls, "positionY");
gui.add(controls, "positionZ");
我们可以在后面使用name()
事件进行设置显示的名称:
gui.add(controls, "positionX", -1, 1).name("x轴");
gui.add(controls, "positionY", -1, 1).name("y轴");
gui.add(controls, "positionZ", -1, 1).name("z轴");
实现颜色选择框首先需要值为一种正常的格式,比如css的颜色样式或者RGB
格式,然后再使用gui.addColor()
的方法添加:
controls = {
positionX:"#cccccc", //css样式
positionY: [0, 255, 255], //RGB格式
positionZ: [0, 255, 255, 0.6] //RGBA格式
};
gui = new dat.GUI();
gui.addColor(controls, "positionX").name("x轴");
gui.addColor(controls, "positionY").name("y轴");
gui.addColor(controls, "positionZ").name("z轴");
dat.GUI
给我们提供了监听事件回调的方法onChange()
,如果值变化就能够触发函数回调:
gui.add(controls, "positionX", -1, 1).onChange(updatePosition);
gui.add(controls, "positionY", -1, 1).onChange(updatePosition);
gui.add(controls, "positionZ", -1, 1).onChange(updatePosition);
function updatePosition() {
mesh.position.set(controls.positionX, controls.positionY, controls.positionZ);
}
我们可以使用gui.addFolder()
方法来创建分组
gui = new dat.GUI();
var first = gui.addFolder("第一个分组"); //创建第一个分组
first.add(controls, "positionX", -1, 1).onChange(updatePosition);
first.open();
var two = gui.addFolder("第二个分组");
two.add(controls, "positionY", -1, 1).onChange(updatePosition);
two.add(controls, "positionZ", -1, 1).onChange(updatePosition);
到这里,基本用法也大概讲全, 不要求一次吃掉,只希望用到的时候,还能够回来翻到自己需要的内容。
最后上传一下这一节案例的结构代码:github地址
懒人直接查看效果页面:github地址
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。