New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EffectComposer] Issue with 2 consecutive RenderPass #1234

Closed
BKcore opened this Issue Feb 1, 2012 · 18 comments

Comments

Projects
None yet
7 participants
@BKcore

BKcore commented Feb 1, 2012

The link

http://demo.bkcore.com/threejs/webgl_tron_glow.html

The code

glowcomposer = new THREE.EffectComposer( renderer, renderTarget );

glowcomposer.addPass( renderModelGlow ); // RenderPass
glowcomposer.addPass( hblur ); // ShaderPass
glowcomposer.addPass( vblur ); // ShaderPass
glowcomposer.addPass( effectSave ); // SavePass

glowcomposer.addPass( renderModel ); // RenderPass
glowcomposer.addPass( finalPass ); // ShaderPass

The context

I got two scenes with the same object/camera/light. One with the diffuse texture (http://demo.bkcore.com/threejs/webgl_tron.html) and one with a glow texture (glowing areas are white on black).

My rendering pipeline is as follow :
I first render the glow scene and apply a H/V blur to it, then store the output into a frameBuffer using a SavePass.
Then I want to render the diffuse scene.
And finally mix that with the glow framebuffer.

The issue

My glow sampler2D is passing well to my finalPass shader, but as you can see in the demo, the second RenderPass call for the diffuse scene is not performing well. It only outputs black.

I tried to render the diffuse scene in a single Render pass and that works. It's when I use it as a second RenderPass that it's not working.

Any ideas ?

Thank you.
Thibaut D.

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 2, 2012

Contributor

I think it's because depth buffer gets destroyed, see #1017.

For your use case, what may work instead is to use two composers (and thus two fully separate render targets, each with own depth buffer).

Something like this:

glowComposer = new THREE.EffectComposer( renderer, renderTargetGlow );

glowComposer.addPass( renderModelGlow ); // RenderPass
glowComposer.addPass( hblur ); // ShaderPass
glowComposer.addPass( vblur ); // ShaderPass

finalComposer = new THREE.EffectComposer( renderer, renderTargetFinal );
finalComposer.addPass( renderModel ); // RenderPass
finalComposer.addPass( finalPass ); // ShaderPass

Now diffuse layer will be available as regular tDiffuse texture sampler in finalComposer and glow layer you will supply from glowComposer.

It will be either glowComposer.renderTarget1 or glowComposer.renderTarget2 depending on particular number of passes and whether passes switch front and back buffers, simplest is to try both and see which works.

finalshader.uniforms[ 'tGlow' ].texture = glowComposer.renderTarget1;
Contributor

alteredq commented Feb 2, 2012

I think it's because depth buffer gets destroyed, see #1017.

For your use case, what may work instead is to use two composers (and thus two fully separate render targets, each with own depth buffer).

Something like this:

glowComposer = new THREE.EffectComposer( renderer, renderTargetGlow );

glowComposer.addPass( renderModelGlow ); // RenderPass
glowComposer.addPass( hblur ); // ShaderPass
glowComposer.addPass( vblur ); // ShaderPass

finalComposer = new THREE.EffectComposer( renderer, renderTargetFinal );
finalComposer.addPass( renderModel ); // RenderPass
finalComposer.addPass( finalPass ); // ShaderPass

Now diffuse layer will be available as regular tDiffuse texture sampler in finalComposer and glow layer you will supply from glowComposer.

It will be either glowComposer.renderTarget1 or glowComposer.renderTarget2 depending on particular number of passes and whether passes switch front and back buffers, simplest is to try both and see which works.

finalshader.uniforms[ 'tGlow' ].texture = glowComposer.renderTarget1;
@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore Feb 2, 2012

I tried that, but got a very unexpected result.

This, is the code you proposed :
http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html
The tDiffuse from the renderModel is still black.

And this, is the exact same code, with just the two render pass swapped (the diffuse scene is in the glow composer, and the glow scene in the final composer) :
http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html
It is of course not the effet I'm looking for since the diffuse is blurred and not the glow, but as you can see both tGlow and tDiffuse samplers are working...

And all I did was to swap (line 130 & 175) :
new THREE.RenderPass( scene, camera );
with
new THREE.RenderPass( glowscene, glowcamera );

It's quite startling.

BKcore commented Feb 2, 2012

I tried that, but got a very unexpected result.

This, is the code you proposed :
http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html
The tDiffuse from the renderModel is still black.

And this, is the exact same code, with just the two render pass swapped (the diffuse scene is in the glow composer, and the glow scene in the final composer) :
http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html
It is of course not the effet I'm looking for since the diffuse is blurred and not the glow, but as you can see both tGlow and tDiffuse samplers are working...

And all I did was to swap (line 130 & 175) :
new THREE.RenderPass( scene, camera );
with
new THREE.RenderPass( glowscene, glowcamera );

It's quite startling.

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 2, 2012

Contributor

You need to use RGBA format for render targets if you use transparency.

Contributor

alteredq commented Feb 2, 2012

You need to use RGBA format for render targets if you use transparency.

@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore Feb 2, 2012

Well, I agree, but I'm not using any transparency since my glow pass renders white on black and my diffuse pass well... diffuse on black. I then compose them with an addition so transparency is not needed.

Still, just to be sure, I changed the RT's format to RGBA, but that didn't change anything.

The strange issue here, is that doing my scene/camera RenderPass after my glowscene/glowcamera RenderPass does not work, while doing my scene/camera RenderPass before my glowscene/glowcamera RenderPass does.

BKcore commented Feb 2, 2012

Well, I agree, but I'm not using any transparency since my glow pass renders white on black and my diffuse pass well... diffuse on black. I then compose them with an addition so transparency is not needed.

Still, just to be sure, I changed the RT's format to RGBA, but that didn't change anything.

The strange issue here, is that doing my scene/camera RenderPass after my glowscene/glowcamera RenderPass does not work, while doing my scene/camera RenderPass before my glowscene/glowcamera RenderPass does.

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 2, 2012

Contributor

Hmmm, seems like geometry sharing issue for different materials (see #1211).

Try this:

function createScene( geometry, x, y, z, b ) {

    zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );

    // ...

    var geometryClone = THREE.GeometryUtils.clone( geometry );
    var gmat = new THREE.MeshBasicMaterial( { map: gtex } );
    var gmesh = new THREE.Mesh( geometryClone, gmat );

    // ...
}
Contributor

alteredq commented Feb 2, 2012

Hmmm, seems like geometry sharing issue for different materials (see #1211).

Try this:

function createScene( geometry, x, y, z, b ) {

    zmesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );

    // ...

    var geometryClone = THREE.GeometryUtils.clone( geometry );
    var gmat = new THREE.MeshBasicMaterial( { map: gtex } );
    var gmesh = new THREE.Mesh( geometryClone, gmat );

    // ...
}
@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore Feb 2, 2012

Yes ! That was it.

My two samplers are now correctly filled. Thank you :)

