03 Scene场景及页面调试插件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30100043/article/details/80445807

场景是我们每个Three.js项目里面放置内容的容器,我们也可以拥有多个场景进行切换展示,你可以在场景内放置你的模型,灯光和照相机。还可以通过调整场景的位置,让场景内的所有内容都一起跟着调整位置。

场景的结构

之前在刚刚开始学JavaScript的基础时,我们总免不了去操作dom对象,而且我们都知道dom的结构都是树形结构的,Three.js也遵循了这样的理念,将所有可以添加到场景内的结构梳理成了一种树形的结构,方便我们能够更好的理解Three.js

我们可以把scene想象成一个bodybody内可以添加dom对象,scene内也可以添加它的3d对象,这样一层层的嵌套出来,组成了我们现在需要的项目。所以,在Three.js中,为了方便操作,将所有3d对象共同的内容抽象成了一个基类,就是THREE.Object3D

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轴保持不变
  • 第二种是使用set方法:
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依次旋转的,如果你想先旋转其他轴,我们可以添加第四项修改,有可能的情况为:YZXZXYXZYYXZ'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实现页面调试

有些时候,我们需要调整模型的位置或者大小什么的需要每次都去场景内进行调试,现在我推荐一种常用的插件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);
}

enter image description here
这样,只要我们每次都修改对象里面的值以后,都会触发updatePosition回调,来更新模型的位置。我们就实现了一个简单的案例,接下来,我列出一下经常会使用到一些方式和方法:

生成一个输入框

dat.GUI能够根据controls值的不同生成不同的操作方法,如果值的类型为字符串或者数字类型,则可以生成一个默认的输入框:

gui.add(controls, "positionX");
gui.add(controls, "positionY");
gui.add(controls, "positionZ");

enter image description here

生成一个可以滑动的滑块

使用gui.add()方法如果值为数字类型传入第三个值(最小值)和第四个值(最大值),也就是限制了值的能够取值的范围,就能够生成可以滑动的滑块:

gui.add(controls, "positionX", -1, 1); //设置了最小值和最大值,可以生成滑块
gui.add(controls, "positionY").max(1); //只设置了最大值,无法生成滑块
gui.add(controls, "positionZ").min(-1); //只设置了最小值,也无法生成滑块

enter image description here

我们还可以通过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的倍数

step

生成一个下拉框

只要按规则在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"]); //字符串类型的下拉框

下拉框

生成一个checkbox

只要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地址

猜你喜欢

转载自blog.csdn.net/qq_30100043/article/details/80445807
03
今日推荐