threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
158 lines • 6.7 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 { AViewerPluginSync } from '../../viewer';
import { uiFolderContainer, uiSlider, uiToggle } from 'uiconfig.js';
import { onChange, serialize } from 'ts-browser-helpers';
import { ProgressivePlugin } from './ProgressivePlugin';
/**
* SSAA Plugin
*
* Jitters the render camera and optionally other cameras in the scene
* to create a super-sampled anti-aliasing effect.
* This is done across multiple frames by integrating with the ProgressivePlugin
* @category Plugins
*/
let SSAAPlugin = class SSAAPlugin extends AViewerPluginSync {
constructor(rendersPerFrame = 1) {
super();
this.enabled = true;
this.rendersPerFrame = 1;
this.jitterRenderCamera = true;
this.jitterLightCameras = true;
this._hasSetOffsetRC = false;
this._hasSetOffsetLC = false;
this.trackedJitterCameras = new Set(); // todo register other cameras and light shadows cameras when added to the scene and changed.
this.dependencies = [ProgressivePlugin];
this._addSceneObject = (event) => {
event.object?.traverse((o) => {
if (o && o.shadow && o.shadow.camera && o.shadow.mapSize) {
this.trackedJitterCameras.add([o.shadow.camera, o.shadow.mapSize]);
}
// if (o?.material) {
// if (o.material.alphaMap) console.log(o.material) //todo why?
// }
});
};
this._preRender = () => {
const v = this._viewer;
if (!v || !this.enabled || v.renderManager.frameCount <= 1)
return;
this.rendersPerFrame = v.rendersPerFrame; // just to sync. todo: should this be here?. ideally there should be a event fired from the viewer when the prop changes
const cam = v.scene.renderCamera;
if (this.jitterRenderCamera)
this._jitter(cam, {
width: v.renderManager.renderSize.x * v.renderManager.renderScale,
height: v.renderManager.renderSize.y * v.renderManager.renderScale,
}, v.renderManager.frameCount);
if (this.jitterLightCameras)
this.trackedJitterCameras.forEach((a) => this._jitter(...a, v.renderManager.frameCount));
this._hasSetOffsetRC = this.jitterRenderCamera;
this._hasSetOffsetLC = this.jitterLightCameras;
v.renderManager.resetShadows();
};
this._postRender = () => {
const v = this._viewer;
if (!v)
return;
if (this._hasSetOffsetRC) {
this._clearJitter(v.scene.renderCamera);
this._hasSetOffsetRC = false;
}
if (this._hasSetOffsetLC) {
this.trackedJitterCameras.forEach(([camera]) => this._clearJitter(camera));
this._hasSetOffsetLC = false;
}
};
this.jitterOffsets = [
{ x: 0, y: 0 },
{ x: -0.5, y: 0 },
{ x: -0.375, y: -0.25 },
{ x: -0.1875, y: -0.125 },
{ x: -0.125, y: -0.375 },
{ x: 0.0625, y: -0.0625 },
{ x: 0.125, y: -0.3125 },
{ x: 0.375, y: -0.4375 },
{ x: 0.3125, y: -0.1875 },
{ x: 0.25, y: 0.0625 },
{ x: 0.4375, y: 0.25 },
{ x: 0.1875, y: 0.3125 },
{ x: 0, y: 0.4375 },
{ x: -0.0625, y: 0.1875 },
{ x: -0.25, y: 0.375 },
{ x: -0.4375, y: 0.5 },
{ x: -0.3125, y: 0.125 },
];
this.rendersPerFrame = rendersPerFrame;
}
onAdded(viewer) {
super.onAdded(viewer);
viewer.addEventListener('preRender', this._preRender);
viewer.addEventListener('postRender', this._postRender);
viewer.scene.addEventListener('addSceneObject', this._addSceneObject);
}
onRemove(viewer) {
viewer.removeEventListener('preRender', this._preRender);
viewer.removeEventListener('postRender', this._postRender);
viewer.scene.removeEventListener('addSceneObject', this._addSceneObject);
return super.onRemove(viewer);
}
setDirty() {
if (!this._viewer)
return;
this._viewer.rendersPerFrame = this.rendersPerFrame;
this._viewer.setDirty();
this.uiConfig?.uiRefresh?.(true, 'postFrame');
}
_jitter(camera, size, frameCount) {
if (camera.userData.disableJitter)
return;
if (camera.userData.__jittered) {
this._viewer?.console.warn('SSAAPlugin: Camera already jittered');
return;
}
const sample = { ...this.jitterOffsets[frameCount % this.jitterOffsets.length] };
// const sample = {...offsets[Math.floor(Math.random() * (offsets.length - 0.001))]}
// {
// sample.x += 1 * (Math.random() - 0.5)
// sample.y += 1 * (Math.random() - 0.5)
// }
camera.setViewOffset(size.width, size.height, sample.x, sample.y, size.width, size.height);
camera.userData.__jittered = true;
}
_clearJitter(camera) {
if (!camera.userData.__jittered)
return;
camera.clearViewOffset();
delete camera.userData.__jittered;
}
};
SSAAPlugin.PluginType = 'SSAAPlugin';
__decorate([
serialize(),
uiToggle('Enabled'),
onChange(SSAAPlugin.prototype.setDirty)
], SSAAPlugin.prototype, "enabled", void 0);
__decorate([
serialize(),
uiSlider('Renders/Frame', [1, 32], 1),
onChange(SSAAPlugin.prototype.setDirty)
], SSAAPlugin.prototype, "rendersPerFrame", void 0);
__decorate([
serialize(),
uiToggle('Render Camera'),
onChange(SSAAPlugin.prototype.setDirty)
], SSAAPlugin.prototype, "jitterRenderCamera", void 0);
__decorate([
serialize(),
uiToggle('Light Cameras'),
onChange(SSAAPlugin.prototype.setDirty)
], SSAAPlugin.prototype, "jitterLightCameras", void 0);
SSAAPlugin = __decorate([
uiFolderContainer('SSAA Plugin')
], SSAAPlugin);
export { SSAAPlugin };
//# sourceMappingURL=SSAAPlugin.js.map