i can’t get multiple passes working together for some reason, chromaticaberrationpass speaks for itself and transitionpass is just an empty copypass currently, both work fine seperately but once i enable both the screen renders black. been stuck for some time and really don’t know what to do. am i doing something wrong? can’t be a glsl issue since the first pass literally just copies readbuffer’s texture onto the screen

The code doesn’t get much more complex than what you have - you just setup a chain of passes and call composer.render() - first pass should be RenderPass , and all subsequent passes must have .renderToScreen set to false in order not to override the frame (they can still render to render targets, if necessary.)

  • Which version of three are you using?
  • Are you using built-in postprocessing or an external postprocessing library?
  • What’s the content of the passes you’re using? Are you importing necessary shaders? (postprocessing often relies on you also importing CopyShader and other necessary external shaders to work.)
  • Are there any errors in the devtools console? (passes sometimes require you to pass params as an empty {} object, instead of not passing anything at all, which may cause the code to just throw right away.)
  • 0.159.0
  • no, everything written by me
  • chromaticaberrationpass code below, the other one is similar and works when it’s the only pass in the chain so i don’t think its contents are important
  • no errors unfortunately, an error would make my life so much easier right now :smiley:
  • `import * as THREE from ‘three’;
    import { Pass, FullScreenQuad } from ‘three/addons/postprocessing/Pass.js’;

    class ChromaticAberrationPass extends Pass {

    constructor( ) {
    	super();
    	this.material = this.getVerticalBoxBlurMaterial();
    	this.fsQuad = new FullScreenQuad( this.material);
    render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
    	this.material.uniforms['tDiffuse'].value = readBuffer.texture;
        renderer.setRenderTarget( null );
        renderer.clear();
    	this.fsQuad.render( renderer );
    dispose() {
    	this.material.dispose();
    	this.fsQuad.dispose();
    getVerticalBoxBlurMaterial()
    	return new THREE.ShaderMaterial({
            defines: {
                // 0: NONE, 1: RGB, 2: RYGCBV
                BAND_MODE: 2,
                CHROMA_SAMPLES: 1,
            uniforms: {
                tDiffuse: { value: null },
                baseIor: { value: 1.1 },
                bandOffset: { value: 0.02 },
                jitterIntensity: { value: 0.0 },
                jitterOffset: { value: 0.0 }
            vertexShader: /* glsl */`
                varying vec2 vUv;
                varying vec2 viewDir;
                float map(float value, float min1, float max1, float min2, float max2) {
                    return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
                void main() {
                    vUv = uv;
                    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                    //viewDir = normalize( ( modelViewMatrix * vec4( position, 1.0 ) ).xyz );
                    viewDir = uv;
            fragmentShader: /* glsl */`
            uniform sampler2D tDiffuse;
            varying vec2 vUv;
            varying vec2 viewDir;
            float map(float value, float min1, float max1, float min2, float max2) {
                return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
            vec2 map(vec2 value, vec2 min1, vec2 max1, vec2 min2, vec2 max2) {
                return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
            void main()
                float rior = 10.0;
                float gior = 10.0;
                float bior = 10.0;
                vec3 normal = vec3( ( map(vUv.xy,vec2(0.0),vec2(1.0),vec2(-1.0),vec2(1.0)) ), 1.0 );
    		    normal.z = 1.0;
    		    normal = normalize( normal );
                vec3 mappedNormal = vec3(map(normal.xy,vec2(0.0),vec2(1.0),vec2(-0.0),vec2(1.0)), 1.0);
                vec3 uvRed = refract(mappedNormal, vec3(0.0, 0.0, 1.0), rior);
                vec2 reMappedNormalRed = map(uvRed.xy / rior,vec2(-1.0),vec2(1.0),vec2(0.0),vec2(1.0));
                vec3 uvGreen = refract(mappedNormal, vec3(0.0, 0.0, 1.0), gior);
                vec2 reMappedNormalGreen = map(uvGreen.xy / gior,vec2(-1.0),vec2(1.0),vec2(0.0),vec2(1.0));
                vec3 uvBlue = refract(mappedNormal, vec3(0.0, 0.0, 1.0), bior);
                vec2 reMappedNormalBlue = map(uvBlue.xy / bior,vec2(-1.0),vec2(1.0),vec2(0.0),vec2(1.0));
                vec2 finalizedUvRed = mix(reMappedNormalRed, vUv, pow(abs(1.0 - abs(map(vUv.y, 0.0, 1.0, -0.88, 0.88))),0.5));
                vec2 finalizedUvGreen = mix(reMappedNormalGreen, vUv, pow(abs(1.0 - abs(map(vUv.y, 0.0, 1.0, -0.9, 0.9))),0.5));
                vec2 finalizedUvBlue = mix(reMappedNormalBlue, vUv, pow(abs(1.0 - abs(map(vUv.y, 0.0, 1.0, -0.92, 0.92))),0.5));
                float red = texture2D(tDiffuse,finalizedUvRed).r;
                float green = texture2D(tDiffuse,finalizedUvGreen).g;
                float blue = texture2D(tDiffuse,finalizedUvBlue).b;
                gl_FragColor = vec4(red, green, blue, 1.0);
                //texture2D(tDiffuse,finalizedUv);
    

    export { ChromaticAberrationPass };

    If I were you, I’d first try with a pass-friendly render method (i.e. a method that renders off-screen when it is not the final pass, and on-screen if it is final pass). Something like this:

    render( renderer, writeBuffer, readBuffer /*, deltaTime , maskActive */ ) {
    	this.material.uniforms['tDiffuse'].value = readBuffer.texture;
    	if ( this.renderToScreen ) {
    		renderer.setRenderTarget( null );
    		this.fsQuad.render( renderer );
    	} else {
    		renderer.setRenderTarget( writeBuffer );
    		if ( this.clear ) renderer.clear();
    		this.fsQuad.render( renderer );
    

    Note: renderToScreen is managed by the effect composer, so it is already defined.