2
votes

here's the demo: http://percentcer.github.io/poi3d/

I have a shape being drawn on a black background that I'd like to give some sort of "light trails" effect to, which I thought I could achieve by keeping the previous framebuffer around and "darkening" it each frame (i.e. in a fragment shader, scaling each fragment vector by a value less than 1, assuming eventually the color will hit something close to vec4(0.0, 0.0, 0.0, a) ).

Each frame, I redraw the previous frame's fragment * some_scalar, draw the object on top of that, and then send it through a copy shader out to screen. I do all of this through the EffectComposer, which is used as follows.

Initialization:

composer  = new THREE.EffectComposer( renderer );

shDim = new THREE.ShaderPass( THREE.DimShader );
composer.addPass( shDim );

var scenePass  = new THREE.RenderPass( scene, camera );
composer.addPass( scenePass );

var shCopy = new THREE.ShaderPass( THREE.CopyShader );
composer.addPass( shCopy );

And the animation loop:

shDim.uniforms[ 'prevTDiffuse' ].value = composer.writeBuffer;
composer.render();

( https://github.com/percentcer/poi3d/blob/master/js/logic.js#L61 )

The DimShader itself isn't that complicated either:

fragmentShader: [

        "varying vec2 vUv;",

        "uniform sampler2D tDiffuse;",
        "uniform sampler2D prevTDiffuse;",
        "uniform float scaleRate;",

        "void main() {",

            "vec4 texel = texture2D( prevTDiffuse, vUv );",
            "gl_FragColor = vec4( texel.rgb * scaleRate, texel.a );",

        "}"

    ].join("\n")

So, repeatedly multiply a value by something less than 1 and you should get close to zero, right? However, if you run the demo you'll see that it gets darker but stops at some mid-tone gray value.

The default scaling rate is 0.995, which you can change with page-up and page-down. If you go to a faster rate (0.95, page down once from the default state) the fade out will get much closer to black but still actually never reaches it, it hangs on a (very dark) gray. Some of my friends think this is a symptom of float precision issues. I also considered that this might be a blending issue, where the alpha value is being scaled to 0.0 so at some point the new pixel colors stop showing up, but this occurs when I force alpha to be 1.0 as well.

I'd appreciate any help, I'm new to both three.js and GLSL. I have some openGL experience but would consider myself a beginner there as well. Thanks!

Oh also here's the full repo: https://github.com/percentcer/poi3d/

1

1 Answers

4
votes

This is due the the fact that the color buffer stores discrete values. At some point

texel.rgb * scaleRate

when rounded, ends up with what you started with -- so it never goes lower.

In fact, you can predict the approximate color. For color in 0 to 255, solve

scaleRate * color = ( color - 0.5 )

For scaleRate = 0.995, color = 100. I observe 96 in your demo.