From e0e6213b1e18b5070284f4e505e1d8b5b8f1b670 Mon Sep 17 00:00:00 2001 From: ChrisBeWithYou Date: Fri, 26 Jun 2026 00:29:19 -0500 Subject: [PATCH] =?UTF-8?q?feat(highway=5F3d):=20#4=20bloom=20+=20ACES=20?= =?UTF-8?q?=E2=80=94=20vendored=20Three.js=20postprocessing,=20perf-gated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single biggest fidelity lever from the charrette. Core had only three.module.min.js (no postprocessing addons), so this vendors the r170 EffectComposer/RenderPass/UnrealBloomPass/OutputPass + their shader deps into static/vendor/three/addons/, with every `from 'three'` rewritten to the SAME vendored three (../../three.module.min.js) so the addons share the plugin's three instance (a CDN copy would be a second, non-interoperable module). highway_3d wiring: - Lazy-loads the addons only when the new `bloom` setting is on (dynamic import), builds EffectComposer(RenderPass → UnrealBloomPass(strength .65/radius .5/ threshold .82 — high so only emissive gems + the hit flash bloom) → OutputPass). - Render loop uses composer.render() with ACES tone-mapping when bloom is active, else the unchanged direct ren.render() with NoToneMapping (bloom-off = today's look). - Perf-gated: OFF in splitscreen; graceful fallback to direct render if the modules or composer fail; composer.setSize on canvas resize; disposed on teardown. - settings.html: "Glow bloom" toggle (default on). Verified the import chain resolves + renders via a same-origin module-load test (EffectComposer built + a bloom frame rendered, three r170). Charrette status: 7/8 (only #5's early/late timing tint remains — a notedetect verdict-field change, outside the highway). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq --- plugins/highway_3d/screen.js | 51 ++- plugins/highway_3d/settings.html | 10 + .../addons/postprocessing/EffectComposer.js | 231 ++++++++++ .../three/addons/postprocessing/MaskPass.js | 104 +++++ .../three/addons/postprocessing/OutputPass.js | 97 ++++ .../three/addons/postprocessing/Pass.js | 95 ++++ .../three/addons/postprocessing/RenderPass.js | 99 +++++ .../three/addons/postprocessing/ShaderPass.js | 77 ++++ .../addons/postprocessing/UnrealBloomPass.js | 415 ++++++++++++++++++ .../vendor/three/addons/shaders/CopyShader.js | 45 ++ .../shaders/LuminosityHighPassShader.js | 64 +++ .../three/addons/shaders/OutputShader.js | 85 ++++ 12 files changed, 1370 insertions(+), 3 deletions(-) create mode 100644 static/vendor/three/addons/postprocessing/EffectComposer.js create mode 100644 static/vendor/three/addons/postprocessing/MaskPass.js create mode 100644 static/vendor/three/addons/postprocessing/OutputPass.js create mode 100644 static/vendor/three/addons/postprocessing/Pass.js create mode 100644 static/vendor/three/addons/postprocessing/RenderPass.js create mode 100644 static/vendor/three/addons/postprocessing/ShaderPass.js create mode 100644 static/vendor/three/addons/postprocessing/UnrealBloomPass.js create mode 100644 static/vendor/three/addons/shaders/CopyShader.js create mode 100644 static/vendor/three/addons/shaders/LuminosityHighPassShader.js create mode 100644 static/vendor/three/addons/shaders/OutputShader.js diff --git a/plugins/highway_3d/screen.js b/plugins/highway_3d/screen.js index 0e35e2f..114d3fa 100644 --- a/plugins/highway_3d/screen.js +++ b/plugins/highway_3d/screen.js @@ -1768,7 +1768,7 @@ return _bgBandsCache; } - const BG_DEFAULTS = { style: 'particles', intensity: 0.5, reactive: true, palette: 'default', bgTheme: 'default', hwTheme: 'default', showFretOnNote: true, fretNumberGhostScope: 'chords', cameraSmoothing: 0.5, zoomSmoothing: 0.5, tiltSmoothing: 0.5, cameraLockLow: false, cameraLockZoom: 0.5, cameraMode: 'lookahead', nutHeadstockVisible: true, tuningLabelsVisible: true, nutColor: '#f5f3f0', headstockColor: '#d4b48a', textSize: 0.5, vibrancy: 0.85, glow: 0.25, customImageDataUrl: '', customImageName: '', customVideoName: '', chordDiagramVisible: true, chordDiagramSize: 0.5, chordDiagramPosition: 'tl', fretColumnMarkerCadence: 1, projectionVisible: true, inlayLabelsVisible: false, sectionLabelsOnHighway: false, sectionHudVisible: false, sectionHudPosition: 'tr', sectionHudSize: 0.5, toneHudVisible: false, toneHudPosition: 'tl', toneHudSize: 0.5, fpsVisible: false, fretDividersVisible: true, slideArrowApproachVisible: true, slideArrowNeckVisible: true, slideArrowChainPreviewVisible: true, hitFx: 0.7, cinematic: true, verdictMarks: true, timingFx: true, streakFx: true }; + const BG_DEFAULTS = { style: 'particles', intensity: 0.5, reactive: true, palette: 'default', bgTheme: 'default', hwTheme: 'default', showFretOnNote: true, fretNumberGhostScope: 'chords', cameraSmoothing: 0.5, zoomSmoothing: 0.5, tiltSmoothing: 0.5, cameraLockLow: false, cameraLockZoom: 0.5, cameraMode: 'lookahead', nutHeadstockVisible: true, tuningLabelsVisible: true, nutColor: '#f5f3f0', headstockColor: '#d4b48a', textSize: 0.5, vibrancy: 0.85, glow: 0.25, customImageDataUrl: '', customImageName: '', customVideoName: '', chordDiagramVisible: true, chordDiagramSize: 0.5, chordDiagramPosition: 'tl', fretColumnMarkerCadence: 1, projectionVisible: true, inlayLabelsVisible: false, sectionLabelsOnHighway: false, sectionHudVisible: false, sectionHudPosition: 'tr', sectionHudSize: 0.5, toneHudVisible: false, toneHudPosition: 'tl', toneHudSize: 0.5, fpsVisible: false, fretDividersVisible: true, slideArrowApproachVisible: true, slideArrowNeckVisible: true, slideArrowChainPreviewVisible: true, hitFx: 0.7, cinematic: true, verdictMarks: true, timingFx: true, streakFx: true, bloom: true }; // User-selectable, persistable bg styles — must mirror settings.html's // VALID_STYLES. 'venue' is deliberately NOT here: it is an internal effective // style reached only via _venueSceneOverride (the viz-picker Venue flow), so @@ -2115,7 +2115,7 @@ // means (fall back to default rather than silently flipping to // false). Add new boolean keys to BG_DEFAULTS and they pick this // up via the dispatch below. - const _BG_BOOL_KEYS = new Set(['reactive', 'showFretOnNote', 'cameraLockLow', 'inlayLabelsVisible', 'sectionLabelsOnHighway', 'sectionHudVisible', 'nutHeadstockVisible', 'tuningLabelsVisible', 'projectionVisible', 'chordDiagramVisible', 'fpsVisible', 'toneHudVisible', 'fretDividersVisible', 'slideArrowApproachVisible', 'slideArrowNeckVisible', 'slideArrowChainPreviewVisible', 'cinematic', 'verdictMarks', 'timingFx', 'streakFx']); + const _BG_BOOL_KEYS = new Set(['reactive', 'showFretOnNote', 'cameraLockLow', 'inlayLabelsVisible', 'sectionLabelsOnHighway', 'sectionHudVisible', 'nutHeadstockVisible', 'tuningLabelsVisible', 'projectionVisible', 'chordDiagramVisible', 'fpsVisible', 'toneHudVisible', 'fretDividersVisible', 'slideArrowApproachVisible', 'slideArrowNeckVisible', 'slideArrowChainPreviewVisible', 'cinematic', 'verdictMarks', 'timingFx', 'streakFx', 'bloom']); function _bgCoerceBool(val, fallback) { if (val === 'true' || val === '1') return true; if (val === 'false' || val === '0') return false; @@ -2264,6 +2264,7 @@ window.h3dBgSetVerdictMarks = (v) => _bgWriteGlobal('verdictMarks', !!v); window.h3dBgSetTimingFx = (v) => _bgWriteGlobal('timingFx', !!v); window.h3dBgSetStreakFx = (v) => _bgWriteGlobal('streakFx', !!v); + window.h3dBgSetBloom = (v) => _bgWriteGlobal('bloom', !!v); window.h3dBgSetToneHudVisible = (v) => _bgWriteGlobal('toneHudVisible', !!v); window.h3dBgSetToneHudPosition = (v) => _bgWriteGlobal('toneHudPosition', v); window.h3dBgSetToneHudSize = (v) => _bgWriteGlobal('toneHudSize', v); @@ -3562,6 +3563,8 @@ let _verdictMarks = BG_DEFAULTS.verdictMarks; let _timingFx = BG_DEFAULTS.timingFx; let _streakFx = BG_DEFAULTS.streakFx; + let _bloom = BG_DEFAULTS.bloom; + let _composer = null, _bloomPass = null, _bloomLoad = null, _bloomW = 0, _bloomH = 0; let _strikeLine = null; // #1 glowing bar at the hit line let _ndHitFlash = 0; // eased hit pulse for the strike line let _ndMissFlash = 0; // eased miss pulse for the strike line @@ -7383,6 +7386,7 @@ _verdictMarks = _bgReadSetting(panelKey, 'verdictMarks'); _timingFx = _bgReadSetting(panelKey, 'timingFx'); _streakFx = _bgReadSetting(panelKey, 'streakFx'); + _bloom = _bgReadSetting(panelKey, 'bloom'); _applyCinematic(); fpsVisible = _bgReadSetting(panelKey, 'fpsVisible'); fretDividersVisible = _bgReadSetting(panelKey, 'fretDividersVisible'); @@ -7861,6 +7865,33 @@ _sparkPts.geometry.attributes.color.needsUpdate = true; _sparkPts.visible = any; } + // #4 Bloom: lazy-load the vendored postprocessing addons and build an + // EffectComposer (RenderPass -> UnrealBloomPass -> OutputPass/ACES). Returns + // the composer once ready, or null (caller falls back to a direct render). + function _bloomEnsure() { + if (_composer) return _composer; + if (_bloomLoad || !ren || !scene || !cam) return null; + const A = '/static/vendor/three/addons/'; + _bloomLoad = Promise.all([ + import(A + 'postprocessing/EffectComposer.js'), + import(A + 'postprocessing/RenderPass.js'), + import(A + 'postprocessing/UnrealBloomPass.js'), + import(A + 'postprocessing/OutputPass.js'), + ]).then(([EC, RP, UB, OP]) => { + try { + const sz = canvasSize(highwayCanvas) || { w: 1280, h: 720 }; + const w = Math.max(2, sz.w | 0), h = Math.max(2, sz.h | 0); + const comp = new EC.EffectComposer(ren); + comp.addPass(new RP.RenderPass(scene, cam)); + _bloomPass = new UB.UnrealBloomPass(new T.Vector2(w, h), 0.65, 0.5, 0.82); // strength, radius, threshold (high → only emissive blooms) + comp.addPass(_bloomPass); + comp.addPass(new OP.OutputPass()); + comp.setSize(w, h); + _bloomW = w; _bloomH = h; _composer = comp; + } catch (e) { console.warn('[3D-Hwy] bloom init failed', e); _composer = null; } + }).catch((e) => console.warn('[3D-Hwy] bloom modules failed', e)); + return null; + } function buildBoard() { // Dispose before clearing (traverse: nut/headstock may live in a Group). while (fretG.children.length) { @@ -14119,6 +14150,7 @@ _ownedSharedGeos.length = 0; txtCache = {}; if (_sparkPts) { try { _sparkPts.geometry.dispose(); _sparkPts.material.dispose(); } catch (e) {} _sparkPts = null; } + if (_composer) { try { _composer.dispose(); if (_bloomPass && _bloomPass.dispose) _bloomPass.dispose(); } catch (e) {} _composer = null; _bloomPass = null; } if (ren) { ren.dispose(); ren = null; } scene = cam = noteG = beatG = lblG = fretG = tuningLblG = null; ambLight = dirLight = null; @@ -14460,7 +14492,20 @@ _ndHitFlash *= 0.82; _ndMissFlash *= 0.82; } } - pbBeg(6); ren.render(scene, cam); pbEnd(6); + { + const comp = (_bloom && !_ssActive()) ? _bloomEnsure() : null; + if (comp) { + const bsz = canvasSize(highwayCanvas); + if (bsz && bsz.w > 0 && bsz.h > 0 && (bsz.w !== _bloomW || bsz.h !== _bloomH)) { + comp.setSize(bsz.w | 0, bsz.h | 0); _bloomW = bsz.w | 0; _bloomH = bsz.h | 0; + } + if (ren.toneMapping !== T.ACESFilmicToneMapping) ren.toneMapping = T.ACESFilmicToneMapping; + pbBeg(6); comp.render(); pbEnd(6); + } else { + if (ren.toneMapping !== T.NoToneMapping) ren.toneMapping = T.NoToneMapping; + pbBeg(6); ren.render(scene, cam); pbEnd(6); + } + } if (lyricsCtx && lyricsCanvas) { lyricsCtx.clearRect(0, 0, lyricsCanvas.width, lyricsCanvas.height); // Capture the actual lyrics-banner bottom so overlay cards diff --git a/plugins/highway_3d/settings.html b/plugins/highway_3d/settings.html index 86bb370..bbed3ad 100644 --- a/plugins/highway_3d/settings.html +++ b/plugins/highway_3d/settings.html @@ -621,6 +621,16 @@ onchange="window.h3dBgSetVerdictMarks && window.h3dBgSetVerdictMarks(this.checked)" class="accent-accent mt-0.5"> + +
+ + +
diff --git a/static/vendor/three/addons/postprocessing/EffectComposer.js b/static/vendor/three/addons/postprocessing/EffectComposer.js new file mode 100644 index 0000000..8953c56 --- /dev/null +++ b/static/vendor/three/addons/postprocessing/EffectComposer.js @@ -0,0 +1,231 @@ +import { + Clock, + HalfFloatType, + NoBlending, + Vector2, + WebGLRenderTarget +} from '../../three.module.min.js'; +import { CopyShader } from '../shaders/CopyShader.js'; +import { ShaderPass } from './ShaderPass.js'; +import { MaskPass } from './MaskPass.js'; +import { ClearMaskPass } from './MaskPass.js'; + +class EffectComposer { + + constructor( renderer, renderTarget ) { + + this.renderer = renderer; + + this._pixelRatio = renderer.getPixelRatio(); + + if ( renderTarget === undefined ) { + + const size = renderer.getSize( new Vector2() ); + this._width = size.width; + this._height = size.height; + + renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType } ); + renderTarget.texture.name = 'EffectComposer.rt1'; + + } else { + + this._width = renderTarget.width; + this._height = renderTarget.height; + + } + + this.renderTarget1 = renderTarget; + this.renderTarget2 = renderTarget.clone(); + this.renderTarget2.texture.name = 'EffectComposer.rt2'; + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + this.renderToScreen = true; + + this.passes = []; + + this.copyPass = new ShaderPass( CopyShader ); + this.copyPass.material.blending = NoBlending; + + this.clock = new Clock(); + + } + + swapBuffers() { + + const tmp = this.readBuffer; + this.readBuffer = this.writeBuffer; + this.writeBuffer = tmp; + + } + + addPass( pass ) { + + this.passes.push( pass ); + pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + insertPass( pass, index ) { + + this.passes.splice( index, 0, pass ); + pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + removePass( pass ) { + + const index = this.passes.indexOf( pass ); + + if ( index !== - 1 ) { + + this.passes.splice( index, 1 ); + + } + + } + + isLastEnabledPass( passIndex ) { + + for ( let i = passIndex + 1; i < this.passes.length; i ++ ) { + + if ( this.passes[ i ].enabled ) { + + return false; + + } + + } + + return true; + + } + + render( deltaTime ) { + + // deltaTime value is in seconds + + if ( deltaTime === undefined ) { + + deltaTime = this.clock.getDelta(); + + } + + const currentRenderTarget = this.renderer.getRenderTarget(); + + let maskActive = false; + + for ( let i = 0, il = this.passes.length; i < il; i ++ ) { + + const pass = this.passes[ i ]; + + if ( pass.enabled === false ) continue; + + pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); + pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); + + if ( pass.needsSwap ) { + + if ( maskActive ) { + + const context = this.renderer.getContext(); + const stencil = this.renderer.state.buffers.stencil; + + //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); + stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); + + this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); + + //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); + stencil.setFunc( context.EQUAL, 1, 0xffffffff ); + + } + + this.swapBuffers(); + + } + + if ( MaskPass !== undefined ) { + + if ( pass instanceof MaskPass ) { + + maskActive = true; + + } else if ( pass instanceof ClearMaskPass ) { + + maskActive = false; + + } + + } + + } + + this.renderer.setRenderTarget( currentRenderTarget ); + + } + + reset( renderTarget ) { + + if ( renderTarget === undefined ) { + + const size = this.renderer.getSize( new Vector2() ); + this._pixelRatio = this.renderer.getPixelRatio(); + this._width = size.width; + this._height = size.height; + + renderTarget = this.renderTarget1.clone(); + renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + this.renderTarget1.dispose(); + this.renderTarget2.dispose(); + this.renderTarget1 = renderTarget; + this.renderTarget2 = renderTarget.clone(); + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + } + + setSize( width, height ) { + + this._width = width; + this._height = height; + + const effectiveWidth = this._width * this._pixelRatio; + const effectiveHeight = this._height * this._pixelRatio; + + this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); + this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); + + for ( let i = 0; i < this.passes.length; i ++ ) { + + this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); + + } + + } + + setPixelRatio( pixelRatio ) { + + this._pixelRatio = pixelRatio; + + this.setSize( this._width, this._height ); + + } + + dispose() { + + this.renderTarget1.dispose(); + this.renderTarget2.dispose(); + + this.copyPass.dispose(); + + } + +} + +export { EffectComposer }; diff --git a/static/vendor/three/addons/postprocessing/MaskPass.js b/static/vendor/three/addons/postprocessing/MaskPass.js new file mode 100644 index 0000000..b30811c --- /dev/null +++ b/static/vendor/three/addons/postprocessing/MaskPass.js @@ -0,0 +1,104 @@ +import { Pass } from './Pass.js'; + +class MaskPass extends Pass { + + constructor( scene, camera ) { + + super(); + + this.scene = scene; + this.camera = camera; + + this.clear = true; + this.needsSwap = false; + + this.inverse = false; + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + const context = renderer.getContext(); + const state = renderer.state; + + // don't update color or depth + + state.buffers.color.setMask( false ); + state.buffers.depth.setMask( false ); + + // lock buffers + + state.buffers.color.setLocked( true ); + state.buffers.depth.setLocked( true ); + + // set up stencil + + let writeValue, clearValue; + + if ( this.inverse ) { + + writeValue = 0; + clearValue = 1; + + } else { + + writeValue = 1; + clearValue = 0; + + } + + state.buffers.stencil.setTest( true ); + state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); + state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); + state.buffers.stencil.setClear( clearValue ); + state.buffers.stencil.setLocked( true ); + + // draw into the stencil buffer + + renderer.setRenderTarget( readBuffer ); + if ( this.clear ) renderer.clear(); + renderer.render( this.scene, this.camera ); + + renderer.setRenderTarget( writeBuffer ); + if ( this.clear ) renderer.clear(); + renderer.render( this.scene, this.camera ); + + // unlock color and depth buffer and make them writable for subsequent rendering/clearing + + state.buffers.color.setLocked( false ); + state.buffers.depth.setLocked( false ); + + state.buffers.color.setMask( true ); + state.buffers.depth.setMask( true ); + + // only render where stencil is set to 1 + + state.buffers.stencil.setLocked( false ); + state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 + state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); + state.buffers.stencil.setLocked( true ); + + } + +} + +class ClearMaskPass extends Pass { + + constructor() { + + super(); + + this.needsSwap = false; + + } + + render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { + + renderer.state.buffers.stencil.setLocked( false ); + renderer.state.buffers.stencil.setTest( false ); + + } + +} + +export { MaskPass, ClearMaskPass }; diff --git a/static/vendor/three/addons/postprocessing/OutputPass.js b/static/vendor/three/addons/postprocessing/OutputPass.js new file mode 100644 index 0000000..313d745 --- /dev/null +++ b/static/vendor/three/addons/postprocessing/OutputPass.js @@ -0,0 +1,97 @@ +import { + ColorManagement, + RawShaderMaterial, + UniformsUtils, + LinearToneMapping, + ReinhardToneMapping, + CineonToneMapping, + AgXToneMapping, + ACESFilmicToneMapping, + NeutralToneMapping, + SRGBTransfer +} from '../../three.module.min.js'; +import { Pass, FullScreenQuad } from './Pass.js'; +import { OutputShader } from '../shaders/OutputShader.js'; + +class OutputPass extends Pass { + + constructor() { + + super(); + + // + + const shader = OutputShader; + + this.uniforms = UniformsUtils.clone( shader.uniforms ); + + this.material = new RawShaderMaterial( { + name: shader.name, + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + } ); + + this.fsQuad = new FullScreenQuad( this.material ); + + // internal cache + + this._outputColorSpace = null; + this._toneMapping = null; + + } + + render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive */ ) { + + this.uniforms[ 'tDiffuse' ].value = readBuffer.texture; + this.uniforms[ 'toneMappingExposure' ].value = renderer.toneMappingExposure; + + // rebuild defines if required + + if ( this._outputColorSpace !== renderer.outputColorSpace || this._toneMapping !== renderer.toneMapping ) { + + this._outputColorSpace = renderer.outputColorSpace; + this._toneMapping = renderer.toneMapping; + + this.material.defines = {}; + + if ( ColorManagement.getTransfer( this._outputColorSpace ) === SRGBTransfer ) this.material.defines.SRGB_TRANSFER = ''; + + if ( this._toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = ''; + else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = ''; + else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = ''; + else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = ''; + else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = ''; + else if ( this._toneMapping === NeutralToneMapping ) this.material.defines.NEUTRAL_TONE_MAPPING = ''; + + this.material.needsUpdate = true; + + } + + // + + if ( this.renderToScreen === true ) { + + renderer.setRenderTarget( null ); + this.fsQuad.render( renderer ); + + } else { + + renderer.setRenderTarget( writeBuffer ); + if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + this.fsQuad.render( renderer ); + + } + + } + + dispose() { + + this.material.dispose(); + this.fsQuad.dispose(); + + } + +} + +export { OutputPass }; diff --git a/static/vendor/three/addons/postprocessing/Pass.js b/static/vendor/three/addons/postprocessing/Pass.js new file mode 100644 index 0000000..fc1db1f --- /dev/null +++ b/static/vendor/three/addons/postprocessing/Pass.js @@ -0,0 +1,95 @@ +import { + BufferGeometry, + Float32BufferAttribute, + OrthographicCamera, + Mesh +} from '../../three.module.min.js'; + +class Pass { + + constructor() { + + this.isPass = true; + + // if set to true, the pass is processed by the composer + this.enabled = true; + + // if set to true, the pass indicates to swap read and write buffer after rendering + this.needsSwap = true; + + // if set to true, the pass clears its buffer before rendering + this.clear = false; + + // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. + this.renderToScreen = false; + + } + + setSize( /* width, height */ ) {} + + render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { + + console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); + + } + + dispose() {} + +} + +// Helper for passes that need to fill the viewport with a single quad. + +const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + +// https://github.com/mrdoob/three.js/pull/21358 + +class FullscreenTriangleGeometry extends BufferGeometry { + + constructor() { + + super(); + + this.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); + + } + +} + +const _geometry = new FullscreenTriangleGeometry(); + +class FullScreenQuad { + + constructor( material ) { + + this._mesh = new Mesh( _geometry, material ); + + } + + dispose() { + + this._mesh.geometry.dispose(); + + } + + render( renderer ) { + + renderer.render( this._mesh, _camera ); + + } + + get material() { + + return this._mesh.material; + + } + + set material( value ) { + + this._mesh.material = value; + + } + +} + +export { Pass, FullScreenQuad }; diff --git a/static/vendor/three/addons/postprocessing/RenderPass.js b/static/vendor/three/addons/postprocessing/RenderPass.js new file mode 100644 index 0000000..29ec553 --- /dev/null +++ b/static/vendor/three/addons/postprocessing/RenderPass.js @@ -0,0 +1,99 @@ +import { + Color +} from '../../three.module.min.js'; +import { Pass } from './Pass.js'; + +class RenderPass extends Pass { + + constructor( scene, camera, overrideMaterial = null, clearColor = null, clearAlpha = null ) { + + super(); + + this.scene = scene; + this.camera = camera; + + this.overrideMaterial = overrideMaterial; + + this.clearColor = clearColor; + this.clearAlpha = clearAlpha; + + this.clear = true; + this.clearDepth = false; + this.needsSwap = false; + this._oldClearColor = new Color(); + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + const oldAutoClear = renderer.autoClear; + renderer.autoClear = false; + + let oldClearAlpha, oldOverrideMaterial; + + if ( this.overrideMaterial !== null ) { + + oldOverrideMaterial = this.scene.overrideMaterial; + + this.scene.overrideMaterial = this.overrideMaterial; + + } + + if ( this.clearColor !== null ) { + + renderer.getClearColor( this._oldClearColor ); + renderer.setClearColor( this.clearColor, renderer.getClearAlpha() ); + + } + + if ( this.clearAlpha !== null ) { + + oldClearAlpha = renderer.getClearAlpha(); + renderer.setClearAlpha( this.clearAlpha ); + + } + + if ( this.clearDepth == true ) { + + renderer.clearDepth(); + + } + + renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); + + if ( this.clear === true ) { + + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + + } + + renderer.render( this.scene, this.camera ); + + // restore + + if ( this.clearColor !== null ) { + + renderer.setClearColor( this._oldClearColor ); + + } + + if ( this.clearAlpha !== null ) { + + renderer.setClearAlpha( oldClearAlpha ); + + } + + if ( this.overrideMaterial !== null ) { + + this.scene.overrideMaterial = oldOverrideMaterial; + + } + + renderer.autoClear = oldAutoClear; + + } + +} + +export { RenderPass }; diff --git a/static/vendor/three/addons/postprocessing/ShaderPass.js b/static/vendor/three/addons/postprocessing/ShaderPass.js new file mode 100644 index 0000000..8c49b7e --- /dev/null +++ b/static/vendor/three/addons/postprocessing/ShaderPass.js @@ -0,0 +1,77 @@ +import { + ShaderMaterial, + UniformsUtils +} from '../../three.module.min.js'; +import { Pass, FullScreenQuad } from './Pass.js'; + +class ShaderPass extends Pass { + + constructor( shader, textureID ) { + + super(); + + this.textureID = ( textureID !== undefined ) ? textureID : 'tDiffuse'; + + if ( shader instanceof ShaderMaterial ) { + + this.uniforms = shader.uniforms; + + this.material = shader; + + } else if ( shader ) { + + this.uniforms = UniformsUtils.clone( shader.uniforms ); + + this.material = new ShaderMaterial( { + + name: ( shader.name !== undefined ) ? shader.name : 'unspecified', + defines: Object.assign( {}, shader.defines ), + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + } + + this.fsQuad = new FullScreenQuad( this.material ); + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + if ( this.uniforms[ this.textureID ] ) { + + this.uniforms[ this.textureID ].value = readBuffer.texture; + + } + + this.fsQuad.material = this.material; + + if ( this.renderToScreen ) { + + renderer.setRenderTarget( null ); + this.fsQuad.render( renderer ); + + } else { + + renderer.setRenderTarget( writeBuffer ); + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + this.fsQuad.render( renderer ); + + } + + } + + dispose() { + + this.material.dispose(); + + this.fsQuad.dispose(); + + } + +} + +export { ShaderPass }; diff --git a/static/vendor/three/addons/postprocessing/UnrealBloomPass.js b/static/vendor/three/addons/postprocessing/UnrealBloomPass.js new file mode 100644 index 0000000..111c661 --- /dev/null +++ b/static/vendor/three/addons/postprocessing/UnrealBloomPass.js @@ -0,0 +1,415 @@ +import { + AdditiveBlending, + Color, + HalfFloatType, + MeshBasicMaterial, + ShaderMaterial, + UniformsUtils, + Vector2, + Vector3, + WebGLRenderTarget +} from '../../three.module.min.js'; +import { Pass, FullScreenQuad } from './Pass.js'; +import { CopyShader } from '../shaders/CopyShader.js'; +import { LuminosityHighPassShader } from '../shaders/LuminosityHighPassShader.js'; + +/** + * UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a + * mip map chain of bloom textures and blurs them with different radii. Because + * of the weighted combination of mips, and because larger blurs are done on + * higher mips, this effect provides good quality and performance. + * + * Reference: + * - https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/ + */ +class UnrealBloomPass extends Pass { + + constructor( resolution, strength, radius, threshold ) { + + super(); + + this.strength = ( strength !== undefined ) ? strength : 1; + this.radius = radius; + this.threshold = threshold; + this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 ); + + // create color only once here, reuse it later inside the render function + this.clearColor = new Color( 0, 0, 0 ); + + // render targets + this.renderTargetsHorizontal = []; + this.renderTargetsVertical = []; + this.nMips = 5; + let resx = Math.round( this.resolution.x / 2 ); + let resy = Math.round( this.resolution.y / 2 ); + + this.renderTargetBright = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } ); + this.renderTargetBright.texture.name = 'UnrealBloomPass.bright'; + this.renderTargetBright.texture.generateMipmaps = false; + + for ( let i = 0; i < this.nMips; i ++ ) { + + const renderTargetHorizontal = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } ); + + renderTargetHorizontal.texture.name = 'UnrealBloomPass.h' + i; + renderTargetHorizontal.texture.generateMipmaps = false; + + this.renderTargetsHorizontal.push( renderTargetHorizontal ); + + const renderTargetVertical = new WebGLRenderTarget( resx, resy, { type: HalfFloatType } ); + + renderTargetVertical.texture.name = 'UnrealBloomPass.v' + i; + renderTargetVertical.texture.generateMipmaps = false; + + this.renderTargetsVertical.push( renderTargetVertical ); + + resx = Math.round( resx / 2 ); + + resy = Math.round( resy / 2 ); + + } + + // luminosity high pass material + + const highPassShader = LuminosityHighPassShader; + this.highPassUniforms = UniformsUtils.clone( highPassShader.uniforms ); + + this.highPassUniforms[ 'luminosityThreshold' ].value = threshold; + this.highPassUniforms[ 'smoothWidth' ].value = 0.01; + + this.materialHighPassFilter = new ShaderMaterial( { + uniforms: this.highPassUniforms, + vertexShader: highPassShader.vertexShader, + fragmentShader: highPassShader.fragmentShader + } ); + + // gaussian blur materials + + this.separableBlurMaterials = []; + const kernelSizeArray = [ 3, 5, 7, 9, 11 ]; + resx = Math.round( this.resolution.x / 2 ); + resy = Math.round( this.resolution.y / 2 ); + + for ( let i = 0; i < this.nMips; i ++ ) { + + this.separableBlurMaterials.push( this.getSeperableBlurMaterial( kernelSizeArray[ i ] ) ); + + this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy ); + + resx = Math.round( resx / 2 ); + + resy = Math.round( resy / 2 ); + + } + + // composite material + + this.compositeMaterial = this.getCompositeMaterial( this.nMips ); + this.compositeMaterial.uniforms[ 'blurTexture1' ].value = this.renderTargetsVertical[ 0 ].texture; + this.compositeMaterial.uniforms[ 'blurTexture2' ].value = this.renderTargetsVertical[ 1 ].texture; + this.compositeMaterial.uniforms[ 'blurTexture3' ].value = this.renderTargetsVertical[ 2 ].texture; + this.compositeMaterial.uniforms[ 'blurTexture4' ].value = this.renderTargetsVertical[ 3 ].texture; + this.compositeMaterial.uniforms[ 'blurTexture5' ].value = this.renderTargetsVertical[ 4 ].texture; + this.compositeMaterial.uniforms[ 'bloomStrength' ].value = strength; + this.compositeMaterial.uniforms[ 'bloomRadius' ].value = 0.1; + + const bloomFactors = [ 1.0, 0.8, 0.6, 0.4, 0.2 ]; + this.compositeMaterial.uniforms[ 'bloomFactors' ].value = bloomFactors; + this.bloomTintColors = [ new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ) ]; + this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors; + + // blend material + + const copyShader = CopyShader; + + this.copyUniforms = UniformsUtils.clone( copyShader.uniforms ); + + this.blendMaterial = new ShaderMaterial( { + uniforms: this.copyUniforms, + vertexShader: copyShader.vertexShader, + fragmentShader: copyShader.fragmentShader, + blending: AdditiveBlending, + depthTest: false, + depthWrite: false, + transparent: true + } ); + + this.enabled = true; + this.needsSwap = false; + + this._oldClearColor = new Color(); + this.oldClearAlpha = 1; + + this.basic = new MeshBasicMaterial(); + + this.fsQuad = new FullScreenQuad( null ); + + } + + dispose() { + + for ( let i = 0; i < this.renderTargetsHorizontal.length; i ++ ) { + + this.renderTargetsHorizontal[ i ].dispose(); + + } + + for ( let i = 0; i < this.renderTargetsVertical.length; i ++ ) { + + this.renderTargetsVertical[ i ].dispose(); + + } + + this.renderTargetBright.dispose(); + + // + + for ( let i = 0; i < this.separableBlurMaterials.length; i ++ ) { + + this.separableBlurMaterials[ i ].dispose(); + + } + + this.compositeMaterial.dispose(); + this.blendMaterial.dispose(); + this.basic.dispose(); + + // + + this.fsQuad.dispose(); + + } + + setSize( width, height ) { + + let resx = Math.round( width / 2 ); + let resy = Math.round( height / 2 ); + + this.renderTargetBright.setSize( resx, resy ); + + for ( let i = 0; i < this.nMips; i ++ ) { + + this.renderTargetsHorizontal[ i ].setSize( resx, resy ); + this.renderTargetsVertical[ i ].setSize( resx, resy ); + + this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy ); + + resx = Math.round( resx / 2 ); + resy = Math.round( resy / 2 ); + + } + + } + + render( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) { + + renderer.getClearColor( this._oldClearColor ); + this.oldClearAlpha = renderer.getClearAlpha(); + const oldAutoClear = renderer.autoClear; + renderer.autoClear = false; + + renderer.setClearColor( this.clearColor, 0 ); + + if ( maskActive ) renderer.state.buffers.stencil.setTest( false ); + + // Render input to screen + + if ( this.renderToScreen ) { + + this.fsQuad.material = this.basic; + this.basic.map = readBuffer.texture; + + renderer.setRenderTarget( null ); + renderer.clear(); + this.fsQuad.render( renderer ); + + } + + // 1. Extract Bright Areas + + this.highPassUniforms[ 'tDiffuse' ].value = readBuffer.texture; + this.highPassUniforms[ 'luminosityThreshold' ].value = this.threshold; + this.fsQuad.material = this.materialHighPassFilter; + + renderer.setRenderTarget( this.renderTargetBright ); + renderer.clear(); + this.fsQuad.render( renderer ); + + // 2. Blur All the mips progressively + + let inputRenderTarget = this.renderTargetBright; + + for ( let i = 0; i < this.nMips; i ++ ) { + + this.fsQuad.material = this.separableBlurMaterials[ i ]; + + this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = inputRenderTarget.texture; + this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionX; + renderer.setRenderTarget( this.renderTargetsHorizontal[ i ] ); + renderer.clear(); + this.fsQuad.render( renderer ); + + this.separableBlurMaterials[ i ].uniforms[ 'colorTexture' ].value = this.renderTargetsHorizontal[ i ].texture; + this.separableBlurMaterials[ i ].uniforms[ 'direction' ].value = UnrealBloomPass.BlurDirectionY; + renderer.setRenderTarget( this.renderTargetsVertical[ i ] ); + renderer.clear(); + this.fsQuad.render( renderer ); + + inputRenderTarget = this.renderTargetsVertical[ i ]; + + } + + // Composite All the mips + + this.fsQuad.material = this.compositeMaterial; + this.compositeMaterial.uniforms[ 'bloomStrength' ].value = this.strength; + this.compositeMaterial.uniforms[ 'bloomRadius' ].value = this.radius; + this.compositeMaterial.uniforms[ 'bloomTintColors' ].value = this.bloomTintColors; + + renderer.setRenderTarget( this.renderTargetsHorizontal[ 0 ] ); + renderer.clear(); + this.fsQuad.render( renderer ); + + // Blend it additively over the input texture + + this.fsQuad.material = this.blendMaterial; + this.copyUniforms[ 'tDiffuse' ].value = this.renderTargetsHorizontal[ 0 ].texture; + + if ( maskActive ) renderer.state.buffers.stencil.setTest( true ); + + if ( this.renderToScreen ) { + + renderer.setRenderTarget( null ); + this.fsQuad.render( renderer ); + + } else { + + renderer.setRenderTarget( readBuffer ); + this.fsQuad.render( renderer ); + + } + + // Restore renderer settings + + renderer.setClearColor( this._oldClearColor, this.oldClearAlpha ); + renderer.autoClear = oldAutoClear; + + } + + getSeperableBlurMaterial( kernelRadius ) { + + const coefficients = []; + + for ( let i = 0; i < kernelRadius; i ++ ) { + + coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius ); + + } + + return new ShaderMaterial( { + + defines: { + 'KERNEL_RADIUS': kernelRadius + }, + + uniforms: { + 'colorTexture': { value: null }, + 'invSize': { value: new Vector2( 0.5, 0.5 ) }, // inverse texture size + 'direction': { value: new Vector2( 0.5, 0.5 ) }, + 'gaussianCoefficients': { value: coefficients } // precomputed Gaussian coefficients + }, + + vertexShader: + `varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + + fragmentShader: + `#include + varying vec2 vUv; + uniform sampler2D colorTexture; + uniform vec2 invSize; + uniform vec2 direction; + uniform float gaussianCoefficients[KERNEL_RADIUS]; + + void main() { + float weightSum = gaussianCoefficients[0]; + vec3 diffuseSum = texture2D( colorTexture, vUv ).rgb * weightSum; + for( int i = 1; i < KERNEL_RADIUS; i ++ ) { + float x = float(i); + float w = gaussianCoefficients[i]; + vec2 uvOffset = direction * invSize * x; + vec3 sample1 = texture2D( colorTexture, vUv + uvOffset ).rgb; + vec3 sample2 = texture2D( colorTexture, vUv - uvOffset ).rgb; + diffuseSum += (sample1 + sample2) * w; + weightSum += 2.0 * w; + } + gl_FragColor = vec4(diffuseSum/weightSum, 1.0); + }` + } ); + + } + + getCompositeMaterial( nMips ) { + + return new ShaderMaterial( { + + defines: { + 'NUM_MIPS': nMips + }, + + uniforms: { + 'blurTexture1': { value: null }, + 'blurTexture2': { value: null }, + 'blurTexture3': { value: null }, + 'blurTexture4': { value: null }, + 'blurTexture5': { value: null }, + 'bloomStrength': { value: 1.0 }, + 'bloomFactors': { value: null }, + 'bloomTintColors': { value: null }, + 'bloomRadius': { value: 0.0 } + }, + + vertexShader: + `varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + + fragmentShader: + `varying vec2 vUv; + uniform sampler2D blurTexture1; + uniform sampler2D blurTexture2; + uniform sampler2D blurTexture3; + uniform sampler2D blurTexture4; + uniform sampler2D blurTexture5; + uniform float bloomStrength; + uniform float bloomRadius; + uniform float bloomFactors[NUM_MIPS]; + uniform vec3 bloomTintColors[NUM_MIPS]; + + float lerpBloomFactor(const in float factor) { + float mirrorFactor = 1.2 - factor; + return mix(factor, mirrorFactor, bloomRadius); + } + + void main() { + gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + + lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + + lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + + lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + + lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); + }` + } ); + + } + +} + +UnrealBloomPass.BlurDirectionX = new Vector2( 1.0, 0.0 ); +UnrealBloomPass.BlurDirectionY = new Vector2( 0.0, 1.0 ); + +export { UnrealBloomPass }; diff --git a/static/vendor/three/addons/shaders/CopyShader.js b/static/vendor/three/addons/shaders/CopyShader.js new file mode 100644 index 0000000..8c3f2cd --- /dev/null +++ b/static/vendor/three/addons/shaders/CopyShader.js @@ -0,0 +1,45 @@ +/** + * Full-screen textured quad shader + */ + +const CopyShader = { + + name: 'CopyShader', + + uniforms: { + + 'tDiffuse': { value: null }, + 'opacity': { value: 1.0 } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform float opacity; + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + void main() { + + vec4 texel = texture2D( tDiffuse, vUv ); + gl_FragColor = opacity * texel; + + + }` + +}; + +export { CopyShader }; diff --git a/static/vendor/three/addons/shaders/LuminosityHighPassShader.js b/static/vendor/three/addons/shaders/LuminosityHighPassShader.js new file mode 100644 index 0000000..3e542e0 --- /dev/null +++ b/static/vendor/three/addons/shaders/LuminosityHighPassShader.js @@ -0,0 +1,64 @@ +import { + Color +} from '../../three.module.min.js'; + +/** + * Luminosity + * http://en.wikipedia.org/wiki/Luminosity + */ + +const LuminosityHighPassShader = { + + name: 'LuminosityHighPassShader', + + shaderID: 'luminosityHighPass', + + uniforms: { + + 'tDiffuse': { value: null }, + 'luminosityThreshold': { value: 1.0 }, + 'smoothWidth': { value: 1.0 }, + 'defaultColor': { value: new Color( 0x000000 ) }, + 'defaultOpacity': { value: 0.0 } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform sampler2D tDiffuse; + uniform vec3 defaultColor; + uniform float defaultOpacity; + uniform float luminosityThreshold; + uniform float smoothWidth; + + varying vec2 vUv; + + void main() { + + vec4 texel = texture2D( tDiffuse, vUv ); + + float v = luminance( texel.xyz ); + + vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity ); + + float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v ); + + gl_FragColor = mix( outputColor, texel, alpha ); + + }` + +}; + +export { LuminosityHighPassShader }; diff --git a/static/vendor/three/addons/shaders/OutputShader.js b/static/vendor/three/addons/shaders/OutputShader.js new file mode 100644 index 0000000..289ac10 --- /dev/null +++ b/static/vendor/three/addons/shaders/OutputShader.js @@ -0,0 +1,85 @@ +const OutputShader = { + + name: 'OutputShader', + + uniforms: { + + 'tDiffuse': { value: null }, + 'toneMappingExposure': { value: 1 } + + }, + + vertexShader: /* glsl */` + precision highp float; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + attribute vec3 position; + attribute vec2 uv; + + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + precision highp float; + + uniform sampler2D tDiffuse; + + #include + #include + + varying vec2 vUv; + + void main() { + + gl_FragColor = texture2D( tDiffuse, vUv ); + + // tone mapping + + #ifdef LINEAR_TONE_MAPPING + + gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb ); + + #elif defined( REINHARD_TONE_MAPPING ) + + gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb ); + + #elif defined( CINEON_TONE_MAPPING ) + + gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb ); + + #elif defined( ACES_FILMIC_TONE_MAPPING ) + + gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb ); + + #elif defined( AGX_TONE_MAPPING ) + + gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb ); + + #elif defined( NEUTRAL_TONE_MAPPING ) + + gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb ); + + #endif + + // color space + + #ifdef SRGB_TRANSFER + + gl_FragColor = sRGBTransferOETF( gl_FragColor ); + + #endif + + }` + +}; + +export { OutputShader };