Trails + Background image

I’m working on a space-like 3D ambient. I would like to have two effects:

  1. Trails for scene objects; I can obtain this by preserving the renderer buffer and setting a transparent black plane in front of the camera so that each time the scene gets rendered the old objects get darker and darker.

Demo Here.

  1. I would like to have a space background texture that I can set using the CubeTextureLoader:

Demo 2

But when I use the CubeTextureLoader, the trail effect goes away because the background texture clears the renderer buffer. Is there a way to obtain those two effects together ?

I thought that one may use WebGLRenderTarget to render the scene with preserveDrawingBuffer and then blend the background and the scene with postprocessing, but I don’t know how to do that.

Using a render target for the trails effect feels like an approach that should work. However, I don’t think it necessary to set preserveDrawingBuffer to true in this case. Since you are not rendering the effect directly to the default framebuffer, it is possible to perform an accumulation in the render target without changing this flag.

So the idea is to render the scene with the background first, then updating the render target and finally blend it over the scene.

Thank you!!!
Ok, I understand, that’s clear now.

Ok, I know how to render the background, I know how to accumulate the updates on the render target, but I don’t know how to blend it over the scene … I’ve been trying to understand if that’s possibile with some postprocessing Pass but couldn’t find an easy way. Do you have any suggestions?

You can write a custom shader that receives two textures. The first one is the normal rendered scene, the second one is the trails effect. You can additively blend both textures in order to produce the final image.

Maybe you start developing without EffectComposer. Meaning you manage two render targets in your application and use the above shader to produce the final image rendered to screen. If this works, you can try to migrate to EffectComposer by using RenderPass to produce your scene image and then a custom TrailsPass which manages the trials effect and the final composition.

Thank you!! I’m going to follow your instructions and see what I’m able to do, thx.

This is what I got so far, but there’s something still not working properly.

Edited because I solved some problems.

  • I create a scene with the space cubic-texture.
  • I let the renderer render that texture normally into the screen.
  • I create a renderTarget and rtScene containing the stars (just some random points).
  • I create a transparent black plane (fadeMesh) to put in front of the camera in the rtScene.
  • I create copyPass like this: var copyPass = new THREE.ShaderPass( THREE.CopyShader );
    using the standard CopyShader and a simplified ShaderPass wich I use (so far) without the EffectComposer:

THREE.ShaderPass = function ( shader ) { this );

this.textureID = "tDiffuse";
this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );

this.material = new THREE.ShaderMaterial( {

	defines: shader.defines || {},
	uniforms: this.uniforms,
	vertexShader: shader.vertexShader,
	fragmentShader: shader.fragmentShader ,
    transparent: true
} );

this.material.blending = THREE.CustomBlending;
this.material.blendEquation = THREE.AddEquation; 
this.material.blendSrc = THREE.SrcAlphaFactor; 
this.material.blendDst = THREE.SrcAlphaFactor; = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
this.scene = new THREE.Scene();

this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
this.scene.add( this.quad );


THREE.ShaderPass.prototype.render = function( renderer, writeBuffer ) {

this.uniforms[ this.textureID ].value = writeBuffer.texture;

this.quad.material = this.material;

renderer.setRenderTarget ( null ) ;
renderer.autoClearColor = false ;
renderer.render( this.scene, );


And the render cycle:

function render() {

	// render background to screen
	renderer.render(scene, camera);

	// fade plane in rtScene
	var worldDir = new THREE.Vector3 ( ) ;
	camera.getWorldDirection ( worldDir ) ;
	worldDir.multiplyScalar ( 0.1 );
	fadeMesh.position.addVectors ( camera.position , worldDir ) ;  	
	fadeMesh.rotation.copy ( camera.rotation ) ;

	// rendere Stars to renderTarget (with accumulation), same camera
	renderer.autoClearColor = false ;
	renderer.setRenderTarget ( renderTarget );
	renderer.render( rtScene, camera );
	// shader Pass copies stars into screen from renderTarget
	copyPass.render ( renderer ,  renderTarget ) ;

	// controls
	controls.update ( ) ;


Demo 3

I’m not sure if I have to manage the blending operations in the ShaderPass material (that’s what I did) or if I have to manage the blending operations in the CopyShader with GLSL (don’t know how).
More over for some reason half of the stars disappear after a while if the camera doesn’t move … any ideas why ?.
[Edit: solved, I was using renderer.autoClear = false instead of autoClearColor = false].

EDIT: I read your answer again and you said to use two render targets … in order to then blend the two textures in the target buffers … Now I understand that step … [see next update]


THREE.ShaderPass.prototype.render = function( renderer, target1, target2 ) {

	renderer.setRenderTarget ( null ) ;
	this.uniforms[ this.textureID ].value = target1.texture;
	this.uniforms[ this.textureID2 ].value = target2.texture;

	this.quad.material = this.material;

	renderer.autoClearColor = false ;
	renderer.render( this.scene, );


So I render the sky in renderTarget1
and the stars with trails in renderTarget2
so then I have to textures and therefore two texels in the shader:

			"vec4 texel1 = texture2D( tDiffuse, vUv );",
			"vec4 texel2 = texture2D( tDiffuse2, vUv );",
			"gl_FragColor = texel1 + texel2;",

What’s the correct way to add the two values?
texel1 is the color of the background (sky)
and texel2 i the color of the foreground (stars and trails).

Demo 4

This works and seems allright.
I used two renderTargets and blended the textures they produce with a shader that sums the colors like this:

"gl_FragColor = texel1 * texel1.a + texel2 * texel2.a;"

I don’t know if this is the correct way to do it but it looks ok.

1 Like