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
JavaScript
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