ShaderPass post processing in EffectComposer removes alpha channel of objects in the scene

Hello everyone.

I am trying to build a project using the postprocessing library from NPM. I am having problems with the ShaderPass: I wrote a custom blur shader that I want to apply as postprocessing filter, but it removes the alpha channel of the objects in the scene.

If I use threejs library by importing it in the index.html file and using the example code for the EffectComposer, it works like expected. If I use the ES6 syntax by importing modules from postprocessing, the result is different. The problem is ShaderPass, as EffectComposer without any filter keeps the alpha of the objects in the scene. I tried also three-effectcomposer module, same result.

Am I doing something wrong? Or simply ShaderPass doesn’t allow the use of ES6 / Object Oriented approach? Or is there any other way? Thank you very much.

Here is the code I am using:

import {
    Vector2,
    Clock,
    ShaderMaterial,
    LinearFilter,
    RGBAFormat,
    WebGLRenderTarget,
    Color,
} from "three";

import {
    EffectComposer,
    RenderPass,
    ShaderPass
}
from "postprocessing";

const glsl = require("glslify");

export default class PostEffect {

    constructor(renderer, scene, camera, resolution) {

        this.clock = new Clock();
        this.drawShader = new ShaderMaterial({
            uniforms: {
                tDiffuse: {
                    type: "t",
                    value: null
                },
                iResolution: {
                    type: "v2",
                    value: resolution
                }
            },
            vertexShader: glsl.file("./Shaders/Post_effect/vertexShader.glsl"),
            fragmentShader: glsl.file("./Shaders/Post_effect/fragmentShader.glsl")
        });

        const width = resolution.x;// || 1;
        const height = resolution.y;// || 1;
        const parameters = {
            minFilter: LinearFilter,
            magFilter: LinearFilter,
            format: RGBAFormat,
            stencilBuffer: false
        };

        const renderTarget = new WebGLRenderTarget(width, height, parameters);

        this.composer = new EffectComposer(renderer);
        this.renderpass = new RenderPass(scene, camera);
        this.renderpass.clearColor = new Color(0, 0, 0);
        this.renderpass.clearAlpha = 0;
        this.composer.addPass(this.renderpass);

        // this is my shader
        this.blurPass = new ShaderPass(this.drawShader, "tDiffuse");
        this.blurPass.renderToScreen = true;

        this.composer.addPass(this.blurPass);
    }

    runShader() {
        this.composer.render(this.clock.getDelta());
    }
}

And this is how I implement it:

// threejs
const THREE = require("three");

// the particle system
import ParticleSystem from "./ParticleSystem.js";

// THIS is the post processing filter class
import PostEffect from "./PostEffect.js";
let postProcessing;

// variables
let scene, camera, renderer;

let particlesystem;

let resl;

window.onload = function() {

        scene = new THREE.Scene();

        camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000000);
        scene.add(camera);

        renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true,
            preserveDrawingBuffer: true,
            premultipliedAlpha: rrue
        });
        renderer.setPixelRatio(1);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0xececec, 1.0);
        document.body.appendChild(renderer.domElement);

        resl = new THREE.Vector2(window.innerWidth, window.innerHeight);

        particlesystem = new ParticleSystem(resl, e);
        scene.add(particlesystem.ParticleSystem);

        // Here is the post processing class filter initialization
        postProcessing = new PostEffect(renderer, scene, camera, resl);

        animate();
}

function update() {
    // update the particle system
    particlesystem.update(performance.now());
};

function animate() {

    requestAnimationFrame(animate);

    // update and render
    update();

    renderer.render(scene, camera);

    // run the post processing shader after the renderer
    postProcessing.runShader();
};

This is the result that I get when using the classic approach of adding the library and js file to the index.html (and also the expected result).
old_postprocessing

These are the results with the ES6 syntax, both without (the first one) and with the postprocessing filter.
without_postprocessing
with_postprocessing

Can you replace this line with import * as THREE from "three";. Do you see any improvements?

Keep in mind the npm postprocessing library is not a 1:1 match with three.js post processing files. There are a number of subtle but important differences. In somce cases parameters and settings are not available or not the the same.
Alpha is only saved IF the buffers are set for RGBA.
By default all buffers are 8bit per color. You can change that with three.js (needed when doing multipel passes of color passes) but not with the postprocessing library.

@Mugen87 thank you for the help, but I got the same result.

@scottpix so according to what you said, it is impossible it would ever work in any way? Are there alternatives? Or I will try to convert the examples for EffectComposer and RenderPass/ShaderPass in modules by myself. Thank you for the explanation.

