({ renderer, scene, camera, samples = 4 })
| 70 | }; |
| 71 | |
| 72 | export function createPostFX({ renderer, scene, camera, samples = 4 }) { |
| 73 | const w = window.innerWidth; |
| 74 | const h = window.innerHeight; |
| 75 | const maxSamples = renderer.capabilities.maxSamples; // WebGL2 hardware cap |
| 76 | |
| 77 | // MSAA can only happen INSIDE the composer — the renderer's own `antialias` |
| 78 | // flag is ignored once we render through render targets. So we give the |
| 79 | // composer a multisampled float target; `setSamples` rebuilds it on demand. |
| 80 | function makeRenderTarget(n) { |
| 81 | const dpr = renderer.getPixelRatio(); |
| 82 | const rt = new THREE.WebGLRenderTarget( |
| 83 | Math.floor(window.innerWidth * dpr), |
| 84 | Math.floor(window.innerHeight * dpr), |
| 85 | { type: THREE.HalfFloatType } |
| 86 | ); |
| 87 | rt.samples = Math.min(n, maxSamples); |
| 88 | return rt; |
| 89 | } |
| 90 | |
| 91 | const composer = new EffectComposer(renderer, makeRenderTarget(samples)); |
| 92 | composer.addPass(new RenderPass(scene, camera)); |
| 93 | |
| 94 | // Clouds (see clouds.js): a low-res volumetric raymarch composited at full |
| 95 | // res, right after Render so DoF/bloom/grade all affect it. |
| 96 | const fog = createClouds({ renderer, scene, camera }); |
| 97 | composer.addPass(fog.compositePass); |
| 98 | |
| 99 | const bokeh = new BokehPass(scene, camera, { |
| 100 | focus: 9.7, |
| 101 | aperture: 0.0012, |
| 102 | maxblur: 0.005, |
| 103 | }); |
| 104 | composer.addPass(bokeh); |
| 105 | |
| 106 | const bloom = new UnrealBloomPass(new THREE.Vector2(w, h), 0.04, 0.7, 0.62); |
| 107 | composer.addPass(bloom); |
| 108 | |
| 109 | composer.addPass(new OutputPass()); // tone mapping + sRGB |
| 110 | |
| 111 | const grade = new ShaderPass(FilmGradeShader); |
| 112 | composer.addPass(grade); // last -> renders to screen, in display space |
| 113 | |
| 114 | function setSize(width, height) { |
| 115 | composer.setSize(width, height); |
| 116 | bloom.setSize(width, height); |
| 117 | fog.setSize(); |
| 118 | } |
| 119 | setSize(w, h); |
| 120 | |
| 121 | // Swap the composer's ping-pong buffers for ones with a new sample count. |
| 122 | function setSamples(n) { |
| 123 | composer.reset(makeRenderTarget(n)); // disposes the old targets for us |
| 124 | setSize(window.innerWidth, window.innerHeight); // re-sync pass sizes |
| 125 | } |
| 126 | |
| 127 | return { |
| 128 | composer, |
| 129 | bokeh, |
no test coverage detected