EDIT

Hum... Yet another strange behaviour, my two samplers are good, when I do

gl_FragColor = texel;

I get the diffuse correctly (http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html)

When I do

gl_FragColor = glow;

I get the glow correctly.

But when I try to do a simple additive blend on both
gl_FragColor = texel + glow;
I only get the glow (http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html).

Sorry for asking so many questions, I've been trying to get this to work but to no avail..

EDIT2

In fact it seems that doing

gl_FragColor = texel + glow;

Looks more like this regarding rendering output :

gl_FragColor = glow + glow;

It's like the texel vec4 gets unexplicably replaced by the glow vec4. Sounds crazy.

BKcore commented Feb 2, 2012

Yes ! That was it.

My two samplers are now correctly filled. Thank you :)

EDIT

Hum... Yet another strange behaviour, my two samplers are good, when I do

gl_FragColor = texel;

I get the diffuse correctly (http://demo.bkcore.com/threejs/webgl_tron_glow_swap2.html)

When I do

gl_FragColor = glow;

I get the glow correctly.

But when I try to do a simple additive blend on both
gl_FragColor = texel + glow;
I only get the glow (http://demo.bkcore.com/threejs/webgl_tron_glow_swap.html).

Sorry for asking so many questions, I've been trying to get this to work but to no avail..

EDIT2

In fact it seems that doing

gl_FragColor = texel + glow;

Looks more like this regarding rendering output :

gl_FragColor = glow + glow;

It's like the texel vec4 gets unexplicably replaced by the glow vec4. Sounds crazy.

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 2, 2012

Contributor

You need to put textures in different texture units:

uniforms: {
    tDiffuse: { type: "t", value: 0, texture: null },
    tGlow:    { type: "t", value: 1, texture: null }
},
Contributor

alteredq commented Feb 2, 2012

You need to put textures in different texture units:

uniforms: {
    tDiffuse: { type: "t", value: 0, texture: null },
    tGlow:    { type: "t", value: 1, texture: null }
},
@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore commented Feb 2, 2012

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 2, 2012

Contributor

Yay!

Contributor

alteredq commented Feb 2, 2012

Yay!

@mrdoob mrdoob closed this Feb 3, 2012

@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore Feb 7, 2012

In case anyone is interrested in selective glow rendering with Three.js, I just published a small article about it : http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html

Hope it helps.

BKcore commented Feb 7, 2012

In case anyone is interrested in selective glow rendering with Three.js, I just published a small article about it : http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html

Hope it helps.

@alteredq

This comment has been minimized.

Show comment
Hide comment
@alteredq

alteredq Feb 7, 2012

Contributor

Nice ;)

Alternatively, instead of geometry cloning, another cheaper option would be to use more similar materials (so that geometry in both passes would need the same buffers).

In this case, instead of using Basic material for glow pass, you could try Lambert material with zero diffuse lighting, just with ambient light.

Something like this:

glowScene.add( new THREE.AmbientLight( 0xffffff ) );

// ...

var gmat = new THREE.MeshLambertMaterial( { map: gtex, ambient: 0xffffff, color: 0x000000 } );
var gmesh = new THREE.Mesh( geometry, gmat );
Contributor

alteredq commented Feb 7, 2012

Nice ;)

