【THREE.js源码】THREE.js的layers

THREE.js中控制物体的显隐有多种方式,在不考虑使用材质、剖切等使用着色器的方式控制显隐的情况下,还有两种代码级别的方式,或者说通过物体的属性,可以控制显隐。

一种是使用Object3D基类的visible属性。当visible设置为true时,物体显示,当visible设置为false时,会跳过该物体的渲染,达到隐藏效果。值得注意的是,由于three.js中物体是由上下级关系的,如,整个scene为根节点,scene下面会挂载多个group,group下面还可以继续挂载group,或者挂载mesh。当父级group的visible为false时,其所有的子级也都不可见,当子节点的visible为false时,只有子节点不可见,其父级依旧是可见的。

另一种方式是使用Object3D的layers。每一个Object3D对象都有一个layers属性,值是一个THREE.Layers对象,这个对象内存储了一个1-32的掩码值,指定该物体所在的层。默认为1。对象有以下实例方法:

  • set(channal):设置掩码值。channel为0-31的整型值,对应1-32的掩码值,内部转换处理方式为
this.mask = (1 << channel | 0) >>> 0;

掩码是一个32位的整型,先使用1左移channel位,实际上就是将第channel位(右数)设置为1,其余为0。然后和0做或操作,依然保持原值。最后无符号右移0位的作用是整体右移0位,取模32,多余位将会被删除,保证最终结果是2^(0-31),只保留32位。如果channel超过31,则会回到起点,继续计数
调用set之后,可以想象为,有一个编号为1-32的格子位,下标为0-31,将对应下标的值设置为1,其余为0,表示当前的物体在这一层。

  • enable(channel):开启某一层。这个方法和set方法类似,但是使用set方法后,只有其中一位为1,其余全部为0,而enable方法是在原来的掩码基础上,继续添加一个1.其内部实现为
this.mask |= 1 << channel | 0;

1 << channel | 0的作用前面已经知道了,就是将第channel位设置为1.然后将这个结果与原来的mask进行或操作,有1为1,无1为0,即将原来的为1的位置和新的为1的位置合并起来。
enable的结果就是,开启一个新的层,同时保留原来的层。

  • enableAll():全部开启
    全部开启就是将所有的位都设置为1,其实现方式为
this.mask = 0xffffffff | 0;
  • toggle(channel):将某一位翻转,即0转为1,1转为0,实现方式为
this.mask ^= 1 << channel | 0;
  • disable(channel):将某一位关闭,即设置为0,实现方式为
this.mask &= ~(1 << channel | 0);

原理是,创建一个channel位为1的掩码,然后取反,即channel位为0,其余为1,然后与原掩码值做与操作,原掩码的非channel位不管是1值还是0值,都不变,channel为变为0,这样就关闭了channal位

  • disableAll():关闭所有层,直接将掩码全部设置为0即可
  • test(layers):做测试,与另一个layer对象的掩码做与操作,如果在某一位上同时为1,则说明两个layer共享某一层,否则在不同的层上。
  • isEnable(channel):判断某一层是否开启。与只有channel位为1的掩码做与操作,如果结果不为0,这说明channel位是开启的,即为1。
return (this.mask & (1 << channel | 0)) !== 0;

在three.js的render流程中,在进入renderObject步骤之前,会将物体的layers与camera的layers进行一次test,如果camera和物体在不同的层,则物体是不会被渲染的,从而可以实现物体的显隐。

function renderObjects( renderList, scene, camera ) {

    const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;

    for ( let i = 0, l = renderList.length; i < l; i ++ ) {

        const renderItem = renderList[ i ];

        const object = renderItem.object;
        const geometry = renderItem.geometry;
        const material = overrideMaterial === null ? renderItem.material : overrideMaterial;
        const group = renderItem.group;

        if ( object.layers.test( camera.layers ) ) {

            renderObject( object, scene, camera, geometry, material, group );

        }

    }

}
posted @ 2024-03-20 09:21  李煎饼_GISer  阅读(38)  评论(0编辑  收藏  举报