Toggle between multiple THREE.EffectComposer scenes using the same renderer in three.js

I am in the process of creating complex scenes with Composer in three.js. I am wanting to know if it is possible to switch between two scenes that have different composer effects attributed to them. To gain some sort of perspective I have created an example which allows you to toggle between two normally rendered scenes.

Two scene example

From my understanding of how composer works you create an instance of it and then apply a render pass like so:

    this.composer = new THREE.EffectComposer(this.renderer.default.init);
    this.renderPass = new THREE.RenderPass(this.stage,  this.camera);
    this.renderPass.renderToScreen = true;
    this.composer.addPass(this.renderPass); 

and then apply a composer render like so:

    this.composer.render();

So my question is if I have a second scene which a composer instance how can I then:

  1. Use the same renderer (if possible)
  2. Toggle between scene 1 and scene 2 like in a similar fashion to my example.

Answer

Two classical composers

So before I begin I just want to give a shout out to Martin Schuhfuß who provided the insight to this solution.

Disclaimer

I am not an expert or professional Programmer in javascript and I by no means suggest that this is the only way of doing this, my presentation should be taken in an abstract fashion. My thoughts only explain the theoretical premises for which the solution is based on and implementation of this will depend upon your own architecture.

Architecture

I am using an OOP method in this example, you should be able to manipulate the solution to whatever method you are using.

Method

So based on Martin’s current example you can see that we able to add the composer property/object to our scene object this means that scenes could inherit the same composer effects which is brilliant, however In my case I have many scenes with different composer effects so I needed to rethink the problem.

1. Create a Composer object.

So I created an object to put composer objects in and keep to the nice ‘composer’ name convention when reference my effects.

This.composer = {};

2. Create a function to initialise RenderPasses with the concerning scenes.

So it is important to remember that we need to define and initailse the RenderPasses first before will call our composer. RenderPasses allows us to attribute the effects (know has shaders) that we want. In my example I have two scenes therefore I needed to create:

  1. Two RenderPasses.
  2. One RenderPass copied the scene.
  3. The other RenderPass applied a sepia effect to its scene.

Example of Code:

this.init = function() {

stackoverflow.webgl.pass.base = new THREE.RenderPass(stackoverflow.webgl.scene.default, 
stackoverflow.webgl.camera);

stackoverflow.webgl.pass.base2 = new THREE.RenderPass(stackoverflow.webgl.scene.test, 
stackoverflow.webgl.camera);

stackoverflow.webgl.pass.sepia = new 
THREE.ShaderPass(THREE.SepiaShader);
stackoverflow.webgl.pass.sepia.renderToScreen = true;

stackoverflow.webgl.pass.copy = new THREE.ShaderPass(THREE.CopyShader);
stackoverflow.webgl.pass.copy.renderToScreen = true;

} 

Note: some file names had to be renamed due to legal reason, but the point being is that this function is called into a larger object.

3. Create a start function and lets assign composers to scenes

The purpose of this function is just to demonstrate how we combine it all together. So the code that we have is something like this.

// so we create an object to use for reference for EffectComposer objects
    this.composer = {};


// this property is used to set the scene that we want
// this.scene.default = new THREE.Scene();

    this.activeScene = this.scene.default;

// this function is just a wrapper for our code

    this.start = function () {

// so call our initialise our RenderPasses

    stackoverflow.webgl.pass.init();

// my own method of seting up the WebGLRenderer scene (You do it your Way!)
    stackoverflow.webgl.renderer.default.setup;

// Create a new composer for scene 1
    this.composer.ui = new THREE.EffectComposer(stackoverflow.webgl.renderer.default.init);

// Create a new composer for scene 1
    this.composer.ui2 = new THREE.EffectComposer(stackoverflow.webgl.renderer.default.init);

// Now here is the cool stuff you can assign a composer to each scene

    this.scene.default.composer = stackoverflow.webgl.composer.ui;
    this.scene.test.composer = stackoverflow.webgl.composer.ui2;

// and i always like to check that things happen and they do ;)

    console.log(this.scene.default);
    console.log(this.scene.test);
    console.log(this.composer);

// so  you will need to add the passes some place, I created a function call render, (you do according to your code structure)

    stackoverflow.webgl.render();

    }

4. Define where you add the passes in your architecture

So now we will need to add the pass declarations (addpass functions from composer) for our effects to take place.

this.render = function () {    
stackoverflow.webgl.composer.ui.addPass(stackoverflow.webgl.pass.base);        
stackoverflow.webgl.composer.ui.addPass(stackoverflow.webgl.pass.copy);      
stackoverflow.webgl.composer.ui2.addPass(stackoverflow.webgl.pass.base2);
stackoverflow.webgl.composer.ui2.addPass(stackoverflow.webgl.pass.sepia);
};

5. Add the EffectComposer render method

So this line of code will need to be placed wherever you do the composer processing (that depends on your setup).

stackoverflow.webgl.activeScene.composer.render();

Please take note that the ‘activecScene’ part makes reference to the actual scene used at the time. It is this that allows us to change the scene.

6. Create a button and toggle between the scenes

So I created two buttons in a dat.gui instance that allows me to toggle between the two scenes.

Again you can create a button, function whatever you like.

// again just change the vale of active scene to the scene you wish to change to

// button 1 value will be something like this:
stackoverflow.webgl.activeScene = stackoverflow.webgl.scene.test;

// button 2 value will takes us back to scene 1
stackoverflow.webgl.activeScene = stackoverflow.webgl.scene.default;

Conclusion This is my method of achieving this effect, but if there are better or alternative ways then please add to the discussion and share.