|
@@ -1,17 +1,18 @@
|
|
|
-import { MultiRenderTarget } from "../Materials/Textures/multiRenderTarget";
|
|
|
+import { PrePassRenderTarget } from "../Materials/Textures/prePassRenderTarget";
|
|
|
import { Scene } from "../scene";
|
|
|
import { Engine } from "../Engines/engine";
|
|
|
import { Constants } from "../Engines/constants";
|
|
|
-import { ImageProcessingPostProcess } from "../PostProcesses/imageProcessingPostProcess";
|
|
|
import { PostProcess } from "../PostProcesses/postProcess";
|
|
|
import { Effect } from "../Materials/effect";
|
|
|
import { _DevTools } from '../Misc/devTools';
|
|
|
import { Color4 } from "../Maths/math.color";
|
|
|
-import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
|
|
|
import { Nullable } from "../types";
|
|
|
import { AbstractMesh } from '../Meshes/abstractMesh';
|
|
|
+import { Camera } from '../Cameras/camera';
|
|
|
import { Material } from '../Materials/material';
|
|
|
import { SubMesh } from '../Meshes/subMesh';
|
|
|
+import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
|
|
|
+import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
|
|
|
import { GeometryBufferRenderer } from '../Rendering/geometryBufferRenderer';
|
|
|
|
|
|
/**
|
|
@@ -26,7 +27,55 @@ export class PrePassRenderer {
|
|
|
throw _DevTools.WarnImport("PrePassRendererSceneComponent");
|
|
|
}
|
|
|
|
|
|
- private _textureFormats = [
|
|
|
+ /**
|
|
|
+ * To save performance, we can excluded skinned meshes from the prepass
|
|
|
+ */
|
|
|
+ public excludedSkinnedMesh: AbstractMesh[] = [];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Force material to be excluded from the prepass
|
|
|
+ * Can be useful when `useGeometryBufferFallback` is set to `true`
|
|
|
+ * and you don't want a material to show in the effect.
|
|
|
+ */
|
|
|
+ public excludedMaterials: Material[] = [];
|
|
|
+
|
|
|
+ private _scene: Scene;
|
|
|
+ private _engine: Engine;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Number of textures in the multi render target texture where the scene is directly rendered
|
|
|
+ */
|
|
|
+ public mrtCount: number = 0;
|
|
|
+
|
|
|
+ private _mrtFormats: number[] = [];
|
|
|
+ private _mrtLayout: number[] = [];
|
|
|
+ private _textureIndices: number[] = [];
|
|
|
+
|
|
|
+ private _multiRenderAttachments: number[];
|
|
|
+ private _defaultAttachments: number[];
|
|
|
+ private _clearAttachments: number[];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the index of a texture in the multi render target texture array.
|
|
|
+ * @param type Texture type
|
|
|
+ * @return The index
|
|
|
+ */
|
|
|
+ public getIndex(type: number) : number {
|
|
|
+ return this._textureIndices[type];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * How many samples are used for MSAA of the scene render target
|
|
|
+ */
|
|
|
+ public get samples() {
|
|
|
+ return this.defaultRT.samples;
|
|
|
+ }
|
|
|
+
|
|
|
+ public set samples(n: number) {
|
|
|
+ this.defaultRT.samples = n;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static _textureFormats = [
|
|
|
{
|
|
|
type: Constants.PREPASS_IRRADIANCE_TEXTURE_TYPE,
|
|
|
format: Constants.TEXTURETYPE_HALF_FLOAT,
|
|
@@ -48,7 +97,11 @@ export class PrePassRenderer {
|
|
|
format: Constants.TEXTURETYPE_HALF_FLOAT,
|
|
|
},
|
|
|
{
|
|
|
- type: Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE,
|
|
|
+ type: Constants.PREPASS_DEPTH_TEXTURE_TYPE,
|
|
|
+ format: Constants.TEXTURETYPE_HALF_FLOAT,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: Constants.PREPASS_NORMAL_TEXTURE_TYPE,
|
|
|
format: Constants.TEXTURETYPE_HALF_FLOAT,
|
|
|
},
|
|
|
{
|
|
@@ -57,102 +110,66 @@ export class PrePassRenderer {
|
|
|
},
|
|
|
];
|
|
|
|
|
|
- /**
|
|
|
- * To save performance, we can excluded skinned meshes from the prepass
|
|
|
- */
|
|
|
- public excludedSkinnedMesh: AbstractMesh[] = [];
|
|
|
-
|
|
|
- /**
|
|
|
- * Force material to be excluded from the prepass
|
|
|
- * Can be useful when `useGeometryBufferFallback` is set to `true`
|
|
|
- * and you don't want a material to show in the effect.
|
|
|
- */
|
|
|
- public excludedMaterials: Material[] = [];
|
|
|
-
|
|
|
- private _textureIndices: number[] = [];
|
|
|
-
|
|
|
- private _scene: Scene;
|
|
|
- private _engine: Engine;
|
|
|
- private _isDirty: boolean = false;
|
|
|
-
|
|
|
- /**
|
|
|
- * Number of textures in the multi render target texture where the scene is directly rendered
|
|
|
- */
|
|
|
- public mrtCount: number = 0;
|
|
|
+ private _isDirty: boolean = true;
|
|
|
|
|
|
/**
|
|
|
* The render target where the scene is directly rendered
|
|
|
*/
|
|
|
- public prePassRT: MultiRenderTarget;
|
|
|
-
|
|
|
- private _multiRenderAttachments: number[];
|
|
|
- private _defaultAttachments: number[];
|
|
|
-
|
|
|
- private _postProcesses: PostProcess[] = [];
|
|
|
-
|
|
|
- private readonly _clearColor = new Color4(0, 0, 0, 0);
|
|
|
-
|
|
|
- /**
|
|
|
- * Image processing post process for composition
|
|
|
- */
|
|
|
- public imageProcessingPostProcess: ImageProcessingPostProcess;
|
|
|
+ public defaultRT: PrePassRenderTarget;
|
|
|
|
|
|
/**
|
|
|
* Configuration for prepass effects
|
|
|
*/
|
|
|
private _effectConfigurations: PrePassEffectConfiguration[] = [];
|
|
|
|
|
|
- private _mrtFormats: number[] = [];
|
|
|
- private _mrtLayout: number[];
|
|
|
-
|
|
|
- private _enabled: boolean = false;
|
|
|
-
|
|
|
/**
|
|
|
- * Indicates if the prepass is enabled
|
|
|
+ * @return the prepass render target for the rendering pass.
|
|
|
+ * If we are currently rendering a render target, it returns the PrePassRenderTarget
|
|
|
+ * associated with that render target. Otherwise, it returns the scene default PrePassRenderTarget
|
|
|
*/
|
|
|
- public get enabled() {
|
|
|
- return this._enabled;
|
|
|
+ public getRenderTarget(): PrePassRenderTarget {
|
|
|
+ return this._currentTarget;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * How many samples are used for MSAA of the scene render target
|
|
|
+ * @hidden
|
|
|
+ * Managed by the scene component
|
|
|
+ * @param prePassRenderTarget
|
|
|
*/
|
|
|
- public get samples() {
|
|
|
- return this.prePassRT.samples;
|
|
|
- }
|
|
|
-
|
|
|
- public set samples(n: number) {
|
|
|
- if (!this.imageProcessingPostProcess) {
|
|
|
- this._createCompositionEffect();
|
|
|
+ public _setRenderTarget(prePassRenderTarget: Nullable<PrePassRenderTarget>): void {
|
|
|
+ if (prePassRenderTarget) {
|
|
|
+ this._currentTarget = prePassRenderTarget;
|
|
|
+ } else {
|
|
|
+ this._currentTarget = this.defaultRT;
|
|
|
}
|
|
|
-
|
|
|
- this.prePassRT.samples = n;
|
|
|
}
|
|
|
|
|
|
- private _geometryBuffer: Nullable<GeometryBufferRenderer>;
|
|
|
- private _useGeometryBufferFallback = false;
|
|
|
/**
|
|
|
- * Uses the geometry buffer renderer as a fallback for non prepass capable effects
|
|
|
+ * Returns true if the currently rendered prePassRenderTarget is the one
|
|
|
+ * associated with the scene.
|
|
|
*/
|
|
|
- public get useGeometryBufferFallback() : boolean {
|
|
|
- return this._useGeometryBufferFallback;
|
|
|
+ public get currentRTisSceneRT(): boolean {
|
|
|
+ return this._currentTarget === this.defaultRT;
|
|
|
}
|
|
|
|
|
|
- public set useGeometryBufferFallback(value: boolean) {
|
|
|
- this._useGeometryBufferFallback = value;
|
|
|
+ private _geometryBuffer: Nullable<GeometryBufferRenderer>;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Prevents the PrePassRenderer from using the GeometryBufferRenderer as a fallback
|
|
|
+ */
|
|
|
+ public doNotUseGeometryRendererFallback = false;
|
|
|
|
|
|
- if (value) {
|
|
|
+ private _refreshGeometryBufferRendererLink() {
|
|
|
+ if (!this.doNotUseGeometryRendererFallback) {
|
|
|
this._geometryBuffer = this._scene.enableGeometryBufferRenderer();
|
|
|
|
|
|
if (!this._geometryBuffer) {
|
|
|
// Not supported
|
|
|
- this._useGeometryBufferFallback = false;
|
|
|
+ this.doNotUseGeometryRendererFallback = true;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- this._geometryBuffer.renderList = [];
|
|
|
this._geometryBuffer._linkPrePassRenderer(this);
|
|
|
- this._updateGeometryBufferLayout();
|
|
|
} else {
|
|
|
if (this._geometryBuffer) {
|
|
|
this._geometryBuffer._unlinkPrePassRenderer();
|
|
@@ -162,6 +179,27 @@ export class PrePassRenderer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private _currentTarget: PrePassRenderTarget;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * All the render targets generated by prepass
|
|
|
+ */
|
|
|
+ public renderTargets: PrePassRenderTarget[] = [];
|
|
|
+
|
|
|
+ private readonly _clearColor = new Color4(0, 0, 0, 0);
|
|
|
+
|
|
|
+ private _enabled: boolean = false;
|
|
|
+
|
|
|
+ private _needsCompositionForThisPass = false;
|
|
|
+ private _postProcessesSourceForThisPass: Nullable<PostProcess>[];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Indicates if the prepass is enabled
|
|
|
+ */
|
|
|
+ public get enabled() {
|
|
|
+ return this._enabled;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Set to true to disable gamma transform in PrePass.
|
|
|
* Can be useful in case you already proceed to gamma transform on a material level
|
|
@@ -178,40 +216,25 @@ export class PrePassRenderer {
|
|
|
this._engine = scene.getEngine();
|
|
|
|
|
|
PrePassRenderer._SceneComponentInitialization(this._scene);
|
|
|
- this._resetLayout();
|
|
|
- }
|
|
|
-
|
|
|
- private _initializeAttachments() {
|
|
|
- const multiRenderLayout = [];
|
|
|
- const clearLayout = [false];
|
|
|
- const defaultLayout = [true];
|
|
|
-
|
|
|
- for (let i = 0; i < this.mrtCount; i++) {
|
|
|
- multiRenderLayout.push(true);
|
|
|
-
|
|
|
- if (i > 0) {
|
|
|
- clearLayout.push(true);
|
|
|
- defaultLayout.push(false);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this._multiRenderAttachments = this._engine.buildTextureLayout(multiRenderLayout);
|
|
|
- this._defaultAttachments = this._engine.buildTextureLayout(defaultLayout);
|
|
|
+ this.defaultRT = this._createRenderTarget("sceneprePassRT", null);
|
|
|
+ this._setRenderTarget(null);
|
|
|
}
|
|
|
|
|
|
- private _createCompositionEffect() {
|
|
|
- this.prePassRT = new MultiRenderTarget("sceneprePassRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, this.mrtCount, this._scene,
|
|
|
- { generateMipMaps: false, generateDepthTexture: true, defaultType: Constants.TEXTURETYPE_UNSIGNED_INT, types: this._mrtFormats });
|
|
|
- this.prePassRT.samples = 1;
|
|
|
+ /**
|
|
|
+ * Creates a new PrePassRenderTarget
|
|
|
+ * This should be the only way to instanciate a `PrePassRenderTarget`
|
|
|
+ * @param name Name of the `PrePassRenderTarget`
|
|
|
+ * @param renderTargetTexture RenderTarget the `PrePassRenderTarget` will be attached to.
|
|
|
+ * Can be `null` if the created `PrePassRenderTarget` is attached to the scene (default framebuffer).
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _createRenderTarget(name: string, renderTargetTexture: Nullable<RenderTargetTexture>) : PrePassRenderTarget {
|
|
|
+ const rt = new PrePassRenderTarget(name, renderTargetTexture, { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, 0, this._scene,
|
|
|
+ { generateMipMaps: false, generateStencilBuffer: this._engine.isStencilEnable, defaultType: Constants.TEXTURETYPE_UNSIGNED_INT, types: [], drawOnlyOnFirstAttachmentByDefault: true });
|
|
|
|
|
|
- this._initializeAttachments();
|
|
|
- if (this._useGeometryBufferFallback && !this._geometryBuffer) {
|
|
|
- // Initializes the link with geometry buffer
|
|
|
- this.useGeometryBufferFallback = true;
|
|
|
- }
|
|
|
+ this.renderTargets.push(rt);
|
|
|
|
|
|
- this.imageProcessingPostProcess = new ImageProcessingPostProcess("sceneCompositionPass", 1, null, undefined, this._engine);
|
|
|
- this.imageProcessingPostProcess.autoClear = false;
|
|
|
+ return rt;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -227,15 +250,15 @@ export class PrePassRenderer {
|
|
|
* @param subMesh Submesh on which the effect is applied
|
|
|
*/
|
|
|
public bindAttachmentsForEffect(effect: Effect, subMesh: SubMesh) {
|
|
|
- if (this.enabled) {
|
|
|
+ if (this.enabled && this._currentTarget.enabled) {
|
|
|
if (effect._multiTarget) {
|
|
|
this._engine.bindAttachments(this._multiRenderAttachments);
|
|
|
} else {
|
|
|
this._engine.bindAttachments(this._defaultAttachments);
|
|
|
|
|
|
- if (this._geometryBuffer) {
|
|
|
+ if (this._geometryBuffer && this.currentRTisSceneRT) {
|
|
|
const material = subMesh.getMaterial();
|
|
|
- if (material && this.excludedMaterials.indexOf(material) === -1) {
|
|
|
+ if (material && !material.isPrePassCapable && this.excludedMaterials.indexOf(material) === -1) {
|
|
|
this._geometryBuffer.renderList!.push(subMesh.getRenderingMesh());
|
|
|
}
|
|
|
}
|
|
@@ -243,96 +266,39 @@ export class PrePassRenderer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Restores attachments for single texture draw.
|
|
|
- */
|
|
|
- public restoreAttachments() {
|
|
|
- if (this.enabled && this._defaultAttachments) {
|
|
|
- this._engine.bindAttachments(this._defaultAttachments);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @hidden
|
|
|
- */
|
|
|
- public _beforeCameraDraw() {
|
|
|
- if (this._isDirty) {
|
|
|
- this._update();
|
|
|
- }
|
|
|
-
|
|
|
- if (this._geometryBuffer) {
|
|
|
- this._geometryBuffer.renderList!.length = 0;
|
|
|
- }
|
|
|
+ private _reinitializeAttachments() {
|
|
|
+ const multiRenderLayout = [];
|
|
|
+ const clearLayout = [false];
|
|
|
+ const defaultLayout = [true];
|
|
|
|
|
|
- this._bindFrameBuffer();
|
|
|
- }
|
|
|
+ for (let i = 0; i < this.mrtCount; i++) {
|
|
|
+ multiRenderLayout.push(true);
|
|
|
|
|
|
- /**
|
|
|
- * @hidden
|
|
|
- */
|
|
|
- public _afterCameraDraw() {
|
|
|
- if (this._enabled) {
|
|
|
- const firstCameraPP = this._scene.activeCamera && this._scene.activeCamera._getFirstPostProcess();
|
|
|
- if (firstCameraPP && this._postProcesses.length) {
|
|
|
- this._scene.postProcessManager._prepareFrame();
|
|
|
+ if (i > 0) {
|
|
|
+ clearLayout.push(true);
|
|
|
+ defaultLayout.push(false);
|
|
|
}
|
|
|
- this._scene.postProcessManager.directRender(this._postProcesses, firstCameraPP ? firstCameraPP.inputTexture : null);
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private _checkRTSize() {
|
|
|
- var requiredWidth = this._engine.getRenderWidth(true);
|
|
|
- var requiredHeight = this._engine.getRenderHeight(true);
|
|
|
- var width = this.prePassRT.getRenderWidth();
|
|
|
- var height = this.prePassRT.getRenderHeight();
|
|
|
-
|
|
|
- if (width !== requiredWidth || height !== requiredHeight) {
|
|
|
- this.prePassRT.resize({ width: requiredWidth, height: requiredHeight });
|
|
|
-
|
|
|
- this._updateGeometryBufferLayout();
|
|
|
- this._bindPostProcessChain();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private _bindFrameBuffer() {
|
|
|
- if (this._enabled) {
|
|
|
- this._checkRTSize();
|
|
|
- var internalTexture = this.prePassRT.getInternalTexture();
|
|
|
- if (internalTexture) {
|
|
|
- this._engine.bindFramebuffer(internalTexture);
|
|
|
- }
|
|
|
- }
|
|
|
+ this._multiRenderAttachments = this._engine.buildTextureLayout(multiRenderLayout);
|
|
|
+ this._clearAttachments = this._engine.buildTextureLayout(clearLayout);
|
|
|
+ this._defaultAttachments = this._engine.buildTextureLayout(defaultLayout);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Clears the scene render target (in the sense of settings pixels to the scene clear color value)
|
|
|
- */
|
|
|
- public clear() {
|
|
|
- if (this._enabled) {
|
|
|
- this._bindFrameBuffer();
|
|
|
-
|
|
|
- this._engine.clearAttachments(
|
|
|
- this._multiRenderAttachments,
|
|
|
- this._scene.autoClear || this._scene.forceWireframe || this._scene.forcePointsCloud ? this._scene.clearColor : null,
|
|
|
- this._clearColor,
|
|
|
- this._scene.autoClearDepthAndStencil,
|
|
|
- this._scene.autoClearDepthAndStencil,
|
|
|
- );
|
|
|
-
|
|
|
- this._engine.bindAttachments(this._defaultAttachments);
|
|
|
+ private _resetLayout() {
|
|
|
+ for (let i = 0 ; i < PrePassRenderer._textureFormats.length; i++) {
|
|
|
+ this._textureIndices[PrePassRenderer._textureFormats[i].type] = -1;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- private _setState(enabled: boolean) {
|
|
|
- this._enabled = enabled;
|
|
|
- this._scene.prePass = enabled;
|
|
|
|
|
|
- if (this.imageProcessingPostProcess) {
|
|
|
- this.imageProcessingPostProcess.imageProcessingConfiguration.applyByPostProcess = enabled;
|
|
|
- }
|
|
|
+ this._textureIndices[Constants.PREPASS_COLOR_TEXTURE_TYPE] = 0;
|
|
|
+ this._mrtLayout = [Constants.PREPASS_COLOR_TEXTURE_TYPE];
|
|
|
+ this._mrtFormats = [Constants.TEXTURETYPE_HALF_FLOAT];
|
|
|
+ this.mrtCount = 1;
|
|
|
}
|
|
|
|
|
|
private _updateGeometryBufferLayout() {
|
|
|
+ this._refreshGeometryBufferRendererLink();
|
|
|
+
|
|
|
if (this._geometryBuffer) {
|
|
|
this._geometryBuffer._resetLayout();
|
|
|
|
|
@@ -342,12 +308,16 @@ export class PrePassRenderer {
|
|
|
texturesActivated.push(false);
|
|
|
}
|
|
|
|
|
|
- this._geometryBuffer._linkInternalTexture(this.prePassRT.getInternalTexture()!);
|
|
|
+ this._geometryBuffer._linkInternalTexture(this.defaultRT.getInternalTexture()!);
|
|
|
|
|
|
const matches = [
|
|
|
{
|
|
|
- prePassConstant: Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE,
|
|
|
- geometryBufferConstant: GeometryBufferRenderer.DEPTHNORMAL_TEXTURE_TYPE,
|
|
|
+ prePassConstant: Constants.PREPASS_DEPTH_TEXTURE_TYPE,
|
|
|
+ geometryBufferConstant: GeometryBufferRenderer.DEPTH_TEXTURE_TYPE,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prePassConstant: Constants.PREPASS_NORMAL_TEXTURE_TYPE,
|
|
|
+ geometryBufferConstant: GeometryBufferRenderer.NORMAL_TEXTURE_TYPE,
|
|
|
},
|
|
|
{
|
|
|
prePassConstant: Constants.PREPASS_POSITION_TEXTURE_TYPE,
|
|
@@ -377,7 +347,109 @@ export class PrePassRenderer {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Adds an effect configuration to the prepass.
|
|
|
+ * Restores attachments for single texture draw.
|
|
|
+ */
|
|
|
+ public restoreAttachments() {
|
|
|
+ if (this.enabled && this._currentTarget.enabled && this._defaultAttachments) {
|
|
|
+ this._engine.bindAttachments(this._defaultAttachments);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _beforeDraw(camera?: Camera, faceIndex?: number, layer?: number) {
|
|
|
+ // const previousEnabled = this._enabled && this._currentTarget.enabled;
|
|
|
+
|
|
|
+ if (this._isDirty) {
|
|
|
+ this._update();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this._enabled || !this._currentTarget.enabled) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._geometryBuffer) {
|
|
|
+ this._geometryBuffer.renderList = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ this._setupOutputForThisPass(this._currentTarget, camera);
|
|
|
+ }
|
|
|
+
|
|
|
+ private _prepareFrame(prePassRenderTarget: PrePassRenderTarget, faceIndex?: number, layer?: number) {
|
|
|
+ if (prePassRenderTarget.renderTargetTexture) {
|
|
|
+ prePassRenderTarget.renderTargetTexture._prepareFrame(this._scene, faceIndex, layer, prePassRenderTarget.renderTargetTexture.useCameraPostProcesses);
|
|
|
+ } else if (this._postProcessesSourceForThisPass.length) {
|
|
|
+ this._scene.postProcessManager._prepareFrame();
|
|
|
+ } else {
|
|
|
+ this._engine.restoreDefaultFramebuffer();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _renderPostProcesses(prePassRenderTarget: PrePassRenderTarget, faceIndex?: number) {
|
|
|
+ const firstPP = this._postProcessesSourceForThisPass[0];
|
|
|
+ let outputTexture = firstPP ? firstPP.inputTexture : (prePassRenderTarget.renderTargetTexture ? prePassRenderTarget.renderTargetTexture.getInternalTexture() : null);
|
|
|
+
|
|
|
+ // Build post process chain for this prepass post draw
|
|
|
+ let postProcessChain = this._currentTarget._beforeCompositionPostProcesses;
|
|
|
+
|
|
|
+ if (this._needsCompositionForThisPass) {
|
|
|
+ postProcessChain = postProcessChain.concat([this._currentTarget.imageProcessingPostProcess]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Activates and renders the chain
|
|
|
+ if (postProcessChain.length) {
|
|
|
+ this._scene.postProcessManager._prepareFrame(this._currentTarget.getInternalTexture()!, postProcessChain);
|
|
|
+ this._scene.postProcessManager.directRender(postProcessChain, outputTexture, false, faceIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _afterDraw(faceIndex?: number, layer?: number) {
|
|
|
+ if (this._enabled && this._currentTarget.enabled) {
|
|
|
+ this._prepareFrame(this._currentTarget, faceIndex, layer);
|
|
|
+ this._renderPostProcesses(this._currentTarget, faceIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clears the current prepass render target (in the sense of settings pixels to the scene clear color value)
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _clear() {
|
|
|
+ if (this._enabled && this._currentTarget.enabled) {
|
|
|
+ this._bindFrameBuffer(this._currentTarget);
|
|
|
+
|
|
|
+ // Clearing other attachment with 0 on all other attachments
|
|
|
+ this._engine.bindAttachments(this._clearAttachments);
|
|
|
+ this._engine.clear(this._clearColor, true, false, false);
|
|
|
+ // Regular clear color with the scene clear color of the 1st attachment
|
|
|
+ this._engine.bindAttachments(this._defaultAttachments);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _bindFrameBuffer(prePassRenderTarget: PrePassRenderTarget) {
|
|
|
+ if (this._enabled && this._currentTarget.enabled) {
|
|
|
+ this._currentTarget._checkSize();
|
|
|
+ var internalTexture = this._currentTarget.getInternalTexture();
|
|
|
+ if (internalTexture) {
|
|
|
+ this._engine.bindFramebuffer(internalTexture);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _setEnabled(enabled: boolean) {
|
|
|
+ this._enabled = enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ private _setRenderTargetEnabled(prePassRenderTarget: PrePassRenderTarget, enabled: boolean) {
|
|
|
+ prePassRenderTarget.enabled = enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Adds an effect configuration to the prepass render target.
|
|
|
* If an effect has already been added, it won't add it twice and will return the configuration
|
|
|
* already present.
|
|
|
* @param cfg the effect configuration
|
|
@@ -395,15 +467,6 @@ export class PrePassRenderer {
|
|
|
return cfg;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Returns the index of a texture in the multi render target texture array.
|
|
|
- * @param type Texture type
|
|
|
- * @return The index
|
|
|
- */
|
|
|
- public getIndex(type: number) : number {
|
|
|
- return this._textureIndices[type];
|
|
|
- }
|
|
|
-
|
|
|
private _enable() {
|
|
|
const previousMrtCount = this.mrtCount;
|
|
|
|
|
@@ -413,90 +476,157 @@ export class PrePassRenderer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (this.prePassRT && this.mrtCount !== previousMrtCount) {
|
|
|
- this.prePassRT.updateCount(this.mrtCount, { types: this._mrtFormats });
|
|
|
- }
|
|
|
+ for (let i = 0; i < this.renderTargets.length; i++) {
|
|
|
+ if (this.mrtCount !== previousMrtCount) {
|
|
|
+ this.renderTargets[i].updateCount(this.mrtCount, { types: this._mrtFormats });
|
|
|
+ }
|
|
|
|
|
|
- this._updateGeometryBufferLayout();
|
|
|
- this._resetPostProcessChain();
|
|
|
+ this.renderTargets[i]._resetPostProcessChain();
|
|
|
|
|
|
- for (let i = 0; i < this._effectConfigurations.length; i++) {
|
|
|
- if (this._effectConfigurations[i].enabled) {
|
|
|
- if (!this._effectConfigurations[i].postProcess && this._effectConfigurations[i].createPostProcess) {
|
|
|
- this._effectConfigurations[i].createPostProcess!();
|
|
|
- }
|
|
|
+ for (let j = 0; j < this._effectConfigurations.length; j++) {
|
|
|
+ if (this._effectConfigurations[j].enabled) {
|
|
|
+ // TODO : subsurface scattering has 1 scene-wide effect configuration
|
|
|
+ // solution : do not stock postProcess on effectConfiguration, but in the prepassRenderTarget (hashmap configuration => postProcess)
|
|
|
+ // And call createPostProcess whenever the post process does not exist in the RT
|
|
|
+ if (!this._effectConfigurations[j].postProcess && this._effectConfigurations[j].createPostProcess) {
|
|
|
+ this._effectConfigurations[j].createPostProcess!();
|
|
|
+ }
|
|
|
|
|
|
- if (this._effectConfigurations[i].postProcess) {
|
|
|
- this._postProcesses.push(this._effectConfigurations[i].postProcess!);
|
|
|
+ if (this._effectConfigurations[j].postProcess) {
|
|
|
+ this.renderTargets[i]._beforeCompositionPostProcesses.push(this._effectConfigurations[j].postProcess!);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this._initializeAttachments();
|
|
|
+ this._reinitializeAttachments();
|
|
|
+ this._setEnabled(true);
|
|
|
+ this._updateGeometryBufferLayout();
|
|
|
+ }
|
|
|
|
|
|
- if (!this.imageProcessingPostProcess) {
|
|
|
- this._createCompositionEffect();
|
|
|
+ private _disable() {
|
|
|
+ this._setEnabled(false);
|
|
|
+
|
|
|
+ for (let i = 0; i < this.renderTargets.length; i++) {
|
|
|
+ this._setRenderTargetEnabled(this.renderTargets[i], false);
|
|
|
}
|
|
|
|
|
|
- let isIPPAlreadyPresent = false;
|
|
|
- if (this._scene.activeCamera?._postProcesses) {
|
|
|
- for (let i = 0; i < this._scene.activeCamera._postProcesses.length; i++) {
|
|
|
- if (this._scene.activeCamera._postProcesses[i]?.getClassName() === "ImageProcessingPostProcess") {
|
|
|
- isIPPAlreadyPresent = true;
|
|
|
- }
|
|
|
- }
|
|
|
+ this._resetLayout();
|
|
|
|
|
|
+ for (let i = 0; i < this._effectConfigurations.length; i++) {
|
|
|
+ this._effectConfigurations[i].enabled = false;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (!isIPPAlreadyPresent && !this.disableGammaTransform) {
|
|
|
- this._postProcesses.push(this.imageProcessingPostProcess);
|
|
|
+ private _getPostProcessesSource(prePassRenderTarget: PrePassRenderTarget, camera?: Camera) : Nullable<PostProcess>[] {
|
|
|
+ if (camera) {
|
|
|
+ return camera._postProcesses;
|
|
|
+ } else if (prePassRenderTarget.renderTargetTexture) {
|
|
|
+ if (prePassRenderTarget.renderTargetTexture.useCameraPostProcesses) {
|
|
|
+ const camera = prePassRenderTarget.renderTargetTexture.activeCamera ? prePassRenderTarget.renderTargetTexture.activeCamera : this._scene.activeCamera;
|
|
|
+ return camera ? camera._postProcesses : [];
|
|
|
+ } else if (prePassRenderTarget.renderTargetTexture.postProcesses) {
|
|
|
+ return prePassRenderTarget.renderTargetTexture.postProcesses;
|
|
|
+ } else {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return this._scene.activeCamera ? this._scene.activeCamera._postProcesses : [];
|
|
|
}
|
|
|
- this._bindPostProcessChain();
|
|
|
- this._setState(true);
|
|
|
}
|
|
|
|
|
|
- private _disable() {
|
|
|
- this._setState(false);
|
|
|
- this._resetLayout();
|
|
|
+ private _setupOutputForThisPass(prePassRenderTarget: PrePassRenderTarget, camera?: Camera) {
|
|
|
+ // Order is : draw ===> prePassRenderTarget._postProcesses ==> ipp ==> camera._postProcesses
|
|
|
+ const secondaryCamera = camera && this._scene.activeCameras && !!this._scene.activeCameras.length && this._scene.activeCameras.indexOf(camera) !== 0;
|
|
|
+ this._postProcessesSourceForThisPass = this._getPostProcessesSource(prePassRenderTarget, camera);
|
|
|
+ this._postProcessesSourceForThisPass = (this._postProcessesSourceForThisPass.filter((pp) => { return pp != null; }));
|
|
|
+ this._scene.autoClear = true;
|
|
|
|
|
|
- for (let i = 0; i < this._effectConfigurations.length; i++) {
|
|
|
- this._effectConfigurations[i].enabled = false;
|
|
|
+ const cameraHasImageProcessing = this._hasImageProcessing(this._postProcessesSourceForThisPass);
|
|
|
+ this._needsCompositionForThisPass = !cameraHasImageProcessing &&
|
|
|
+ !this.disableGammaTransform &&
|
|
|
+ this._needsImageProcessing() &&
|
|
|
+ !secondaryCamera;
|
|
|
+
|
|
|
+ const firstCameraPP = this._getFirstPostProcess(this._postProcessesSourceForThisPass);
|
|
|
+ const firstPrePassPP = prePassRenderTarget._beforeCompositionPostProcesses && prePassRenderTarget._beforeCompositionPostProcesses[0];
|
|
|
+ let firstPP = null;
|
|
|
+
|
|
|
+ // Setting the scene-wide post process configuration
|
|
|
+ this._scene.imageProcessingConfiguration.applyByPostProcess = this._needsCompositionForThisPass || cameraHasImageProcessing;
|
|
|
+
|
|
|
+ // Create composition effect if needed
|
|
|
+ if (this._needsCompositionForThisPass && !prePassRenderTarget.imageProcessingPostProcess) {
|
|
|
+ prePassRenderTarget._createCompositionEffect();
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private _resetLayout() {
|
|
|
- for (let i = 0 ; i < this._textureFormats.length; i++) {
|
|
|
- this._textureIndices[this._textureFormats[i].type] = -1;
|
|
|
+ // Setting the prePassRenderTarget as input texture of the first PP
|
|
|
+ if (firstPrePassPP) {
|
|
|
+ firstPP = firstPrePassPP;
|
|
|
+ } else if (this._needsCompositionForThisPass) {
|
|
|
+ firstPP = prePassRenderTarget.imageProcessingPostProcess;
|
|
|
+ } else if (firstCameraPP) {
|
|
|
+ firstPP = firstCameraPP;
|
|
|
}
|
|
|
|
|
|
- this._textureIndices[Constants.PREPASS_COLOR_TEXTURE_TYPE] = 0;
|
|
|
- this._mrtLayout = [Constants.PREPASS_COLOR_TEXTURE_TYPE];
|
|
|
- this._mrtFormats = [Constants.TEXTURETYPE_HALF_FLOAT];
|
|
|
- this.mrtCount = 1;
|
|
|
+ this._bindFrameBuffer(prePassRenderTarget);
|
|
|
+ this._linkInternalTexture(prePassRenderTarget, firstPP);
|
|
|
}
|
|
|
|
|
|
- private _resetPostProcessChain() {
|
|
|
- this._postProcesses = [];
|
|
|
- if (this.imageProcessingPostProcess) {
|
|
|
- this.imageProcessingPostProcess.restoreDefaultInputTexture();
|
|
|
+ private _linkInternalTexture(prePassRenderTarget: PrePassRenderTarget, postProcess: Nullable<PostProcess>) {
|
|
|
+ if (postProcess) {
|
|
|
+ postProcess.autoClear = false;
|
|
|
+ postProcess.inputTexture = prePassRenderTarget.getInternalTexture()!;
|
|
|
}
|
|
|
|
|
|
+ if (prePassRenderTarget._outputPostProcess !== postProcess) {
|
|
|
+ if (prePassRenderTarget._outputPostProcess) {
|
|
|
+ prePassRenderTarget._outputPostProcess.restoreDefaultInputTexture();
|
|
|
+ }
|
|
|
+ prePassRenderTarget._outputPostProcess = postProcess;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (prePassRenderTarget._internalTextureDirty) {
|
|
|
+ this._updateGeometryBufferLayout();
|
|
|
+ prePassRenderTarget._internalTextureDirty = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _needsImageProcessing(): boolean {
|
|
|
for (let i = 0; i < this._effectConfigurations.length; i++) {
|
|
|
- if (this._effectConfigurations[i].postProcess) {
|
|
|
- this._effectConfigurations[i].postProcess!.restoreDefaultInputTexture();
|
|
|
+ if (this._effectConfigurations[i].enabled && this._effectConfigurations[i].needsImageProcessing) {
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- private _bindPostProcessChain() {
|
|
|
- if (this._postProcesses.length) {
|
|
|
- this._postProcesses[0].inputTexture = this.prePassRT.getInternalTexture()!;
|
|
|
- } else {
|
|
|
- const pp = this._scene.activeCamera?._getFirstPostProcess();
|
|
|
- if (pp) {
|
|
|
- pp.inputTexture = this.prePassRT.getInternalTexture()!;
|
|
|
+ private _hasImageProcessing(postProcesses: Nullable<PostProcess>[]): boolean {
|
|
|
+ let isIPPAlreadyPresent = false;
|
|
|
+ if (postProcesses) {
|
|
|
+ for (let i = 0; i < postProcesses.length; i++) {
|
|
|
+ if (postProcesses[i]?.getClassName() === "ImageProcessingPostProcess") {
|
|
|
+ isIPPAlreadyPresent = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return isIPPAlreadyPresent;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Internal, gets the first post proces.
|
|
|
+ * @returns the first post process to be run on this camera.
|
|
|
+ */
|
|
|
+ private _getFirstPostProcess(postProcesses: Nullable<PostProcess>[]): Nullable<PostProcess> {
|
|
|
+ for (var ppIndex = 0; ppIndex < postProcesses.length; ppIndex++) {
|
|
|
+ if (postProcesses[ppIndex] !== null) {
|
|
|
+ return postProcesses[ppIndex];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -517,7 +647,7 @@ export class PrePassRenderer {
|
|
|
this._textureIndices[type] = this._mrtLayout.length;
|
|
|
this._mrtLayout.push(type);
|
|
|
|
|
|
- this._mrtFormats.push(this._textureFormats[type].format);
|
|
|
+ this._mrtFormats.push(PrePassRenderer._textureFormats[type].format);
|
|
|
this.mrtCount++;
|
|
|
}
|
|
|
}
|
|
@@ -526,24 +656,49 @@ export class PrePassRenderer {
|
|
|
private _update() {
|
|
|
this._disable();
|
|
|
let enablePrePass = false;
|
|
|
+ this._scene.imageProcessingConfiguration.applyByPostProcess = false;
|
|
|
|
|
|
for (let i = 0; i < this._scene.materials.length; i++) {
|
|
|
if (this._scene.materials[i].setPrePassRenderer(this)) {
|
|
|
enablePrePass = true;
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const camera = this._scene.activeCamera;
|
|
|
- if (!camera) {
|
|
|
- return;
|
|
|
+ if (enablePrePass) {
|
|
|
+ this._setRenderTargetEnabled(this.defaultRT, true);
|
|
|
}
|
|
|
|
|
|
- const postProcesses = (<Nullable<PostProcess[]>>camera._postProcesses.filter((pp) => { return pp != null; }));
|
|
|
+ let postProcesses;
|
|
|
|
|
|
- if (postProcesses) {
|
|
|
- for (let i = 0; i < postProcesses.length; i++) {
|
|
|
- if (postProcesses[i].setPrePassRenderer(this)) {
|
|
|
- enablePrePass = true;
|
|
|
+ for (let i = 0; i < this.renderTargets.length; i++) {
|
|
|
+ if (this.renderTargets[i].renderTargetTexture) {
|
|
|
+ postProcesses = this._getPostProcessesSource(this.renderTargets[i]);
|
|
|
+ } else {
|
|
|
+ const camera = this._scene.activeCamera;
|
|
|
+ if (!camera) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ postProcesses = camera._postProcesses;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!postProcesses) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ postProcesses = (<Nullable<PostProcess[]>>postProcesses.filter((pp) => { return pp != null; }));
|
|
|
+
|
|
|
+ if (postProcesses) {
|
|
|
+ for (let j = 0; j < postProcesses.length; j++) {
|
|
|
+ if (postProcesses[j].setPrePassRenderer(this)) {
|
|
|
+ this._setRenderTargetEnabled(this.renderTargets[i], true);
|
|
|
+ enablePrePass = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._hasImageProcessing(postProcesses)) {
|
|
|
+ this._scene.imageProcessingConfiguration.applyByPostProcess = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -554,12 +709,6 @@ export class PrePassRenderer {
|
|
|
if (enablePrePass) {
|
|
|
this._enable();
|
|
|
}
|
|
|
-
|
|
|
- if (!this.enabled) {
|
|
|
- // Prepass disabled, we render only on 1 color attachment
|
|
|
- this._engine.restoreDefaultFramebuffer();
|
|
|
- this._engine.restoreSingleAttachment();
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
private _markAllMaterialsAsPrePassDirty() {
|
|
@@ -574,14 +723,15 @@ export class PrePassRenderer {
|
|
|
* Disposes the prepass renderer.
|
|
|
*/
|
|
|
public dispose() {
|
|
|
+ for (let i = this.renderTargets.length - 1; i >= 0; i--) {
|
|
|
+ this.renderTargets[i].dispose();
|
|
|
+ }
|
|
|
+
|
|
|
for (let i = 0; i < this._effectConfigurations.length; i++) {
|
|
|
if (this._effectConfigurations[i].dispose) {
|
|
|
this._effectConfigurations[i].dispose!();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- this.imageProcessingPostProcess.dispose();
|
|
|
- this.prePassRT.dispose();
|
|
|
}
|
|
|
|
|
|
}
|