UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

209 lines 8.55 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { UnsignedByteType } from 'three'; import { PipelinePassPlugin } from '../base/PipelinePassPlugin'; import { uiFolderContainer, uiImage, uiInput } from 'uiconfig.js'; import { AddBlendTexturePass } from '../../postprocessing/AddBlendTexturePass'; import { getOrCall, serialize } from 'ts-browser-helpers'; import { SSAAPlugin } from './SSAAPlugin'; /** * Progressive Plugin * * Adds a post-render pass to blend the last frame with the current frame. * This can be used to create a progressive rendering effect which is useful for progressive shadows, gi, denoising, baking, anti-aliasing, and many other effects. * @category Plugins */ let ProgressivePlugin = class ProgressivePlugin extends PipelinePassPlugin { // @uiImage('Last Texture', {readOnly: true}) texture?: Texture get texture() { return this.target?.texture; } get target() { return this._viewer ? this._targets.get(this._viewer.scene.renderCamera.uuid) : undefined; } getTarget(camera) { return this._viewer ? this._targets.get((camera ? camera : this._viewer.scene.renderCamera).uuid) : undefined; } get textures() { return this._viewer ? Array.from(this._targets.values()).map(t => t.texture) : []; } get mainTexture() { return this._viewer ? this.getTarget(this._viewer.scene.mainCamera)?.texture : undefined; } constructor(maxFrameCount = 32, bufferType = UnsignedByteType, // this is not used. todo use halffloat when rgbm = false enabled = true) { super(); this.passId = 'progressive'; /** * Different targets for different render cameras. * Need to save them all here since we need them in the next frame. * @protected */ this._targets = new Map(); this.maxFrameCount = maxFrameCount; this.enabled = enabled; this.bufferType = bufferType; } _createTarget(camera, recreate = false) { if (!this._viewer) return; camera = camera ?? this._viewer.scene.renderCamera; if (recreate) this._disposeTarget(camera); if (this._targets.has(camera.uuid)) return this._targets.get(camera.uuid); const target = this._viewer.renderManager.composerTarget.clone(true); target.texture.name = 'progressiveLastBuffer_' + camera.uuid; // target.texture.type = this.bufferType this._targets.set(camera.uuid, target); // if (this._pass) this._pass.target = this.target return target; } _disposeTarget(camera) { if (!this._viewer) return; if (!camera) { this._targets.forEach((t) => this._viewer.renderManager.disposeTarget(t)); this._targets.clear(); } else { const t = this._targets.get(camera.uuid); if (t) { this._viewer.renderManager.disposeTarget(t); this._targets.delete(camera.uuid); } } } _createPass() { // this._createTarget(true) const pass = new ProgressiveBlendPass(this.passId, () => this.target ?? this._createTarget(), this._viewer?.renderManager.maxHDRIntensity); // todo: disposeTarget somewhere pass.dirty = () => (this._viewer?.renderManager.frameCount || 0) < this.maxFrameCount; // todo use isConverged function return pass; } onAdded(viewer) { super.onAdded(viewer); } onRemove(viewer) { this._disposeTarget(); return super.onRemove(viewer); } /** * * @param postRender - if called after rendering frame. */ isConverged(postRender = false) { return (this._viewer?.renderManager.frameCount || 0) >= this.maxFrameCount - 1 + (postRender ? 1 : 0); } updateShaderProperties(material) { if (material.uniforms.tLastFrame) material.uniforms.tLastFrame.value = this.target?.texture ?? undefined; return this; } /** * Get recording delta post render, For use with animations to sync with converge mode in canvas recorder. See PopmotionPlugin for usage. * @returns {number} - delta time in milliseconds, or 0 when converging, or -1 in case of not recording in converge mode */ postFrameConvergedRecordingDelta(_ = 'CanvasRecorder') { // const recorder = this._viewer!.getPluginByType<IConvergedCanvasRecorder&IViewerPlugin>(recorderPlugin) // if (recorder && recorder.isRecording() && recorder.convergeMode) // return this.isConverged(true) ? 1. / recorder.videoFrameRate : 0 return -1; } get convergedPromise() { return new Promise(resolve => { if (this.isConverged()) { this._viewer?.doOnce('postFrame', () => resolve()); } else { const l = () => { if (!this.isConverged(true)) return; this._viewer?.removeEventListener('postRender', l); this._viewer?.doOnce('postFrame', () => resolve()); }; this._viewer?.addEventListener('postRender', l); } }); } fromJSON(data, meta) { if (data.jitter !== undefined) { const ssaa = this._viewer?.getPlugin(SSAAPlugin); if (!ssaa) { console.warn('Loading old webgi v0 file, add SSAAPlugin to get anti-aliasing'); } else { data = { ...data }; ssaa.enabled = data.jitter; delete data.jitter; } } return super.fromJSON(data, meta); } }; ProgressivePlugin.PluginType = 'ProgressivePlugin'; ProgressivePlugin.OldPluginType = 'Progressive'; __decorate([ serialize(), uiInput('Frame count') ], ProgressivePlugin.prototype, "maxFrameCount", void 0); __decorate([ uiImage('Last Texture', { readOnly: true }) ], ProgressivePlugin.prototype, "mainTexture", null); ProgressivePlugin = __decorate([ uiFolderContainer('Progressive Plugin') ], ProgressivePlugin); export { ProgressivePlugin }; export class ProgressiveBlendPass extends AddBlendTexturePass { constructor(passId, target, maxIntensity = 120) { super(undefined, maxIntensity); this.passId = passId; this.target = target; this.before = ['screen']; this.after = ['render']; this.required = ['render']; this.dirty = () => false; } render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { if (!this.enabled) return; const target = getOrCall(this.target); if (!target) { console.warn('ProgressiveBlendPass: target not defined'); return; } if (renderer.renderManager.frameCount < 1) { this.needsSwap = false; if (readBuffer?.texture) renderer.renderManager.blit(target, { source: readBuffer.texture, respectColorSpace: false, }); return; } this.needsSwap = true; super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive); renderer.renderManager.blit(target, { source: writeBuffer.texture, respectColorSpace: false, }); } beforeRender(_, _1, renderManager) { if (!this.enabled) return; if (!this.target) { console.error('ProgressiveBlendPass: render target undefined'); return; } let f = 1. / (Math.max(renderManager.frameCount, 0) + 1); this.uniforms.weight.value.set(f, f, f, f); f = 1. - f; this.uniforms.weight2.value.set(f, f, f, f); this.uniforms.tDiffuse2.value = getOrCall(this.target)?.texture; this.material.uniformsNeedUpdate = true; } } //# sourceMappingURL=ProgressivePlugin.js.map