Instead of using the postprocessing package, you can import these classes from the three package, too:

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';

This approach is actually highly recommended instead of using third-party npm packages.

@Mugen87 this was my first approach, but it gives me an error during the building with gulp:

[15:26:20] Using gulpfile F:\Projects\ParticleBackground\gulpfile.js
[15:26:20] Starting ‘build’…
[15:26:23] ‘build’ errored after 2.19 s
[15:26:23] Error:
F:\Projects\ParticleBackground\node_modules\three\examples\jsm\postprocessing\EffectComposer.js:5
import {
^
ParseError: ‘import’ and ‘export’ may appear only with ‘sourceType: module’
at formatError (C:\Users\Kesson\AppData\Roaming\npm\node_modules\gulp-cli\lib\versioned^4.0.0\format-error.js:21:10)
at Gulp. (C:\Users\Kesson\AppData\Roaming\npm\node_modules\gulp-cli\lib\versioned^4.0.0\log\events.js:33:15)
at Gulp.emit (events.js:203:15)
at Gulp.EventEmitter.emit (domain.js:448:20)
at Object.error (F:\Projects\ParticleBackground\node_modules\undertaker\lib\helpers\createExtensions.js:61:10)
at handler (F:\Projects\ParticleBackground\node_modules\now-and-later\lib\map.js:50:14)
at f (F:\Projects\ParticleBackground\node_modules\once\once.js:25:25)
at f (F:\Projects\ParticleBackground\node_modules\once\once.js:25:25)
at tryCatch (F:\Projects\ParticleBackground\node_modules\async-done\index.js:24:15)
at done (F:\Projects\ParticleBackground\node_modules\async-done\index.js:40:12)

Sorry, I’m not familiar with Gulp but I’m sure this is some sort of build configuration issue, see

1 Like

Main point is PostProcess library is not the exact same thing as three.js postprocessing routines.
From their notes’ ShaderPass should not be used to create multiple chained effects. For a more

  • efficient solution, please refer to the {@link EffectPass}.’

You also have to set a special alpha flag if you wantto use/keep the alpha.

PostProcesing library also has a blend/opacticy setting and mode for each pass.
If those aren’t set up properly you won’t get the correct results. The defaults were not neutral the last time I used it.

OK , after a long pause I am finally working again on this project.

I managed to make the original three.js effectcomposer by using the esmify library (looks like it is an incompatibility in babel with es2015, but I am still investigating as this is quite new field for me).

But still, I get the same result as the PostProcessing library.

Here is the code for the post effect:

import * as THREE from "three";

import {
    EffectComposer
} from 'three/examples/jsm/postprocessing/EffectComposer.js';
import {
    RenderPass
} from 'three/examples/jsm/postprocessing/RenderPass.js';
import {
    ShaderPass
} from 'three/examples/jsm/postprocessing/ShaderPass.js';

const glsl = require("glslify");

export default class PostEffect {

    constructor(renderer, scene, camera, resolution) {

        this.mousePos = new THREE.Vector2(resolution.x / 2, resolution.y / 2);
        this.res = resolution;
        this.clock = new THREE.Clock();
        this.drawShader = new THREE.ShaderMaterial({
            defines: {
                LABEL: "value"
            },
            uniforms: {
                tDiffuse: {
                    type: "t",
                    value: null
                },
                iResolution: {
                    type: "v2",
                    value: resolution
                },
                mouse: {
                    type: "v2",
                    value: this.mousePos
                },
            },
            vertexShader: glsl.file("./Shaders/Post_effect/vertexShader.glsl"),
            fragmentShader: glsl.file("./Shaders/Post_effect/fragmentShadercopy.glsl")
        });

        const width = resolution.x;// || 1;
        const height = resolution.y;// || 1;
        const parameters = {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
            format: THREE.RGBAFormat,
            stencilBuffer: !1
        };

        const renderTarget = new THREE.WebGLRenderTarget(width, height, parameters);

        this.composer = new EffectComposer(renderer, renderTarget);
        this.renderpass = new RenderPass(scene, camera);
        this.composer.addPass(this.renderpass);

        this.blurPass = new ShaderPass(this.drawShader, "tDiffuse");
        this.blurPass.renderToScreen = true;

        this.composer.addPass(this.blurPass);
    }

    runShader(m) {
        this.drawShader.uniforms.mouse.value = m;
        this.composer.render(this.clock.getDelta());
    }
}