Alternatively, instead of geometry cloning, another cheaper option would be to use more similar materials (so that geometry in both passes would need the same buffers).

In this case, instead of using Basic material for glow pass, you could try Lambert material with zero diffuse lighting, just with ambient light.

Something like this:

glowScene.add( new THREE.AmbientLight( 0xffffff ) );

// ...

var gmat = new THREE.MeshLambertMaterial( { map: gtex, ambient: 0xffffff, color: 0x000000 } );
var gmesh = new THREE.Mesh( geometry, gmat );
@BKcore

This comment has been minimized.

Show comment
Hide comment
@BKcore

BKcore Feb 7, 2012

Indeed, that's working perfectly.

http://demo.bkcore.com/threejs/webgl_tron_glow_seq.html

I'll update my article regarding this solution. Thank you !

BKcore commented Feb 7, 2012

Indeed, that's working perfectly.

http://demo.bkcore.com/threejs/webgl_tron_glow_seq.html

I'll update my article regarding this solution. Thank you !

@bbiswas

This comment has been minimized.

Show comment
Hide comment
@bbiswas

bbiswas Apr 15, 2014

I'm porting this code to r60 and strangely coming across the same issue with chaining the effect composers. I have the godray pass working if I render it to screen. But the final pass only outputs the model. The oclcomposer output to the "tadd" texture channel seems to come in okay when I don't use a separate Effect Composer!! I've tried with renderer.PreserveDrawingBuffer = true but that didn't work either. I've tried setting autoclear to false but that wasn't it.

If I render the oclcomposer after the final composer and make the godray pass output to screen, I can see the godrays but no models.

Instead of directing the oclcomposer output to the tadd channel in final pass, if I pipe it to a MeshBasicMaterial and create a textured quad and add it to the main scene, now I can see the model and the godrays (on a plane), not the effect I want but it tells me that the texture piping is working.

// COMPOSERS
//-------------------
var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBufer: false };

renderTargetOcl = new THREE.WebGLRenderTarget( SCREEN_WIDTH/2, SCREEN_HEIGHT/2, renderTargetParameters );

hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );
var bluriness = 2;

hblur.uniforms[ 'h' ].value = bluriness / SCREEN_WIDTH*2;
vblur.uniforms[ 'v' ].value = bluriness / SCREEN_HEIGHT*2;

var renderModel = new THREE.RenderPass( scene, camera );
var renderModelOcl = new THREE.RenderPass( oclscene, oclcamera );

grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
grPass.needsSwap = true;
grPass.renderToScreen = false;

oclcomposer = new THREE.EffectComposer( renderer, renderTargetOcl );

oclcomposer.addPass( renderModelOcl );
oclcomposer.addPass( hblur );
oclcomposer.addPass( vblur );
oclcomposer.addPass( grPass );

var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
finalPass.needsSwap = true;
finalPass.renderToScreen = true;
finalPass.uniforms[ 'tAdd' ].texture = oclcomposer.renderTarget1;

renderTarget = new THREE.WebGLRenderTarget( SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters );
finalcomposer = new THREE.EffectComposer( renderer, renderTarget );

