本文会讲解一下Three.js控制物体显示与隐藏的方法,主要包括以下几种方式:
- visible属性;
- layers属性。
r105
版本为例:visible属性
visible
是Object3D的属性。只有当 visible
是 true
的时候,该物体才会被渲染。任何继承 Object3D
的对象都可以通过该属性去控制它的显示与否,比如:Mesh
,Group
,Sprite
,Light
等。举个简单的例子:
// 控制单个物体的显示和隐藏
const geometry = new THREE.PlaneGeometry(1, 1) // 1*1的一个平面
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) // 红色平面
const plane = new THREE.Mesh(geometry, planeMaterial)
plane.visible = false // 不显示单个物体
scene.add(plane)
// 控制一组物体的显示和隐藏
const geometry = new THREE.PlaneGeometry(1, 1)
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const plane = new THREE.Mesh(geometry, planeMaterial)
const group = new THREE.Group()
group.add(plane)
group.visible = false // 不显示一组物体
scene.add(group)
通过后面的例子可以看出,当我们想要控制一组物体的显示与隐藏,可以把这些物体放入一个
Group
中,只通过控制 Group
的显示与隐藏即可。这块的代码逻辑是在WebGLRenderer.js的
projectObject
方法中实现的。首先,在
render
方法中调用了 projectObject
方法:this.render = function ( scene, camera ) {
// ...
projectObject( scene, camera, 0, _this.sortObjects );
// ...
}
projectObject
方法的定义如下:function projectObject( object, camera, groupOrder, sortObjects ) {
if ( object.visible === false ) return;
// 注释1:visible属性是false直接返回
// ...
var children = object.children;
// 注释2:递归应用在children上for ( var i = 0, l = children.length;
i < l;
i ++ ) {projectObject( children[ i ], camera, groupOrder, sortObjects );
// 注释2:递归应用在children上}
}
从注释1可以看出,如果
Group
的 visible
是 false
,那么就不会在 children
上递归调用,所以就能达到通过 Group
控制一组对象的显示与隐藏的效果。当
visible
是 false
的时候,Raycaster
的 intersectObject
或者 intersectObjects
也不会把该物体考虑在内。这块的代码逻辑是在 Raycaster.js:intersectObject: function ( object, recursive, optionalTarget ) {
// ...
intersectObject( object, this, intersects, recursive );
// 注释1:调用了公共方法intersectObject
// ...
},intersectObjects: function ( objects, recursive, optionalTarget ) {
// ...for ( var i = 0, l = objects.length;
i < l;
i ++ ) {intersectObject( objects[ i ], this, intersects, recursive );
// 注释1:循环调用了公共方法intersectObject}
// ...
}// 注释1:公共方法intersectObject
function intersectObject( object, raycaster, intersects, recursive ) {if ( object.visible === false ) return;
// 注释1:如果visible是false,直接return// ...
}
从注释1可以看出,如果
Group
或者单个物体的 visible
是 false
,就不做检测了。layers属性 Object3D的layers属性 是一个 Layers 对象。任何继承
Object3D
的对象都有这个属性,比如 Camera
。Raycaster
虽然不是继承自 Object3D
,但它同样有 layers
属性(r113版本以上)。和上面的
visible
属性一样,layers
属性同样可以控制物体的显示与隐藏、Raycaster
的行为。当物体和相机至少有一个同样的层的时候,物体就可见,否则不可见。同样,当物体和 Raycaster
至少有一个同样的层的时候,才会进行是否相交的测试。这里,强调了是至少有一个,是因为 Layers
可以设置多个层。Layers
一共可以表示 32
个层,0
到 31
层。内部表示为:layer | value(二进制,32位) | 说明 |
---|---|---|
0 | 00000000000000000000000000000001 | 第32位为1 |
1 | 00000000000000000000000000000010 | 第31位为1 |
2 | 00000000000000000000000000000100 | 第30位为1 |
3 | 00000000000000000000000000001000 | 第29位为1 |
... | ... | ... |
30 | 01000000000000000000000000000000 | 第2位为1 |
31 | 10000000000000000000000000000000 | 第1位为1 |
Layers
可以设置同时拥有多个层:- 可以通过
Layers
的enable
和disable
方法开启和关闭当前层,参数是上面表格中的0
到31
。 - 可以通过
Layers
的set
方法 只开启 当前层,参数是上述表格中的0
到31
。 - 可以通过
Layers
的test
的方法判断两个Layers
对象是否存在 至少一个公共层 。
0
、2
、31
层,那么内部存储的值就是 10000000000000000000000000000101
。layers
属性默认只开启 0
层。还是上面那个例子,我们看下怎么控制物体的显示和隐藏:
// 控制单个物体的显示和隐藏
const geometry = new THREE.PlaneGeometry(1, 1)
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const plane = new THREE.Mesh(geometry, planeMaterial)
plane.layers.set(1) // 设置平面只有第1层,相机默认是在第0层,所以该物体不会显示出来
scene.add(plane)
// 控制一组物体的显示和隐藏
const geometry = new THREE.PlaneGeometry(1, 1)
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const plane = new THREE.Mesh(geometry, planeMaterial)
const group = new THREE.Group()
group.layers.set(1) // 注释1: 设置group只有第一层,相机默认是在第0层,但是此时平面物体还是显示出来了?
group.add(plane)
scene.add(group)
设置单个物体的
layer
可以看到物体成功的没有显示出来。但是,当我们给 group
设置 layer
之后,发现 group
的 children
(平面物体)还是显示了出来。那么,这是什么原因呢?让我们看下源码,同样还是上面的 projectObject
方法:function projectObject( object, camera, groupOrder, sortObjects ) {if ( object.visible === false ) return;
var visible = object.layers.test( camera.layers );
// 注释1:判断物体和相机是否存在一个公共层if ( visible ) { // 注释1:如果存在,对物体进行下面的处理
// ...
}var children = object.children;
// 注释1:不管该物体是否和相机存在一个公共层,都会对children进行递归for ( var i = 0, l = children.length;
i < l;
i ++ ) {projectObject( children[ i ], camera, groupOrder, sortObjects );
}
}
从上述注释1可以看出,即使该物体和相机不存在公共层,也不影响该物体的
children
显示。这也就解释了上述为什么给 group
设置 layers
,但是平面物体还是能显示出来。从这一点上来看,layers
和 visible
属性在控制物体显示和隐藏的方面是不一样的。和
visible
属性一样,接下来我们看下 Layers
对 Raycaster
的影响。同样我还是看了 Raycaster.js 文件,但是发现根本就没有 layers
字段。后来,我看了下最新版本 r140
的 Raycaster.js:function intersectObject( object, raycaster, intersects, recursive ) {if ( object.layers.test( raycaster.layers ) ) { // 注释1:判断物体和Raycaster是否有公共层object.raycast( raycaster, intersects );
}if ( recursive === true ) { // 注释1:不管该物体和Raycaster是否有公共层,都不影响childrenconst children = object.children;
for ( let i = 0, l = children.length;
i < l;
i ++ ) {intersectObject( children[ i ], raycaster, intersects, true );
}
}
}
不同于前面,
visible
和 layers
都可以用来控制物体的显示与隐藏,visible
和 layers
只有一个可以用来控制 Raycaster
的行为,具体是哪一个生效,可以看下 Three.js的迁移指南。可以看到,从
r114
版本,废除了 visible
,开始使用 layers
控制 Raycaster
的行为:r113 → r114总结 从上面可以看出,
Raycaster honors now invisible 3D objects in intersection tests. Use the new property Raycaster.layers for selectively ignoring 3D objects during raycasting.
visible
和 layers
在控制物体显示与隐藏、Raycaster
是否进行等方面是存在差异的。当该物体的
visible
属性为 false
并且 layers
属性测试失败的时候,行为总结如下:属性 | 物体是否显示 | 该物体的children是否显示 | 该物体是否进行raycaster测试 | 该物体的children是否进行raycaster测试 |
---|---|---|---|---|
visible | 否 | 否 | 否(r114版本以下) | 否(r114版本以下) |
layers | 否 | 是 | 否(r113版本以上) | 是 |