How to Use CopyShader in Three.js EffectComposer

This article explains how to use the CopyShader in Three.js to render the final post-processing results of an EffectComposer directly to the screen. You will learn how to set up the post-processing pipeline, configure a ShaderPass with the CopyShader, and update your animation loop to output the composed frame to the canvas.

Understanding the Role of CopyShader

In Three.js, the EffectComposer manages a chain of post-processing passes. It uses two internal render targets (read and write buffers) to perform double-buffering. To display the final result of these combined effects on the screen rather than keeping them in an off-screen buffer, you must output the final pass to the canvas.

The CopyShader is a simple utility shader that copies the contents of one texture buffer directly to another (or to the screen). By wrapping CopyShader in a ShaderPass and setting its renderToScreen property to true, you instruct the composer to write the final image data to the device screen.

Step-by-Step Implementation

1. Import the Necessary Modules

First, import the required post-processing classes and shaders from the Three.js examples folder:

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';
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js';

2. Initialize the EffectComposer

Create an instance of EffectComposer by passing your WebGLRenderer to it.

const renderer = new THREE.WebGLRenderer({ canvas });
const composer = new EffectComposer(renderer);

3. Add the RenderPass

The first pass in your composer chain should be a RenderPass. This pass renders your basic 3D scene and camera view into the composer’s internal buffers.

const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

4. Create and Configure the CopyShader Pass

To output the final result to the screen, instantiate a ShaderPass using the CopyShader. Ensure you set the renderToScreen property of this pass to true.

const copyPass = new ShaderPass(CopyShader);
copyPass.renderToScreen = true;
composer.addPass(copyPass);

If you have other effect passes (like bloom, film grain, or depth-of-field), add them to the composer before adding the copyPass. The copyPass should always be the last pass added to the chain.

5. Update the Animation Loop

To display the post-processed frame, replace your standard renderer.render(scene, camera) call in your animation loop with composer.render().

function animate() {
    requestAnimationFrame(animate);

    // Perform any object rotations or animations here

    // Render the scene through the composer
    composer.render();
}

animate();

Important Note for Modern Three.js Versions

In recent versions of Three.js (r124 and newer), the EffectComposer automatically sets the last pass in the chain to render to the screen. If you are using a modern version of Three.js, you often do not need to manually add a CopyShader at the end of your chain, as the composer handles this internally. However, manually using the CopyShader remains necessary if you are working with legacy codebases, custom rendering chains, or multiple composers that blend together.