finalcomposer.addPass( renderModel );
finalcomposer.addPass( finalPass );

//RENDER
//-----------
oclcomposer.render(0.1);
finalcomposer.render( 0.1 );

bbiswas commented Apr 15, 2014

I'm porting this code to r60 and strangely coming across the same issue with chaining the effect composers. I have the godray pass working if I render it to screen. But the final pass only outputs the model. The oclcomposer output to the "tadd" texture channel seems to come in okay when I don't use a separate Effect Composer!! I've tried with renderer.PreserveDrawingBuffer = true but that didn't work either. I've tried setting autoclear to false but that wasn't it.

If I render the oclcomposer after the final composer and make the godray pass output to screen, I can see the godrays but no models.

Instead of directing the oclcomposer output to the tadd channel in final pass, if I pipe it to a MeshBasicMaterial and create a textured quad and add it to the main scene, now I can see the model and the godrays (on a plane), not the effect I want but it tells me that the texture piping is working.

// COMPOSERS
//-------------------
var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBufer: false };

renderTargetOcl = new THREE.WebGLRenderTarget( SCREEN_WIDTH/2, SCREEN_HEIGHT/2, renderTargetParameters );

hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );
var bluriness = 2;

hblur.uniforms[ 'h' ].value = bluriness / SCREEN_WIDTH*2;
vblur.uniforms[ 'v' ].value = bluriness / SCREEN_HEIGHT*2;

var renderModel = new THREE.RenderPass( scene, camera );
var renderModelOcl = new THREE.RenderPass( oclscene, oclcamera );

grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
grPass.needsSwap = true;
grPass.renderToScreen = false;

oclcomposer = new THREE.EffectComposer( renderer, renderTargetOcl );

oclcomposer.addPass( renderModelOcl );
oclcomposer.addPass( hblur );
oclcomposer.addPass( vblur );
oclcomposer.addPass( grPass );

var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
finalPass.needsSwap = true;
finalPass.renderToScreen = true;
finalPass.uniforms[ 'tAdd' ].texture = oclcomposer.renderTarget1;

renderTarget = new THREE.WebGLRenderTarget( SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters );
finalcomposer = new THREE.EffectComposer( renderer, renderTarget );

finalcomposer.addPass( renderModel );
finalcomposer.addPass( finalPass );

//RENDER
//-----------
oclcomposer.render(0.1);
finalcomposer.render( 0.1 );
@shi-314

This comment has been minimized.

Show comment
Hide comment
@shi-314

shi-314 Sep 21, 2014

@bbiswas I have the same issue with the latest version. Have you found out anything?

shi-314 commented Sep 21, 2014

@bbiswas I have the same issue with the latest version. Have you found out anything?

@bbiswas

This comment has been minimized.

Show comment
Hide comment
@bbiswas

bbiswas Sep 22, 2014

Yes I did get it working - Here's the code

var renderTargetOcl = new THREE.WebGLRenderTarget( webGL_window_width/4, webGL_window_height/4, renderTargetParameters );

            hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
            vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );

            var bluriness = 3;

            hblur.uniforms[ 'h' ].value = bluriness / webGL_window_width*2;
            vblur.uniforms[ 'v' ].value = bluriness / webGL_window_height*2;

            var renderModel = new THREE.RenderPass( scene, camera );
            var renderModelOcl = new THREE.RenderPass( g_occlusion_buffer, g_occlusion_camera );

            grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
            grPass.needsSwap = true;
            grPass.renderToScreen = false;

            g_volumetric_light_composer = new THREE.EffectComposer( webGLRenderer, renderTargetOcl );

            g_volumetric_light_composer.addPass( renderModelOcl );
            g_volumetric_light_composer.addPass( hblur );
            g_volumetric_light_composer.addPass( vblur );
            g_volumetric_light_composer.addPass( hblur );
            g_volumetric_light_composer.addPass( vblur );
            g_volumetric_light_composer.addPass( grPass );

            var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
            finalPass.needsSwap = true;
            finalPass.renderToScreen = true;
            finalPass.uniforms[ 'tAdd' ].value = g_volumetric_light_composer.renderTarget1;

            finalcomposer.addPass( renderModel );
            finalcomposer.addPass( finalPass );

