Multiple Scenes vs Layers

Which, when, why and where?

Other than rendering multiple viewports, which is better / best practice? To place objects and lights on different scenes or on different layers?

Also which would give the best performance?

scene1.add(cube)
scene1.add(directionalLight)
scene2.add(sphere)
scene2.add(pointLight1);

// in renderloop
renderer.render(scene1, camera);
renderer.autoClear = false;
renderer.render(scene2, camera);

vs

camera.layers.enable(0);
camera.layers.enable(1);

directionalLight.layer.enable(0);
scene.add(directionalLight)
cube.layers.set(0);
scene.add(cube)

pointLight1.layer.enable(1);
scene.add(pointLight1);
sphere.layers.set(0);
scene.add(sphere)

// No need to mess with render loop
1 Like

I might be a bit out of the loop but here are a few thoughts:

Layers allow you to not mess with the render loop, but i think they can potentially do more work. I’m not sure exactly how it works, but i assume the renderer needs to visit every object to see if it belongs to the layer. If you do your second example, i think that this will happen.

1 Like

I think the layers may be more optimised…

As there are layers and a single renderer loops through the objects in that scene handling layers in 1 itteration so to speak, where as multiple scenes the renderer has to render the whole viewport/camera multiple times.

You might be right, for your use case, layers should do fine, but if you want to render things in a certain order, you might run into what i described.

Here’s a small piece of wisdom:

If you add a light to the scene - it will cause most of the shaders of things on that scene to re-compile. If you remove a light - same thing.

If you want to have different light setup - I recommend you use different scenes entirely for different setups. Also, make sure that scenes don’t share any materials.

Layers are actually slower than multiple scenes, even if you ignore shader re-compilation issue. Because layers don’t prevent looping, you still end up with the renderer looping over all objects to filter out what’s visible, the test to see if an object belongs to any visible layer is super fast, but you still check that object, even if it’s not assigned to any visible layer.

3 Likes

Is there any reason you would ever want to use a layer?

1 Like

What do you mean by that? As in, do you find the feature useful at all?

Yes, exactly. At some point, hopefully we’ll be able to do selective lighting via layers. Until then, are there any use cases where layers are better than multiple scenes?

I guess they are a little easier to use, is that the only benefit?

Well, yeah i think it’s convenience first and foremost.

Forget about selective lighting for a second, say that for some reason you need to render the same scene twice, but the second time with some overlay effect. So all your meshes, need to have the same matrixWorld as they did in the first pass.

if you could do something like

const myMasterNode = new Object3D()

const slaveA = new Object3D()
const slaveB = new Object3D()
slaveA.matrixWorld = myMasterNode.matrixWorld
slaveB .matrixWorld = myMasterNode.matrixWorld

you could run

masterScene.update()
render(sceneA, camera)
render(sceneB, camera)

But alas, you can’t reference other node’s matrices.

Personally - no, there is no reason I would use three.js layers in their current incarnation. I can imagine some might use them to hide things temporarily, it can be “convenient” perhaps.

In meep, I use a concept of layers, but it is more akin to using separate groups at the top level of a scene and there’s a full-brown visibility set solution on top of that, so it’s a fundamentally different implementation approach.

I use that version of layers to aggregate various renderable categories, such as trails, particles, terrain and foliage. Using layers this way, I am able to partition the scene in such a way that a system responsible for particle rendering can safely work with it’s renderable objects within the scene while being completely unaware of the rest of the renderable objects. It’s information hiding for the most part.

Another aspect is visibility culling, different renderable objects categories can have different visibility filters.

3 Likes

Could you just disable matrix auto update for the second pass?

You’d have to copy it? ie meshInSomeScene.matrixWorld.copy(meshInAnotherScene.matrixWorld), i think though at this point the other scenes would serve more as groups, you could reference the meshes from the main object in userData for example.

Oh, now I get you. But if you are rendering the same scene twice, then why use two scenes? If you need to hide some of the objects in the scene for the second pass, set material.colorwrite=false for whatever you need to hide.

I do this for a selective bloom pass on a pretty complex scene:

function setColorWrite(model, bool) {
  model.traverse(child => {
    if (child.isMesh && child.userData.bloom !== true) {
      child.material.colorWrite = bool;
    }
  });
}

Any mesh with userData.bloom = true will be rendered in the bloom pass. It doesn’t seem to have any kind of noticeable performance hit.

2 Likes

But what do you do when you have more methods such as this? Say the bool changes along with 3 others, you’d traverse the same scene in the same frame. While, if you have to traverse it at least once to figure out the layer stuff, or whats visible and what not, you could pull from this state and do it all in one pass. Still, this traversal from your snippet is cheap, but you can’t do that with a matrix i think, even if you assign it like that internally the setter calls copy(), yielding another loop and increments.

I’m still thinking though, layers may potentially avoid this overhead and achieve the same use case, if say you’re rendering certain meshes in additional passes. With a named set, you could call render(Object3D[], camera) but since it has to be a scene, you can’t just reparent them. If you keep a copy of a mesh and are trying to sync it, it might be easier and even faster to render just one more time with a certain layer.

Can you give some concrete examples of the kind of methods you mean?

You mean you’d have to traverse the scene three times to check for three different things? I’d say that’s just a sign you need to refactor your app.

I don’t really understand what you’re getting at here. If you are rendering a scene two times without changing object transforms, why would you need to copy matrices? The matrices are already set. By the same logic, why would you use two scenes?

Maybe it would help me understand if you provide an example of when you need to do this?

For the selective bloom pass I did test two methods - mesh.visible, and material.colorWrite.
Setting visibility gives a different result since the objects are not written to the depth buffer, with color write they are. Both are nice results, depending on what you want - e.g. if you want the bloomed object to be visible through walls, set visibility.

I think layers would give the same result as mesh.visible. It would be useful to test these and see if there is any performance difference.

1 Like

I’m having a hard time remembering what i was trying to do. It’s possible that i was trying to render completely different meshes, at the same spot. I think i had a high-resolution mesh with no mapping for example, and then a portion of that mesh, with mapping. Imagine you delete a bunch of triangles from a mesh, and you export both as two different GLTF.
I wanted to draw one on top of the other, so use the same matrix4 derived from one position, rotation, scale,

i had to do something like

scene2.traverse(o=>o.matrixWorld.copy(o.userData.master.matrixWorld)

But i just realized that this is more or less completely unrelated :smiley: layers don’t help here, something like this possibly would, but could be an antipattern

o.transform = new THREE.Transform()
o2.transform = o.transform

Now i could organize the render order however i want, by renderOrder or several scenes, and the update is for sure going to be cached. Ie. if its calculated it doesnt need to copy() just skip.

1 Like

I just meant

function setColorWrite( val ) { root.traverse(...) }
function setVisible(val) { root.traverse(...) }
function setFOO( foo ) {...}
function setBAR( bar ) {...}

Will all have to do their own traversal using this pattern.

And then you can do stuff like

setColorWrite(false)
setColorWrite(true)
setColorWrite(false)
render()

Three traversals,. but only the last one’s effect is visible.

I’m not exactly sure what the solution is. I suggested onBeforeUpdate and onAfterUpdate at the same time i pushed for onBeforeRender. But it’s not as straightforward, because i don’t think it’s guaranteed to visit when updating matrices. The idea is, if you’re making an object that can react to these few methods - color, visible, foo, bar:

class MyObject extends Object3D {
  constructor( store ) {
    this.store = store
  }
  onAfterUpdate(){
    this.colorWrite = store.colorWrte //just copy
    if ( this.visible !== store.visible ) this.visible = store.visible //compare to store
    if ( store.dirtyFoo ) this.foo = store.foo //store resets flag after this pass
  }
}

This may be a total anti pattern, but the idea is that three can automatically traverse your scene and update stuff. Actually needs to traverse everything visible to draw stuff, even if doesn’t have to update. So instead of doing N traversals for N of these properties that need to be set, you do just one. Now:

store.setColorWrite(true)
store.setColorWrite(false)
store.setColorWrite(true)
store.setColorWrite(false)
store.setColorWrite(true)
render()

Only does one traversal.

I agree with this, but i think the boundary of “your app” and three in this case is a bit ambiguous. I usually have a layer that wraps around three, but is not really my app, something that i can use in other apps that use three. In this case, what comes to mind is turning off autoUpdate and just making custom update loop. Something that visits all of the nodes even if the matrices are clean. From there, lots of options :smiley:

Can EffectComposer with multiple RenderPasses be used to achieve layering?

I’m not sure what you understand by “layering”, the layer object is a bitmask to determine if a camera has the same flag as the object has in the layer mask and will render it then.

As others mentioned already this concept isn’t really good as it requires traversing the entire scene all time with all the matrix computations, for EffectComposer some examples do it a more proper way by having a separate scene, what is a bit messy to manage though.

I implemented custom render lists for components like deferred lights, those objects are regular part of the main scene hierarchy to be attached to others or have children, but at the culling phase it will be pushed instead to the actual render list to another that will go into a further render call at the light pass.

This only required a single line added to the WebGLRenderer, a callback (onProject) for the Object3D class as i suggested here.

It would be good if THREE would implement layers as custom lists to prevent repeating the entire process.