Introduce renderer.setPostProcessing()
From https://github.com/mrdoob/three.js/pull/15840#issuecomment-503810517
I'm working on renderer.setPostProcessing() API trial, that is supporting post processing API in core. I want feedbacks so this PR is draft for now.
Suggested API
// enable post processing
renderer.setPostProcessing( [ effect1, effect2 ] ); // effects are existing Pass instances
// disable post processing
renderer.setPostProcessing( [] );
Example
Unreal bloom post processing example.
import {
PerspectiveCamera,
Scene,
WebGLRenderer,
...
} from '../build/three.module.js';
import { UneralBloomPass } from './jsm/postprocessing/UnrealBloomPass.js';
...
var bloomPass = new UnrealBloomPass( params );
renderer.setPostProcessing( [ bloomPass ] );
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function render() {
requestAnimationFrame( render );
renderer.render( scene, camera );
}
window.addEventListener( 'resize', onWindowResize );
render();
Changes
I'm trying to move EffectComposer in core so far.
- Move
EffectComposer.jsfromexamplestosrc. Also movePass.js,ShaderPass.js,MaskPass.js, andCopyShader.js,EffectComposerrequires, tosrc. Other concrete passes (e.g.UnrealBloomPass) are still underexamples. - Export
Pass,ShaderPass,MaskPass, andCopyShaderfromsrc/Three.js
src/renderers/postprocessing
- EffectComposer.js
- passes/
- Pass.js, ShaderPass.js, MaskPass.js
- shaders/
- CopyShader.js
WebGLRenderer- creates
EffectComposerinstance and holds it inside. - has
renderer.setPostProcessing()which adds passes to the composer. renderer.render()renders a scene to composer's read buffer and then runs composer if the composer has passes.- manages composer's size, updating composer's size when renderer size is updated.
- creates
- Regarding
examples, I updatedwebgl_postprocessing,webgl_postprocessing_afterimage, andwebgl_postprocessing_unreal_bloomin this PR so far.
Advantages
- Not many brand new code by reusing
EffectComposer. - User code will be simpler with the new API because they no longer need to take care composer (see
webgl_postprocessing)
Discussion
- The new API expects that first rendering a scene and then applying post processing effects on it. It doesn't cover all the current
EffectComposeruse cases. In some use casesRenderPass(rendering a scene) is a second or later pass (e.g.webgl_postprocessing_backgroundsexample).
composer.addPass( fooPass );
composer.addPass( renderPass ); // rendering a scene
composer.addPass( barPass );
There might be more complex cases. For compatibility, I'm thinking about exporting EffectComposer from src/Three.js. Good point is users who still want to directly use EffectComposer for complex use cases, they can keep using it without big their code change. They just needs to change the location where importing the composer from. Bad point is there will be two ways to apply post processing for users, renderer.setPostProcessing() and EffectComposer. It might be confusing.
/cc @mrdoob
Other than developer convenience and a simpler API, are there more advantages to doing this? I don't know the current state of using EffectComposer with WebXR, for example... is that hard now? easier or harder with this API?
If we do prefer the proposed API but simply want to keep EffectComposer out of core, one option might be to let WebGLRenderer be given a composer by the user. Similar to how Loader.setHandlers(...) lets users "install" loaders that aren't in core. For example:
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
// ...
renderer.setEffectComposer( new EffectComposer() );
renderer.setPasses( [ ... ] );
Thanks for the comments. Before replying I'd like to ask @mrdoob if the suggested API is what he expects because the new API is originally his idea and if I misunderstood his idea the discussion can be mess so just in case.
Can't comment on the direction to take, but PostProcessing could do with some TLC. Some elements are quite confusing.
For example variables are named "readBuffer" and "writeBuffer", but both are then written to and read to! I propose "bufferA" and bufferB".
Also RenderPass.render takes readBuffer as a parameter, but writes to it! This dirties RenderPass with logic from EffectComposer. I propose
render: function ( renderer, unused, writeBuffer /*, deltaTime, maskActive */ ) {
I understand the author was perhaps trying to emulate a "function overriding" mechanism, but that feels constructed.
@donutcoffee I think your post better fits to the following discussion: #12115
Closing in favor of https://github.com/mrdoob/three.js/issues/12115#issuecomment-2429240760