//in Render Loop



                g_occlusion_camera.position = camera.position;

                g_occlusion_camera.lookAt( new THREE.Vector3(0,0,0) );
                camera.lookAt( new THREE.Vector3(0,0,0)  );

                vlight.position = pointLight1.position;
                vlight.updateMatrixWorld();

                var lPos = THREE.Extras.Utils.projectOnScreen(pointLight1, camera);
                grPass.uniforms["fX"].value = lPos.x;
                grPass.uniforms["fY"].value = lPos.y;

                g_volumetric_light_composer.render(0.1);
                finalcomposer.render( 0.1 );

bbiswas commented Sep 22, 2014

Yes I did get it working - Here's the code

var renderTargetOcl = new THREE.WebGLRenderTarget( webGL_window_width/4, webGL_window_height/4, renderTargetParameters );

            hblur = new THREE.ShaderPass( THREE.ShaderExtras[ "horizontalBlur" ] );
            vblur = new THREE.ShaderPass( THREE.ShaderExtras[ "verticalBlur" ] );

            var bluriness = 3;

            hblur.uniforms[ 'h' ].value = bluriness / webGL_window_width*2;
            vblur.uniforms[ 'v' ].value = bluriness / webGL_window_height*2;

            var renderModel = new THREE.RenderPass( scene, camera );
            var renderModelOcl = new THREE.RenderPass( g_occlusion_buffer, g_occlusion_camera );

            grPass = new THREE.ShaderPass( THREE.Extras.Shaders.Godrays );
            grPass.needsSwap = true;
            grPass.renderToScreen = false;

            g_volumetric_light_composer = new THREE.EffectComposer( webGLRenderer, renderTargetOcl );

            g_volumetric_light_composer.addPass( renderModelOcl );
            g_volumetric_light_composer.addPass( hblur );
            g_volumetric_light_composer.addPass( vblur );
            g_volumetric_light_composer.addPass( hblur );
            g_volumetric_light_composer.addPass( vblur );
            g_volumetric_light_composer.addPass( grPass );

            var finalPass = new THREE.ShaderPass( THREE.Extras.Shaders.Additive );
            finalPass.needsSwap = true;
            finalPass.renderToScreen = true;
            finalPass.uniforms[ 'tAdd' ].value = g_volumetric_light_composer.renderTarget1;

            finalcomposer.addPass( renderModel );
            finalcomposer.addPass( finalPass );

//in Render Loop



                g_occlusion_camera.position = camera.position;

                g_occlusion_camera.lookAt( new THREE.Vector3(0,0,0) );
                camera.lookAt( new THREE.Vector3(0,0,0)  );

                vlight.position = pointLight1.position;
                vlight.updateMatrixWorld();

                var lPos = THREE.Extras.Utils.projectOnScreen(pointLight1, camera);
                grPass.uniforms["fX"].value = lPos.x;
                grPass.uniforms["fY"].value = lPos.y;

                g_volumetric_light_composer.render(0.1);
                finalcomposer.render( 0.1 );
@shi-314

This comment has been minimized.

Show comment
Hide comment
@shi-314

shi-314 Sep 22, 2014

Thanks a lot!
It was the line with finalPass.uniforms[ 'tAdd' ].value. I had finalPass.uniforms[ 'tAdd' ].texture before.

shi-314 commented Sep 22, 2014

Thanks a lot!
It was the line with finalPass.uniforms[ 'tAdd' ].value. I had finalPass.uniforms[ 'tAdd' ].texture before.

@netpraxis

This comment has been minimized.

Show comment
Hide comment
@netpraxis

netpraxis Oct 30, 2015

Hey bbiswas, can you add where you created g_occlusion_buffer for this part:

var renderModelOcl = new THREE.RenderPass( g_occlusion_buffer, g_occlusion_camera );

netpraxis commented Oct 30, 2015

Hey bbiswas, can you add where you created g_occlusion_buffer for this part:

var renderModelOcl = new THREE.RenderPass( g_occlusion_buffer, g_occlusion_camera );

@felixturner

This comment has been minimized.

Show comment
Hide comment
@felixturner

felixturner Sep 30, 2016

Contributor

Seems like the original demo is no longer working:

FWIW I made a stripped-down demo of selectively applying FX and composing via Additive Blend (using Three.js v79) here: https://www.airtightinteractive.com/demos/selective-fx/

Contributor

felixturner commented Sep 30, 2016

Seems like the original demo is no longer working:

FWIW I made a stripped-down demo of selectively applying FX and composing via Additive Blend (using Three.js v79) here: https://www.airtightinteractive.com/demos/selective-fx/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment