@itwin/core-frontend
Version:
iTwin.js frontend components
277 lines • 14 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module WebGL
*/
import { assert, dispose } from "@itwin/core-bentley";
import { EDLCalcBasicGeometry, EDLCalcFullGeometry, EDLFilterGeometry, EDLMixGeometry } from "./CachedGeometry";
import { FrameBuffer } from "./FrameBuffer";
import { GL } from "./GL";
import { RenderState } from "./RenderState";
import { collectGeometryStatistics, collectTextureStatistics } from "./SceneCompositor";
import { getDrawParams } from "./ScratchDrawParams";
import { System } from "./System";
import { TextureHandle } from "./Texture";
class Bundle {
edlCalcTex1;
edlCalcTex2;
edlCalcTex4;
edlFiltTex2;
edlFiltTex4;
edlCalcFbo1;
edlCalcFbo2;
edlCalcFbo4;
edlFiltFbo2;
edlFiltFbo4;
edlCalcBasicGeom;
edlCalcFullGeom;
edlFiltGeom;
edlMixGeom;
constructor(edlCalcTex1, edlCalcTex2, edlCalcTex4, edlFiltTex2, edlFiltTex4, edlCalcFbo1, edlCalcFbo2, edlCalcFbo4, edlFiltFbo2, edlFiltFbo4, edlCalcBasicGeom, edlCalcFullGeom, edlFiltGeom, edlMixGeom) {
this.edlCalcTex1 = edlCalcTex1;
this.edlCalcTex2 = edlCalcTex2;
this.edlCalcTex4 = edlCalcTex4;
this.edlFiltTex2 = edlFiltTex2;
this.edlFiltTex4 = edlFiltTex4;
this.edlCalcFbo1 = edlCalcFbo1;
this.edlCalcFbo2 = edlCalcFbo2;
this.edlCalcFbo4 = edlCalcFbo4;
this.edlFiltFbo2 = edlFiltFbo2;
this.edlFiltFbo4 = edlFiltFbo4;
this.edlCalcBasicGeom = edlCalcBasicGeom;
this.edlCalcFullGeom = edlCalcFullGeom;
this.edlFiltGeom = edlFiltGeom;
this.edlMixGeom = edlMixGeom;
}
static create(width, height) {
const edlCalcTex1 = TextureHandle.createForAttachment(width, height, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte);
const edlCalcTex2 = TextureHandle.createForAttachment(width >> 1, height >> 1, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte);
const edlCalcTex4 = TextureHandle.createForAttachment(width >> 2, height >> 2, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte);
const edlFiltTex2 = TextureHandle.createForAttachment(width >> 1, height >> 1, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte);
const edlFiltTex4 = TextureHandle.createForAttachment(width >> 2, height >> 2, GL.Texture.Format.Rgba, GL.Texture.DataType.UnsignedByte);
if (undefined === edlCalcTex1 || undefined === edlCalcTex2 || undefined === edlCalcTex4 || undefined === edlFiltTex2 || undefined === edlFiltTex4) {
dispose(edlCalcTex1);
dispose(edlCalcTex2);
dispose(edlCalcTex4);
dispose(edlFiltTex2);
dispose(edlFiltTex4);
return undefined;
}
const edlCalcFbo1 = FrameBuffer.create([edlCalcTex1]);
const edlCalcFbo2 = FrameBuffer.create([edlCalcTex2]);
const edlCalcFbo4 = FrameBuffer.create([edlCalcTex4]);
const edlFiltFbo2 = FrameBuffer.create([edlFiltTex2]);
const edlFiltFbo4 = FrameBuffer.create([edlFiltTex4]);
if (undefined === edlCalcFbo1 || undefined === edlCalcFbo2 || undefined === edlCalcFbo4 || undefined === edlFiltFbo2 || undefined === edlFiltFbo4) {
dispose(edlCalcFbo1);
dispose(edlCalcFbo2);
dispose(edlCalcFbo4);
dispose(edlFiltFbo2);
dispose(edlFiltFbo4);
return undefined;
}
return new Bundle(edlCalcTex1, edlCalcTex2, edlCalcTex4, edlFiltTex2, edlFiltTex4, edlCalcFbo1, edlCalcFbo2, edlCalcFbo4, edlFiltFbo2, edlFiltFbo4);
}
get isDisposed() {
return undefined === this.edlCalcTex1
&& undefined === this.edlCalcTex2
&& undefined === this.edlCalcTex4
&& undefined === this.edlFiltTex2
&& undefined === this.edlFiltTex4
&& undefined === this.edlCalcFbo1
&& undefined === this.edlCalcFbo2
&& undefined === this.edlCalcFbo4
&& undefined === this.edlFiltFbo2
&& undefined === this.edlFiltFbo4
&& undefined === this.edlCalcBasicGeom
&& undefined === this.edlCalcFullGeom?.[0]
&& undefined === this.edlCalcFullGeom?.[1]
&& undefined === this.edlCalcFullGeom?.[2]
&& undefined === this.edlCalcFullGeom
&& undefined === this.edlFiltGeom?.[0]
&& undefined === this.edlFiltGeom?.[1]
&& undefined === this.edlFiltGeom
&& undefined === this.edlMixGeom;
}
[Symbol.dispose]() {
this.edlCalcTex1 = dispose(this.edlCalcTex1);
this.edlCalcTex2 = dispose(this.edlCalcTex2);
this.edlCalcTex4 = dispose(this.edlCalcTex4);
this.edlFiltTex2 = dispose(this.edlFiltTex2);
this.edlFiltTex4 = dispose(this.edlFiltTex4);
this.edlCalcFbo1 = dispose(this.edlCalcFbo1);
this.edlCalcFbo2 = dispose(this.edlCalcFbo2);
this.edlCalcFbo4 = dispose(this.edlCalcFbo4);
this.edlFiltFbo2 = dispose(this.edlFiltFbo2);
this.edlFiltFbo4 = dispose(this.edlFiltFbo4);
this.edlCalcBasicGeom = dispose(this.edlCalcBasicGeom);
if (this.edlCalcFullGeom) {
this.edlCalcFullGeom[0] = dispose(this.edlCalcFullGeom?.[0]);
this.edlCalcFullGeom[1] = dispose(this.edlCalcFullGeom?.[1]);
this.edlCalcFullGeom[2] = dispose(this.edlCalcFullGeom?.[2]);
this.edlCalcFullGeom = undefined;
}
if (this.edlFiltGeom) {
this.edlFiltGeom[0] = dispose(this.edlFiltGeom?.[0]);
this.edlFiltGeom[1] = dispose(this.edlFiltGeom?.[1]);
this.edlFiltGeom = undefined;
}
this.edlMixGeom = dispose(this.edlMixGeom);
}
}
/** @internal */
export var EDLMode;
(function (EDLMode) {
EDLMode[EDLMode["Off"] = 0] = "Off";
EDLMode[EDLMode["On"] = 1] = "On";
EDLMode[EDLMode["Full"] = 2] = "Full";
})(EDLMode || (EDLMode = {}));
export class EyeDomeLighting {
_bundle;
_width;
_height;
_depth; // depth buffer to read from, has to be non-MS and be up to date in draw if MS is used
_edlFinalFbo;
_edlFinalBufs;
_target;
getBundle() {
if (undefined === this._bundle) {
this._bundle = Bundle.create(this._width, this._height);
assert(undefined !== this._bundle);
}
return this._bundle;
}
constructor(target) {
this._target = target;
this._width = target.viewRect.width;
this._height = target.viewRect.height;
}
init(width, height, depth) {
this._width = width;
this._height = height;
this._depth = depth;
// don't create buffers until we know we will use them (first draw)
return true;
}
collectStatistics(stats) {
const bundle = this._bundle;
if (undefined !== bundle) {
collectTextureStatistics(bundle.edlCalcTex1, stats);
collectTextureStatistics(bundle.edlCalcTex2, stats);
collectTextureStatistics(bundle.edlCalcTex4, stats);
collectTextureStatistics(bundle.edlFiltTex2, stats);
collectTextureStatistics(bundle.edlFiltTex4, stats);
collectGeometryStatistics(bundle.edlCalcBasicGeom, stats);
collectGeometryStatistics(bundle.edlCalcFullGeom?.[0], stats);
collectGeometryStatistics(bundle.edlCalcFullGeom?.[1], stats);
collectGeometryStatistics(bundle.edlCalcFullGeom?.[2], stats);
collectGeometryStatistics(bundle.edlFiltGeom?.[0], stats);
collectGeometryStatistics(bundle.edlFiltGeom?.[1], stats);
collectGeometryStatistics(bundle.edlMixGeom, stats);
}
}
get isDisposed() { return undefined === this._bundle && undefined === this._edlFinalFbo; }
[Symbol.dispose]() {
this._bundle = dispose(this._bundle);
this._edlFinalFbo = dispose(this._edlFinalFbo);
}
reset() {
this[Symbol.dispose]();
}
/** calculate EyeDomeLighting at specified quality using screen space shaders
* returns true if succeeds
*/
draw(edlParams) {
if (undefined === edlParams.inputTex || undefined === this._depth || undefined === edlParams.curFbo)
return false;
const bundle = this.getBundle();
if (undefined === bundle)
return false;
// NB: have to test and create MS buffer as well if useMsBuffers, not outputting to depth
const finalBufs = edlParams.curFbo.getColorTargets(edlParams.useMsBuffers, 0);
if (undefined === this._edlFinalFbo || this._edlFinalBufs?.tex !== finalBufs.tex ||
(edlParams.useMsBuffers && this._edlFinalBufs?.msBuf !== finalBufs.msBuf)) {
this._edlFinalFbo = dispose(this._edlFinalFbo);
this._edlFinalBufs = finalBufs;
const filters = [GL.MultiSampling.Filter.Linear];
this._edlFinalFbo = FrameBuffer.create([this._edlFinalBufs.tex], undefined, edlParams.useMsBuffers && this._edlFinalBufs.msBuf ? [this._edlFinalBufs.msBuf] : undefined, filters, undefined);
if (undefined === this._edlFinalFbo)
return false;
}
const fbStack = System.instance.frameBufferStack;
const useMsBuffers = edlParams.useMsBuffers;
System.instance.applyRenderState(RenderState.defaults);
// ###TODO: should radius be (optionally?) voxel based instead of pixel here?
if (edlParams.edlMode === EDLMode.On) {
// draw using single pass version (still 8 samples, full size)
fbStack.execute(this._edlFinalFbo, true, useMsBuffers, () => {
if (bundle.edlCalcBasicGeom === undefined) {
const ct1 = edlParams.inputTex;
const ctd = this._depth.getHandle();
bundle.edlCalcBasicGeom = EDLCalcBasicGeometry.createGeometry(ct1.getHandle(), ctd, ct1.width, ct1.height);
}
const params = getDrawParams(this._target, bundle.edlCalcBasicGeom);
this._target.techniques.draw(params);
});
}
else { // EDLMode.Full
// draw with full method based on original paper using full, 1/2, and 1/4 sizes
const edlCalc2FB = [bundle.edlCalcFbo1, bundle.edlCalcFbo2, bundle.edlCalcFbo4];
if (bundle.edlCalcFullGeom === undefined) {
const ct1 = edlParams.inputTex;
const ct2 = bundle.edlCalcTex2;
const ct4 = bundle.edlCalcTex4;
const ctd = this._depth.getHandle();
bundle.edlCalcFullGeom = [EDLCalcFullGeometry.createGeometry(ct1.getHandle(), ctd, 1, ct1.width, ct1.height),
EDLCalcFullGeometry.createGeometry(ct1.getHandle(), ctd, 2, ct2.width, ct2.height),
EDLCalcFullGeometry.createGeometry(ct1.getHandle(), ctd, 4, ct4.width, ct4.height)];
}
const edlFiltFbos = [bundle.edlFiltFbo2, bundle.edlFiltFbo4];
if (bundle.edlFiltGeom === undefined) {
const ft2 = bundle.edlCalcTex2;
const ft4 = bundle.edlCalcTex4;
const ftd = this._depth.getHandle();
bundle.edlFiltGeom = [EDLFilterGeometry.createGeometry(ft2.getHandle(), ftd, 2, ft2.width, ft2.height),
EDLFilterGeometry.createGeometry(ft4.getHandle(), ftd, 4, ft4.width, ft4.height)];
}
const gl = System.instance.context;
// Loop through the 3 sizes calculating an edl buffer, and if not first size, then optionally filtering those
for (let i = 0; i < 3; ++i) {
fbStack.execute(edlCalc2FB[i], true, false, () => {
const colTex = edlCalc2FB[i].getColor(0);
gl.viewport(0, 0, colTex.width, colTex.height); // have to set viewport to current texture size
const params = getDrawParams(this._target, bundle.edlCalcFullGeom[i]);
this._target.techniques.draw(params);
});
if (edlParams.edlFilter && i > 0) {
fbStack.execute(edlFiltFbos[i - 1], true, false, () => {
const params = getDrawParams(this._target, bundle.edlFiltGeom[i - 1]);
this._target.techniques.draw(params);
});
}
}
gl.viewport(0, 0, this._width, this._height); // Restore viewport
// Now combine the 3 results and output
const tex1 = bundle.edlCalcTex1.getHandle();
const tex2 = edlParams.edlFilter ? bundle.edlFiltTex2.getHandle() : bundle.edlCalcTex2.getHandle();
const tex4 = edlParams.edlFilter ? bundle.edlFiltTex4.getHandle() : bundle.edlCalcTex4.getHandle();
fbStack.execute(this._edlFinalFbo, true, useMsBuffers, () => {
if (bundle.edlMixGeom === undefined) {
bundle.edlMixGeom = EDLMixGeometry.createGeometry(tex1, tex2, tex4);
}
else {
if (bundle.edlMixGeom.colorTexture1 !== tex1 || bundle.edlMixGeom.colorTexture2 !== tex2 || bundle.edlMixGeom.colorTexture4 !== tex4) {
dispose(bundle.edlMixGeom);
bundle.edlMixGeom = EDLMixGeometry.createGeometry(tex1, tex2, tex4);
}
}
const params = getDrawParams(this._target, bundle.edlMixGeom);
this._target.techniques.draw(params);
});
}
return true;
}
}
//# sourceMappingURL=EDL.js.map