Multiple Scenes vs Layers

How would layer lists (sets) work? While traversing the tree only once (in WebGLRenderer), at each node, the system would check which layer set the node is in, and render it to that render target (or whatever it renders to)?

What I was thinking to do, on the outside of Three.js in user code, was to track which objects are in which layer (layers being a construct I would make outside of Three.js). In my render loop, I’d traverse the tree once per layer (for each), and during that traversal I’d set nodes to visible or not visible depending on if the node is in the current layer. So if there’s 9 layers, there there’s 9 traversals unfortunately. But seems like easiest way to manage it outside of three.js in a simpler way than Three.js layers system, and not limited to 32 layers (though still I don’t imagine having more than 32 layers, but having to wrangle bits is not ideal, especially inserting or swapping layer order)

When THREE is about to render the scene it generates a render list of all visible objects in the culling phase, this list determines what is rendered, by pushing it into a separate custom list instead, this list can be used in another pass in which those objects should be visible, without further scene traversal or culling, straight to the render calls.

That way you’re also not limited to any amount of layers, i can determine flag or component wise if an object goes into another pass, in this or maybe both, you would just implement the lists assignment rule yourself.

Anyway without adding such an internal callback layers are easier to manage than manually using a separate scene, especially when you need those objects to be children of some main scene objects.

That sounds great! Hopefully someday it’ll exist.

True. I’m thinking to use only one scene, but modify objects’ visible each time I render with the same scene (as you said, to take advantage of one hierarchy, and we only need visual separation (imagine a UI has a toggle for “always render this thing behind everything else” regardless where in the hierarchy that thing is)).

As far as I know, layers and visible are the only way to tell Three.js (from the outside user code) which things to render or not render, right?

EDIT, see examples in comments further below for a layering system.


Here’s a simple example of two layers using one scene, and two renderers (probably not ideal, probably should use one renderer, multiple render calls):

and here’s my attempt at using two RenderPasses with one renderer:

Seems the last RenderPass draws over the first one (not sure about perf implications either).

Basically yes, layers also won’t stop recursion which was suggested once but i agree with Mugen this shouldn’t stop recursion as the purpose of layers is different from visibility and can conflict if it would stop there.

What you can do from outside too is add a “added” and “removed” callback, and use a separate scene as said, but don’t add it, instead myGlowPassScene.children.push( this );, and disable matrix update on that myGlowPassScene, so you at least got a reduced flat list for that render pass while the object remains integrated in the main scene.

Oh, so visible stops traversal (recursion) of the tree, and the subtree is therefore also not rendered? If so, then that scraps my plans of using visible.

That myGlowPassScene idea is good! It’s like the internal render lists idea in a way. So if I have ultiple “layers” (backed by the extra scenes), maybe I never render the scene that has everything (assuming every object belongs to a layer), and use it only for matrix update, then always render each layer scene (flat list) on its own.

Just gotta think of a good way to manage this (to make it easy for users of my scene lib to use). By “scene lib”, I mean a rendering system built on top of three, and the end users still have only one “scene” but it’s a higher-level scene, and under the hood there’d be multiple Three.js scenes in the “scene lib” implementation.

You could also simply patch the base classes prototypes with a custom add and remove method, taking care of proper push and removal from the flat lists.

I made this simple patcher to make monkey patching easier with THREE, as all classes based on Object3D already inherited the prototype methods.

function Patch( root ) {

	this.root = root;
	this.list = [];

	for ( let name in root )
		this.list.push( root[name] ) ;

}

Patch.prototype = {
	each: function( property, value, prototype ) {

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

			const object = this.list[ i ];

			if ( object.prototype instanceof Object && object.prototype[property] === value ) {

				Object.assign( object.prototype, prototype );

			}

		}

	}
};

const three = new Patch( THREE );

three.each( 'isObject3D', true, {

	add: function( ... ) {},
	remove: function ( ... ) {}

});

Nice, thanks. I imagine changing the signature of add/remove to some like

const add = Object3D.prototype.add
const remove = Object3D.prototype.add
patcher.each( 'isObject3D', true, {
	add(...objects) {
		if (typeof objects[objects.length - 1] === 'string') {
			// if the last arg is a string, it is a layer name, add the object to a separate scene in the layer-scene map.
			objects.pop()
		} else { ... add to default layer scene ... }
		add.apply(this, objects)
	},
	remove: function ( ... ) { ... similar ... }
});

I suppose certain things like lights may need to be in all layers. Maybe last arg can be a string or string[] or some shortcut like all to add to all layers.

You should store what pass an object gets into on the object, like in userData, on removal it shouldn’t be necessary to explicitly tell again which list or lists it was part of. Also you might try avoiding the usage of apply with array arguments, that can’t be optimized and should be very slow unless it got improved.

I suppose certain things like lights may need to be in all layers

That depends if those further passes will use or need lights. But you might be careful about THREE lights making sure there won’t be some recompiling of materials going on, should work fine though i guess.

Seems nowadays (at least in Chrome) .apply is pretty fast (perf test)

But you might be careful about THREE lights making sure there won’t be some recompiling of materials going on

Yeah, I’m not sure, does sticking lights in multiple scenes cause re-compiling of materials? At the moment my scenes are small enough, to where I just want one light hitting all objects (which will be in different layers)

Alright, first version of a layer system. The demo has some UI to toggle layering on/off and to change layer order:

Updated, with layer visibility toggles and OrbitControls:

Selectively applying a bloom filter to objects in a scene seems like a good use for multiple layers: http://jsfiddle.net/prisoner849/mjfckw02/ (from this thread)

If there’s a way to accomplish this without multiple layers though, I’d like to hear about it!

1 Like