From 2e3b9d67d48822db5cc5606ae282fe09dbf09a56 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 22 Nov 2024 15:10:42 +0100 Subject: [PATCH 01/20] Refactor to use InternalTexture instead of RenderTargetWrapper --- .../Engines/Extensions/engine.multiRender.ts | 25 +- .../Engines/Extensions/engine.renderTarget.ts | 14 +- .../Engines/WebGL/webGLRenderTargetWrapper.ts | 56 +- .../WebGPU/Extensions/engine.multiRender.ts | 14 +- .../Engines/WebGPU/webgpuHardwareTexture.ts | 12 +- .../Engines/WebGPU/webgpuTextureManager.ts | 4 +- .../core/src/Engines/renderTargetWrapper.ts | 21 +- packages/dev/core/src/Engines/thinEngine.ts | 106 +++- .../dev/core/src/Engines/thinWebGPUEngine.ts | 9 + packages/dev/core/src/Engines/webgpuEngine.ts | 25 +- .../Node/Blocks/Textures/clearBlock.ts | 29 +- .../src/FrameGraph/Node/Blocks/inputBlock.ts | 44 +- .../core/src/FrameGraph/Passes/renderPass.ts | 29 +- .../Tasks/PostProcesses/bloomTask.ts | 58 +- .../PostProcesses/depthOfFieldMergeTask.ts | 4 + .../Tasks/PostProcesses/depthOfFieldTask.ts | 60 +- .../Tasks/PostProcesses/postProcessTask.ts | 10 +- .../Tasks/Rendering/cullObjectsTask.ts | 2 +- .../Tasks/Rendering/geometryRendererTask.ts | 97 ++-- .../Tasks/Rendering/objectRendererTask.ts | 26 +- .../Tasks/Rendering/taaObjectRendererTask.ts | 36 +- .../Tasks/Texture/clearTextureTask.ts | 35 +- .../Tasks/Texture/copyToTextureTask.ts | 4 +- .../Tasks/Texture/generateMipMapsTask.ts | 6 +- .../dev/core/src/FrameGraph/frameGraph.ts | 184 +----- .../src/FrameGraph/frameGraphRenderContext.ts | 122 ++-- .../src/FrameGraph/frameGraphRenderTarget.ts | 98 ++++ .../dev/core/src/FrameGraph/frameGraphTask.ts | 65 ++- .../FrameGraph/frameGraphTextureManager.ts | 543 ++++++++++++------ .../core/src/FrameGraph/frameGraphTypes.ts | 45 +- packages/dev/core/src/FrameGraph/index.ts | 1 + .../src/Materials/Textures/internalTexture.ts | 57 ++ .../Materials/Textures/multiRenderTarget.ts | 5 + .../Textures/textureCreationOptions.ts | 12 +- packages/dev/gui/src/2D/FrameGraph/guiTask.ts | 4 +- .../nodeRenderGraphEditor/src/graphEditor.tsx | 10 +- .../properties/inputNodePropertyComponent.tsx | 21 +- 37 files changed, 1226 insertions(+), 667 deletions(-) create mode 100644 packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts diff --git a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts index 6b231cff086..21025be1295 100644 --- a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts +++ b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts @@ -182,6 +182,7 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o let layerIndex: number[] = []; let layers: number[] = []; let labels: string[] = []; + let dontCreateTextures = false; const rtWrapper = this._createHardwareRenderTargetWrapper(true, false, size) as WebGLRenderTargetWrapper; @@ -201,6 +202,7 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o layerIndex = options.layerIndex || layerIndex; layers = options.layerCounts || layers; labels = options.labels || labels; + dontCreateTextures = options.dontCreateTextures ?? false; if ( this.webGLVersion > 1 && @@ -268,7 +270,7 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o attachments.push(attachment); - if (target === -1) { + if (target === -1 || dontCreateTextures) { continue; } @@ -330,7 +332,7 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o this._internalTexturesCache.push(texture); } - if (generateDepthTexture && this._caps.depthTextureExtension) { + if (generateDepthTexture && this._caps.depthTextureExtension && !dontCreateTextures) { // Depth texture const depthTexture = new InternalTexture(this, InternalTextureSource.Depth); @@ -407,7 +409,24 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o this.resetTextureCache(); - this.updateMultipleRenderTargetTextureSampleCount(rtWrapper, samples, initializeBuffers); + if (!dontCreateTextures) { + this.updateMultipleRenderTargetTextureSampleCount(rtWrapper, samples, initializeBuffers); + } else if (samples > 1) { + const framebuffer = gl.createFramebuffer(); + + if (!framebuffer) { + throw new Error("Unable to create multi sampled framebuffer"); + } + + rtWrapper._samples = samples; + rtWrapper._MSAAFramebuffer = framebuffer; + + if (textureCount > 0 && initializeBuffers) { + this._bindUnboundFramebuffer(framebuffer); + gl.drawBuffers(attachments); + this._bindUnboundFramebuffer(null); + } + } return rtWrapper; }; diff --git a/packages/dev/core/src/Engines/Extensions/engine.renderTarget.ts b/packages/dev/core/src/Engines/Extensions/engine.renderTarget.ts index a7e85b70681..826265c933d 100644 --- a/packages/dev/core/src/Engines/Extensions/engine.renderTarget.ts +++ b/packages/dev/core/src/Engines/Extensions/engine.renderTarget.ts @@ -171,18 +171,8 @@ ThinEngine.prototype._createDepthStencilTexture = function (size: TextureSize, o internalTexture.format === Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 || internalTexture.format === Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8; - let type: GLenum = gl.UNSIGNED_INT; - if (internalTexture.format === Constants.TEXTUREFORMAT_DEPTH16) { - type = gl.UNSIGNED_SHORT; - } else if (internalTexture.format === Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 || internalTexture.format === Constants.TEXTUREFORMAT_DEPTH24_STENCIL8) { - type = gl.UNSIGNED_INT_24_8; - } else if (internalTexture.format === Constants.TEXTUREFORMAT_DEPTH32_FLOAT) { - type = gl.FLOAT; - } else if (internalTexture.format === Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8) { - type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV; - } - - const format: GLenum = hasStencil ? gl.DEPTH_STENCIL : gl.DEPTH_COMPONENT; + const type = this._getWebGLTextureTypeFromDepthTextureFormat(internalTexture.format); + const format = hasStencil ? gl.DEPTH_STENCIL : gl.DEPTH_COMPONENT; const internalFormat = this._getInternalFormatFromDepthTextureFormat(internalTexture.format, true, hasStencil); if (internalTexture.is2DArray) { diff --git a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts index 77db83aa687..5235f8b2ced 100644 --- a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts +++ b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts @@ -5,6 +5,8 @@ import { Constants } from "../constants"; import type { Engine } from "../engine"; import { RenderTargetWrapper } from "../renderTargetWrapper"; import type { ThinEngine } from "../thinEngine"; +import type { WebGLHardwareTexture } from "./webGLHardwareTexture"; +import { HasStencilAspect } from "../../Materials/Textures/internalTexture"; /** @internal */ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { @@ -43,6 +45,38 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { */ public _currentLOD = 0; + public override get depthStencilTexture() { + return this._depthStencilTexture; + } + + public override set depthStencilTexture(texture: Nullable) { + this._depthStencilTexture = texture; + this._generateDepthBuffer = this._generateStencilBuffer = false; + + if (!texture) { + return; + } + + this._generateDepthBuffer = true; + this._generateStencilBuffer = HasStencilAspect(texture.format); + + const engine = this._engine as ThinEngine; + const gl = this._context as WebGL2RenderingContext; + const hardwareTexture = texture._hardwareTexture as Nullable; + + if (texture && hardwareTexture && texture._autoMSAAManagement && this._MSAAFramebuffer) { + const currentFB = engine._currentFramebuffer; + engine._bindUnboundFramebuffer(this._MSAAFramebuffer); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + HasStencilAspect(texture.format) ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, + hardwareTexture.getMSAARenderBuffer() + ); + engine._bindUnboundFramebuffer(currentFB); + } + } + constructor(isMulti: boolean, isCube: boolean, size: TextureSize, engine: ThinEngine, context: WebGLRenderingContext) { super(isMulti, isCube, size, engine); @@ -142,7 +176,8 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { * @param lodLevel defines the lod level to bind to the frame buffer */ private _bindTextureRenderTarget(texture: InternalTexture, attachmentIndex: number = 0, faceIndexOrLayer?: number, lodLevel: number = 0) { - if (!texture._hardwareTexture) { + const hardwareTexture = texture._hardwareTexture as WebGLHardwareTexture; + if (!hardwareTexture) { return; } @@ -151,29 +186,36 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { const currentFB = engine._currentFramebuffer; engine._bindUnboundFramebuffer(framebuffer); + let attachment: any; if (engine.webGLVersion > 1) { const gl = this._context as WebGL2RenderingContext; - const attachment = (gl)["COLOR_ATTACHMENT" + attachmentIndex]; + attachment = (gl)["COLOR_ATTACHMENT" + attachmentIndex]; if (texture.is2DArray || texture.is3D) { faceIndexOrLayer = faceIndexOrLayer ?? this.layerIndices?.[attachmentIndex] ?? 0; - gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, texture._hardwareTexture.underlyingResource, lodLevel, faceIndexOrLayer); + gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, hardwareTexture.underlyingResource, lodLevel, faceIndexOrLayer); } else if (texture.isCube) { // if face index is not specified, try to query it from faceIndices // default is face 0 faceIndexOrLayer = faceIndexOrLayer ?? this.faceIndices?.[attachmentIndex] ?? 0; - gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndexOrLayer, texture._hardwareTexture.underlyingResource, lodLevel); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndexOrLayer, hardwareTexture.underlyingResource, lodLevel); } else { - gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture._hardwareTexture.underlyingResource, lodLevel); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, hardwareTexture.underlyingResource, lodLevel); } } else { // Default behavior (WebGL) const gl = this._context; - const attachment = (gl)["COLOR_ATTACHMENT" + attachmentIndex + "_WEBGL"]; + attachment = (gl)["COLOR_ATTACHMENT" + attachmentIndex + "_WEBGL"]; const target = faceIndexOrLayer !== undefined ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndexOrLayer : gl.TEXTURE_2D; - gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, target, texture._hardwareTexture.underlyingResource, lodLevel); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, target, hardwareTexture.underlyingResource, lodLevel); + } + + if (texture._autoMSAAManagement && this._MSAAFramebuffer) { + const gl = this._context; + engine._bindUnboundFramebuffer(this._MSAAFramebuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, hardwareTexture.getMSAARenderBuffer()); } engine._bindUnboundFramebuffer(currentFB); diff --git a/packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts b/packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts index a46ea75c94a..e63122a1f32 100644 --- a/packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts +++ b/packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts @@ -119,6 +119,7 @@ WebGPUEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, let layers: number[] = []; let labels: string[] = []; let creationFlags: number[] = []; + let dontCreateTextures = false; const rtWrapper = this._createHardwareRenderTargetWrapper(true, false, size) as WebGPURenderTargetWrapper; @@ -140,6 +141,7 @@ WebGPUEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, labels = options.labels || labels; creationFlags = options.creationFlags || creationFlags; samples = options.samples ?? samples; + dontCreateTextures = options.dontCreateTextures ?? false; } const width = (<{ width: number; height: number }>size).width ?? size; @@ -156,7 +158,7 @@ WebGPUEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, rtWrapper._defaultAttachments = defaultAttachments; let depthStencilTexture: Nullable = null; - if (generateDepthBuffer || generateStencilBuffer || generateDepthTexture) { + if ((generateDepthBuffer || generateStencilBuffer || generateDepthTexture) && !dontCreateTextures) { if (!generateDepthTexture) { // The caller doesn't want a depth texture, so we are free to use the depth texture format we want. // So, we will align with what the WebGL engine does @@ -200,7 +202,7 @@ WebGPUEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, attachments.push(i + 1); defaultAttachments.push(initializeBuffers ? i + 1 : i === 0 ? 1 : 0); - if (target === -1) { + if (target === -1 || dontCreateTextures) { continue; } @@ -259,13 +261,17 @@ WebGPUEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, rtWrapper.setTextures(textures); rtWrapper.setLayerAndFaceIndices(layerIndex, faceIndex); - this.updateMultipleRenderTargetTextureSampleCount(rtWrapper, samples); + if (!dontCreateTextures) { + this.updateMultipleRenderTargetTextureSampleCount(rtWrapper, samples); + } else { + rtWrapper._samples = samples; + } return rtWrapper; }; WebGPUEngine.prototype.updateMultipleRenderTargetTextureSampleCount = function (rtWrapper: Nullable, samples: number): number { - if (!rtWrapper || !rtWrapper.textures || rtWrapper.textures[0].samples === samples) { + if (!rtWrapper || !rtWrapper.textures || rtWrapper.textures.length === 0 || rtWrapper.textures[0].samples === samples) { return samples; } diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuHardwareTexture.ts b/packages/dev/core/src/Engines/WebGPU/webgpuHardwareTexture.ts index 7fdfbb5f8ce..8a75c1ee61b 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuHardwareTexture.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuHardwareTexture.ts @@ -6,6 +6,7 @@ import type { Nullable } from "../../types"; // eslint-disable-next-line @typescript-eslint/naming-convention import * as WebGPUConstants from "./webgpuConstants"; import { WebGPUTextureHelper } from "./webgpuTextureHelper"; +import type { WebGPUEngine } from "../webgpuEngine"; /** @internal */ export class WebGPUHardwareTexture implements HardwareTextureWrapper { @@ -55,12 +56,12 @@ export class WebGPUHardwareTexture implements HardwareTextureWrapper { public releaseMSAATexture(index?: number): void { if (this._webgpuMSAATexture) { - if (index) { - this._webgpuMSAATexture[index]?.destroy(); + if (index !== undefined) { + this._engine._textureHelper.releaseTexture(this._webgpuMSAATexture[index]); delete this._webgpuMSAATexture[index]; } else { for (const texture of this._webgpuMSAATexture) { - texture?.destroy(); + this._engine._textureHelper.releaseTexture(texture); } this._webgpuMSAATexture = null; } @@ -73,7 +74,10 @@ export class WebGPUHardwareTexture implements HardwareTextureWrapper { public textureUsages = 0; public textureAdditionalUsages = 0; - constructor(existingTexture: Nullable = null) { + constructor( + private _engine: WebGPUEngine, + existingTexture: Nullable = null + ) { this._webgpuTexture = existingTexture; this._webgpuMSAATexture = null; this.view = null; diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuTextureManager.ts b/packages/dev/core/src/Engines/WebGPU/webgpuTextureManager.ts index 7c6d8d28f04..5903ae7fce3 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuTextureManager.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuTextureManager.ts @@ -1011,7 +1011,7 @@ export class WebGPUTextureManager { dontCreateMSAATexture?: boolean ): WebGPUHardwareTexture { if (!texture._hardwareTexture) { - texture._hardwareTexture = new WebGPUHardwareTexture(); + texture._hardwareTexture = new WebGPUHardwareTexture(this._engine); } if (width === undefined) { @@ -1165,7 +1165,7 @@ export class WebGPUTextureManager { this._commandEncoderForCreation, WebGPUConstants.TextureUsage.RenderAttachment, 0, - texture.label ? "MSAA" + texture.label : undefined + texture.label ? "MSAA_" + texture.label : "MSAA" ); gpuTextureWrapper.setMSAATexture(gpuMSAATexture, index); } diff --git a/packages/dev/core/src/Engines/renderTargetWrapper.ts b/packages/dev/core/src/Engines/renderTargetWrapper.ts index 4ce7010957f..2ca96449d18 100644 --- a/packages/dev/core/src/Engines/renderTargetWrapper.ts +++ b/packages/dev/core/src/Engines/renderTargetWrapper.ts @@ -1,5 +1,5 @@ import type { InternalTexture } from "../Materials/Textures/internalTexture"; -import { InternalTextureSource } from "../Materials/Textures/internalTexture"; +import { HasStencilAspect, InternalTextureSource } from "../Materials/Textures/internalTexture"; import type { RenderTargetCreationOptions, TextureSize } from "../Materials/Textures/textureCreationOptions"; import type { Nullable } from "../types"; import { Constants } from "./constants"; @@ -49,12 +49,23 @@ export class RenderTargetWrapper { public label?: string; /** - * Gets the depth/stencil texture (if created by a createDepthStencilTexture() call) + * Gets or sets the depth/stencil texture */ public get depthStencilTexture() { return this._depthStencilTexture; } + public set depthStencilTexture(texture: Nullable) { + this._depthStencilTexture = texture; + + this._generateDepthBuffer = this._generateStencilBuffer = false; + + if (texture) { + this._generateDepthBuffer = true; + this._generateStencilBuffer = HasStencilAspect(texture.format); + } + } + /** * Indicates if the depth/stencil texture has a stencil aspect */ @@ -101,14 +112,14 @@ export class RenderTargetWrapper { * Gets the width of the render target wrapper */ public get width(): number { - return (<{ width: number; height: number }>this._size).width || this._size; + return (<{ width: number; height: number }>this._size).width ?? this._size; } /** * Gets the height of the render target wrapper */ public get height(): number { - return (<{ width: number; height: number }>this._size).height || this._size; + return (<{ width: number; height: number }>this._size).height ?? this._size; } /** @@ -537,7 +548,7 @@ export class RenderTargetWrapper { */ public releaseTextures(): void { if (this._textures) { - for (let i = 0; i < this._textures?.length ?? 0; ++i) { + for (let i = 0; i < this._textures.length; ++i) { this._textures[i].dispose(); } } diff --git a/packages/dev/core/src/Engines/thinEngine.ts b/packages/dev/core/src/Engines/thinEngine.ts index 7dc224d2569..36655908964 100644 --- a/packages/dev/core/src/Engines/thinEngine.ts +++ b/packages/dev/core/src/Engines/thinEngine.ts @@ -47,7 +47,7 @@ import { AbstractEngine } from "./abstractEngine"; import { Constants } from "./constants"; import { WebGLHardwareTexture } from "./WebGL/webGLHardwareTexture"; import { ShaderLanguage } from "../Materials/shaderLanguage"; -import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture"; +import { InternalTexture, InternalTextureSource, IsDepthTexture, HasStencilAspect } from "../Materials/Textures/internalTexture"; import { Effect } from "../Materials/effect"; import { _ConcatenateShader, _getGlobalDefines } from "./abstractEngine.functions"; import { resetCachedPipeline } from "core/Materials/effect.functions"; @@ -2841,20 +2841,26 @@ export class ThinEngine extends AbstractEngine { source = InternalTextureSource.Unknown ): InternalTexture { let generateMipMaps = false; + let createMipMaps = false; let type = Constants.TEXTURETYPE_UNSIGNED_INT; let samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE; let format = Constants.TEXTUREFORMAT_RGBA; let useSRGBBuffer = false; let samples = 1; let label: string | undefined; + let createMSAATexture = false; + let comparisonFunction = 0; if (options !== undefined && typeof options === "object") { generateMipMaps = !!options.generateMipMaps; + createMipMaps = !!options.createMipMaps; type = options.type === undefined ? Constants.TEXTURETYPE_UNSIGNED_INT : options.type; samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode; format = options.format === undefined ? Constants.TEXTUREFORMAT_RGBA : options.format; useSRGBBuffer = options.useSRGBBuffer === undefined ? false : options.useSRGBBuffer; samples = options.samples ?? 1; label = options.label; + createMSAATexture = !!options.createMSAATexture; + comparisonFunction = options.comparisonFunction || 0; } else { generateMipMaps = !!options; } @@ -2873,17 +2879,22 @@ export class ThinEngine extends AbstractEngine { Logger.Warn("Float textures are not supported. Type forced to TEXTURETYPE_UNSIGNED_BYTE"); } + const isDepthTexture = IsDepthTexture(format); + const hasStencil = HasStencilAspect(format); + const gl = this._gl; const texture = new InternalTexture(this, source); const width = (<{ width: number; height: number; depth?: number; layers?: number }>size).width || size; const height = (<{ width: number; height: number; depth?: number; layers?: number }>size).height || size; const depth = (<{ width: number; height: number; depth?: number; layers?: number }>size).depth || 0; const layers = (<{ width: number; height: number; depth?: number; layers?: number }>size).layers || 0; - const filters = this._getSamplingParameters(samplingMode, generateMipMaps); + const filters = this._getSamplingParameters(samplingMode, (generateMipMaps || createMipMaps) && !isDepthTexture); const target = layers !== 0 ? gl.TEXTURE_2D_ARRAY : depth !== 0 ? gl.TEXTURE_3D : gl.TEXTURE_2D; - const sizedFormat = this._getRGBABufferInternalSizedFormat(type, format, useSRGBBuffer); - const internalFormat = this._getInternalFormat(format); - const textureType = this._getWebGLTextureType(type); + const sizedFormat = isDepthTexture + ? this._getInternalFormatFromDepthTextureFormat(format, true, hasStencil) + : this._getRGBABufferInternalSizedFormat(type, format, useSRGBBuffer); + const internalFormat = isDepthTexture ? (hasStencil ? gl.DEPTH_STENCIL : gl.DEPTH_COMPONENT) : this._getInternalFormat(format); + const textureType = isDepthTexture ? this._getWebGLTextureTypeFromDepthTextureFormat(format) : this._getWebGLTextureType(type); // Bind this._bindTextureDirectly(target, texture); @@ -2903,8 +2914,18 @@ export class ThinEngine extends AbstractEngine { gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + if (isDepthTexture && this.webGLVersion > 1) { + if (comparisonFunction === 0) { + gl.texParameteri(target, gl.TEXTURE_COMPARE_FUNC, Constants.LEQUAL); + gl.texParameteri(target, gl.TEXTURE_COMPARE_MODE, gl.NONE); + } else { + gl.texParameteri(target, gl.TEXTURE_COMPARE_FUNC, comparisonFunction); + gl.texParameteri(target, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + } + } + // MipMaps - if (generateMipMaps) { + if (generateMipMaps || createMipMaps) { this._gl.generateMipmap(target); } @@ -2915,7 +2936,7 @@ export class ThinEngine extends AbstractEngine { texture.baseHeight = height; texture.width = width; texture.height = height; - texture.depth = layers; + texture.depth = layers || depth; texture.isReady = true; texture.samples = samples; texture.generateMipMaps = generateMipMaps; @@ -2923,9 +2944,48 @@ export class ThinEngine extends AbstractEngine { texture.type = type; texture.format = format; texture.label = label; + texture.comparisonFunction = comparisonFunction; this._internalTexturesCache.push(texture); + if (createMSAATexture) { + let renderBuffer: Nullable = null; + + if (IsDepthTexture(texture.format)) { + renderBuffer = this._setupFramebufferDepthAttachments( + HasStencilAspect(texture.format), + texture.format !== Constants.TEXTUREFORMAT_STENCIL8, + texture.width, + texture.height, + samples, + texture.format, + true + ); + } else { + renderBuffer = this._createRenderBuffer( + texture.width, + texture.height, + samples, + -1 /* not used */, + this._getRGBABufferInternalSizedFormat(texture.type, texture.format, texture._useSRGBBuffer), + -1 /* attachment */ + ); + } + + if (!renderBuffer) { + throw new Error("Unable to create render buffer"); + } + + texture._autoMSAAManagement = true; + + let hardwareTexture = texture._hardwareTexture as Nullable; + if (!hardwareTexture) { + hardwareTexture = texture._hardwareTexture = this._createHardwareTexture() as WebGLHardwareTexture; + } + + hardwareTexture.addMSAARenderBuffer(renderBuffer); + } + return texture; } @@ -3459,6 +3519,25 @@ export class ThinEngine extends AbstractEngine { return internalFormat; } + public _getWebGLTextureTypeFromDepthTextureFormat(textureFormat: number): GLenum { + const gl = this._gl; + + let type: GLenum = gl.UNSIGNED_INT; + if (textureFormat === Constants.TEXTUREFORMAT_DEPTH16) { + type = gl.UNSIGNED_SHORT; + } else if (textureFormat === Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 || textureFormat === Constants.TEXTUREFORMAT_DEPTH24_STENCIL8) { + type = gl.UNSIGNED_INT_24_8; + } else if (textureFormat === Constants.TEXTUREFORMAT_DEPTH32_FLOAT) { + type = gl.FLOAT; + } else if (textureFormat === Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8) { + type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV; + } else if (textureFormat === Constants.TEXTUREFORMAT_STENCIL8) { + type = gl.UNSIGNED_BYTE; + } + + return type; + } + /** * @internal */ @@ -3468,7 +3547,8 @@ export class ThinEngine extends AbstractEngine { width: number, height: number, samples = 1, - depthTextureFormat?: number + depthTextureFormat?: number, + dontBindRenderBufferToFrameBuffer = false ): Nullable { const gl = this._gl; @@ -3478,13 +3558,13 @@ export class ThinEngine extends AbstractEngine { // Create the depth/stencil buffer if (generateStencilBuffer && generateDepthBuffer) { - return this._createRenderBuffer(width, height, samples, gl.DEPTH_STENCIL, internalFormat, gl.DEPTH_STENCIL_ATTACHMENT); + return this._createRenderBuffer(width, height, samples, gl.DEPTH_STENCIL, internalFormat, dontBindRenderBufferToFrameBuffer ? -1 : gl.DEPTH_STENCIL_ATTACHMENT); } if (generateDepthBuffer) { - return this._createRenderBuffer(width, height, samples, internalFormat, internalFormat, gl.DEPTH_ATTACHMENT); + return this._createRenderBuffer(width, height, samples, internalFormat, internalFormat, dontBindRenderBufferToFrameBuffer ? -1 : gl.DEPTH_ATTACHMENT); } if (generateStencilBuffer) { - return this._createRenderBuffer(width, height, samples, internalFormat, internalFormat, gl.STENCIL_ATTACHMENT); + return this._createRenderBuffer(width, height, samples, internalFormat, internalFormat, dontBindRenderBufferToFrameBuffer ? -1 : gl.STENCIL_ATTACHMENT); } return null; @@ -3527,7 +3607,9 @@ export class ThinEngine extends AbstractEngine { gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); } - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderBuffer); + if (attachment !== -1) { + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderBuffer); + } if (unbindBuffer) { gl.bindRenderbuffer(gl.RENDERBUFFER, null); diff --git a/packages/dev/core/src/Engines/thinWebGPUEngine.ts b/packages/dev/core/src/Engines/thinWebGPUEngine.ts index 62e4edd3a83..8b6cc1d93fc 100644 --- a/packages/dev/core/src/Engines/thinWebGPUEngine.ts +++ b/packages/dev/core/src/Engines/thinWebGPUEngine.ts @@ -56,6 +56,9 @@ export abstract class ThinWebGPUEngine extends AbstractEngine { /** @internal */ public _timestampIndex = 0; + /** @internal */ + public _debugStackRenderPass: string[] = []; + /** * Gets the GPU time spent in the main render pass for the last frame rendered (in nanoseconds). * You have to enable the "timestamp-query" extension in the engine constructor options and set engine.enableGPUTimingMeasurements = true. @@ -95,6 +98,12 @@ export abstract class ThinWebGPUEngine extends AbstractEngine { return 0; } + if (this._debugStackRenderPass.length !== 0) { + for (let i = 0; i < this._debugStackRenderPass.length; ++i) { + this._currentRenderPass.popDebugGroup(); + } + } + const currentPassIndex = this._currentPassIsMainPass() ? 2 : 1; if (!this._snapshotRendering.endRenderPass(this._currentRenderPass) && !this.compatibilityMode) { diff --git a/packages/dev/core/src/Engines/webgpuEngine.ts b/packages/dev/core/src/Engines/webgpuEngine.ts index b972d907ccb..014e6383e84 100644 --- a/packages/dev/core/src/Engines/webgpuEngine.ts +++ b/packages/dev/core/src/Engines/webgpuEngine.ts @@ -335,8 +335,6 @@ export class WebGPUEngine extends ThinWebGPUEngine { }; /** @internal */ public _pendingDebugCommands: Array<[string, Nullable, number?]> = []; - /** @internal */ - public _debugStackRenderPass: string[] = []; // DrawCall Life Cycle // Effect is on the parent class @@ -917,7 +915,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { this._context = this._renderingCanvas.getContext("webgpu") as unknown as GPUCanvasContext; this._configureContext(); this._colorFormat = this._options.swapChainFormat!; - this._mainRenderPassWrapper.colorAttachmentGPUTextures = [new WebGPUHardwareTexture()]; + this._mainRenderPassWrapper.colorAttachmentGPUTextures = [new WebGPUHardwareTexture(this)]; this._mainRenderPassWrapper.colorAttachmentGPUTextures[0]!.format = this._colorFormat; this._setColorFormat(this._mainRenderPassWrapper); } @@ -2226,7 +2224,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { /** @internal */ public _createHardwareTexture(): HardwareTextureWrapper { - return new WebGPUHardwareTexture(); + return new WebGPUHardwareTexture(this); } /** @@ -2271,6 +2269,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { if (options !== undefined && typeof options === "object") { fullOptions.generateMipMaps = options.generateMipMaps; + fullOptions.createMipMaps = options.createMipMaps; fullOptions.type = options.type === undefined ? Constants.TEXTURETYPE_UNSIGNED_INT : options.type; fullOptions.samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode; fullOptions.format = options.format === undefined ? Constants.TEXTUREFORMAT_RGBA : options.format; @@ -2312,7 +2311,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { texture.depth = depth || layers; texture.isReady = true; texture.samples = fullOptions.samples; - texture.generateMipMaps = fullOptions.generateMipMaps ? true : false; + texture.generateMipMaps = !!fullOptions.generateMipMaps; texture.samplingMode = fullOptions.samplingMode; texture.type = fullOptions.type; texture.format = fullOptions.format; @@ -2326,7 +2325,19 @@ export class WebGPUEngine extends ThinWebGPUEngine { this._internalTexturesCache.push(texture); if (!delayGPUTextureCreation) { + const createMipMapsOnly = !fullOptions.generateMipMaps && fullOptions.createMipMaps; + + if (createMipMapsOnly) { + // So that the call to createGPUTextureForInternalTexture creates the mipmaps + texture.generateMipMaps = true; + } + this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, layers || 1, fullOptions.creationFlags); + + if (createMipMapsOnly) { + // So that we don't automatically generate mipmaps when the render target is unbound + texture.generateMipMaps = false; + } } return texture; @@ -2462,7 +2473,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { * @returns the babylon internal texture */ public wrapWebGPUTexture(texture: GPUTexture): InternalTexture { - const hardwareTexture = new WebGPUHardwareTexture(texture); + const hardwareTexture = new WebGPUHardwareTexture(this, texture); const internalTexture = new InternalTexture(this, InternalTextureSource.Unknown, true); internalTexture._hardwareTexture = hardwareTexture; internalTexture.isReady = true; @@ -2481,7 +2492,7 @@ export class WebGPUEngine extends ThinWebGPUEngine { /** * @internal */ - public _getUseSRGBBuffer(useSRGBBuffer: boolean, noMipmap: boolean): boolean { + public _getUseSRGBBuffer(useSRGBBuffer: boolean, _noMipmap: boolean): boolean { return useSRGBBuffer && this._caps.supportSRGBBuffers; } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts index 5767dc4484b..d282a01b8ea 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts @@ -29,11 +29,17 @@ export class NodeRenderGraphClearBlock extends NodeRenderGraphBlock { public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { super(name, frameGraph, scene); - this.registerInput("texture", NodeRenderGraphBlockConnectionPointTypes.Texture); + this.registerInput("texture", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("depth", NodeRenderGraphBlockConnectionPointTypes.TextureBackBufferDepthStencilAttachment, true); + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); + this.registerOutput("outputDepth", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); this.texture.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAll); + this.depth.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment); + this.output._typeConnectionSource = this.texture; + this.outputDepth._typeConnectionSource = this.depth; this._frameGraphTask = new FrameGraphClearTextureTask(name, frameGraph); } @@ -85,6 +91,7 @@ export class NodeRenderGraphClearBlock extends NodeRenderGraphBlock { public override getClassName() { return "NodeRenderGraphClearBlock"; } + /** * Gets the texture input component */ @@ -92,6 +99,13 @@ export class NodeRenderGraphClearBlock extends NodeRenderGraphBlock { return this._inputs[0]; } + /** + * Gets the depth texture input component + */ + public get depth(): NodeRenderGraphConnectionPoint { + return this._inputs[1]; + } + /** * Gets the output component */ @@ -99,17 +113,30 @@ export class NodeRenderGraphClearBlock extends NodeRenderGraphBlock { return this._outputs[0]; } + /** + * Gets the output depth component + */ + public get outputDepth(): NodeRenderGraphConnectionPoint { + return this._outputs[1]; + } + protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); this._frameGraphTask.name = this.name; this._propagateInputValueToOutput(this.texture, this.output); + this._propagateInputValueToOutput(this.depth, this.outputDepth); const textureConnectedPoint = this.texture.connectedPoint; if (textureConnectedPoint) { this._frameGraphTask.destinationTexture = textureConnectedPoint.value as FrameGraphTextureHandle; } + + const depthConnectedPoint = this.depth.connectedPoint; + if (depthConnectedPoint) { + this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; + } } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts index db3b09c0339..df490d4e017 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts @@ -5,7 +5,6 @@ import type { FrameGraph, NodeRenderGraphBuildState, Camera, - RenderTargetWrapper, InternalTexture, Nullable, FrameGraphTextureCreationOptions, @@ -20,7 +19,7 @@ import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorat import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../../FrameGraph/frameGraphTypes"; import { Constants } from "../../../Engines/constants"; -export type NodeRenderGraphValueType = RenderTargetWrapper | Camera | FrameGraphObjectList; +export type NodeRenderGraphValueType = InternalTexture | Camera | FrameGraphObjectList; export type NodeRenderGraphInputCreationOptions = FrameGraphTextureCreationOptions; @@ -86,12 +85,10 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { size: { width: 100, height: 100 }, options: { createMipMaps: false, - generateMipMaps: false, types: [Constants.TEXTURETYPE_UNSIGNED_BYTE], formats: [Constants.TEXTUREFORMAT_RGBA], samples: 1, useSRGBBuffers: [false], - generateDepthBuffer: false, }, sizeIsPercentage: true, }; @@ -103,13 +100,11 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { size: { width: 100, height: 100 }, options: { createMipMaps: false, - generateMipMaps: false, - depthTextureFormat: Constants.TEXTUREFORMAT_DEPTH24_STENCIL8, - textureCount: 0, + types: [Constants.TEXTURETYPE_UNSIGNED_BYTE], + formats: [Constants.TEXTUREFORMAT_DEPTH24_STENCIL8], + useSRGBBuffers: [false], + labels: [this.name], samples: 1, - generateDepthTexture: true, - generateDepthBuffer: true, - generateStencilBuffer: true, }, sizeIsPercentage: true, }; @@ -151,23 +146,12 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { } /** - * Gets the value as a render target wrapper - * @returns The value as a render target wrapper if it is a render target wrapper, otherwise undefined - */ - public getValueAsRenderTargetWrapper(): Nullable { - if ((this._storedValue as RenderTargetWrapper).shareDepth) { - return this._storedValue as RenderTargetWrapper; - } - return null; - } - - /** - * Gets the value as a render target wrapper - * @returns The internal texture stored in value if value is a render target wrapper or a thin texture, otherwise null + * Gets the value as an internal texture + * @returns The internal texture stored in value if value is an internal texture, otherwise null */ public getInternalTextureFromValue(): Nullable { - if ((this._storedValue as RenderTargetWrapper).shareDepth) { - return (this._storedValue as RenderTargetWrapper).texture; + if ((this._storedValue as InternalTexture)._swapAndDie) { + return this._storedValue as InternalTexture; } return null; } @@ -243,9 +227,9 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { if (this._storedValue === undefined || this._storedValue === null) { throw new Error(`NodeRenderGraphInputBlock: External input "${this.name}" is not set`); } - const texture = this.getValueAsRenderTargetWrapper(); + const texture = this.getInternalTextureFromValue(); if (texture) { - this.output.value = this._frameGraph.importTexture(this.name, texture, this.output.value as FrameGraphTextureHandle); + this.output.value = this._frameGraph.textureManager.importTexture(this.name, texture, this.output.value as FrameGraphTextureHandle); } } return; @@ -258,7 +242,7 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { throw new Error(`NodeRenderGraphInputBlock: Creation options are missing for texture "${this.name}"`); } - this.output.value = this._frameGraph.createRenderTargetTexture(this.name, textureCreateOptions); + this.output.value = this._frameGraph.textureManager.createRenderTargetTexture(this.name, textureCreateOptions); } } @@ -301,6 +285,10 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { this.output.type = this._type; this.isExternal = serializationObject.isExternal; if (serializationObject.creationOptions) { + if (serializationObject.creationOptions.options.depthTextureFormat !== undefined) { + // Backward compatibility - remove this code in the future + serializationObject.creationOptions.options.formats = [serializationObject.creationOptions.options.depthTextureFormat]; + } this.creationOptions = serializationObject.creationOptions; } } diff --git a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts index eac30164f18..20a8b148c82 100644 --- a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts +++ b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { Nullable, FrameGraphRenderContext, AbstractEngine, IFrameGraphPass, FrameGraphTextureHandle, FrameGraphTask } from "core/index"; +import type { Nullable, FrameGraphRenderContext, AbstractEngine, IFrameGraphPass, FrameGraphTextureHandle, FrameGraphTask, FrameGraphRenderTarget } from "core/index"; import { FrameGraphPass } from "./pass"; /** @@ -7,10 +7,10 @@ import { FrameGraphPass } from "./pass"; */ export class FrameGraphRenderPass extends FrameGraphPass { protected _engine: AbstractEngine; - protected _renderTarget: FrameGraphTextureHandle; + protected _renderTarget: FrameGraphTextureHandle | FrameGraphTextureHandle[] | undefined; protected _renderTargetDepth: FrameGraphTextureHandle | undefined; protected _usedTextures: FrameGraphTextureHandle[] = []; - protected _depthShared = false; + protected _frameGraphRenderTarget: FrameGraphRenderTarget | undefined; /** * Checks if a pass is a render pass. @@ -22,9 +22,9 @@ export class FrameGraphRenderPass extends FrameGraphPass { const errMsg = super._isValid(); - return errMsg ? errMsg : this._renderTarget !== undefined ? null : "Render target is not set (call setRenderTarget to set it)"; + return errMsg + ? errMsg + : this._renderTarget !== undefined || this.renderTargetDepth !== undefined + ? null + : "Render target and render target depth cannot both be undefined."; } } diff --git a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/bloomTask.ts b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/bloomTask.ts index f878d9d2f75..7cc4e08ea7c 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/bloomTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/bloomTask.ts @@ -43,11 +43,37 @@ export class FrameGraphBloomTask extends FrameGraphTask { */ public readonly hdr: boolean; - private _downscale: FrameGraphExtractHighlightsTask; - private _blurX: FrameGraphBlurTask; - private _blurY: FrameGraphBlurTask; - private _merge: FrameGraphBloomMergeTask; - private _defaultPipelineTextureType: number; + /** + * The name of the task. + */ + public override get name() { + return this._name; + } + + public override set name(name: string) { + this._name = name; + if (this._downscale) { + this._downscale.name = `${name} Downscale`; + } + + if (this._blurX) { + this._blurX.name = `${name} Blur X`; + } + + if (this._blurY) { + this._blurY.name = `${name} Blur Y`; + } + + if (this._merge) { + this._merge.name = `${name} Merge`; + } + } + + private readonly _downscale: FrameGraphExtractHighlightsTask; + private readonly _blurX: FrameGraphBlurTask; + private readonly _blurY: FrameGraphBlurTask; + private readonly _merge: FrameGraphBloomMergeTask; + private readonly _defaultPipelineTextureType: number; /** * Constructs a new bloom task. @@ -85,7 +111,7 @@ export class FrameGraphBloomTask extends FrameGraphTask { this._blurY = new FrameGraphBlurTask(`${name} Blur Y`, this._frameGraph, this.bloom._blurY); this._merge = new FrameGraphBloomMergeTask(`${name} Merge`, this._frameGraph, this.bloom._merge); - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public override isReady() { @@ -97,7 +123,7 @@ export class FrameGraphBloomTask extends FrameGraphTask { throw new Error("FrameGraphBloomTask: sourceTexture is required"); } - const sourceTextureDescription = this._frameGraph.getTextureDescription(this.sourceTexture); + const sourceTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.sourceTexture); const textureCreationOptions: FrameGraphTextureCreationOptions = { size: { @@ -106,45 +132,39 @@ export class FrameGraphBloomTask extends FrameGraphTask { }, options: { createMipMaps: false, - generateMipMaps: false, types: [this._defaultPipelineTextureType], - samplingModes: [Constants.TEXTURE_BILINEAR_SAMPLINGMODE], formats: [Constants.TEXTUREFORMAT_RGBA], samples: 1, useSRGBBuffers: [false], - generateDepthBuffer: false, - generateStencilBuffer: false, - label: "", + labels: [""], }, sizeIsPercentage: false, }; - const downscaleTextureHandle = this._frameGraph.createRenderTargetTexture(this._downscale.name, textureCreationOptions); + const downscaleTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._downscale.name, textureCreationOptions); this._downscale.sourceTexture = this.sourceTexture; this._downscale.sourceSamplingMode = Constants.TEXTURE_BILINEAR_SAMPLINGMODE; this._downscale.destinationTexture = downscaleTextureHandle; this._downscale.record(true); - const blurXTextureHandle = this._frameGraph.createRenderTargetTexture(this._blurX.name, textureCreationOptions); + const blurXTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._blurX.name, textureCreationOptions); this._blurX.sourceTexture = downscaleTextureHandle; this._blurX.sourceSamplingMode = Constants.TEXTURE_BILINEAR_SAMPLINGMODE; this._blurX.destinationTexture = blurXTextureHandle; this._blurX.record(true); - const blurYTextureHandle = this._frameGraph.createRenderTargetTexture(this._blurY.name, textureCreationOptions); + const blurYTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._blurY.name, textureCreationOptions); this._blurY.sourceTexture = blurXTextureHandle; this._blurY.sourceSamplingMode = Constants.TEXTURE_BILINEAR_SAMPLINGMODE; this._blurY.destinationTexture = blurYTextureHandle; this._blurY.record(true); - const sourceTextureCreationOptions = this._frameGraph.getTextureCreationOptions(this.sourceTexture, true); - sourceTextureCreationOptions.options.generateDepthBuffer = false; - sourceTextureCreationOptions.options.generateStencilBuffer = false; + const sourceTextureCreationOptions = this._frameGraph.textureManager.getTextureCreationOptions(this.sourceTexture); - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this._merge.name, sourceTextureCreationOptions); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this._merge.name, sourceTextureCreationOptions); this._merge.sourceTexture = this.sourceTexture; this._merge.sourceSamplingMode = this.sourceSamplingMode; diff --git a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldMergeTask.ts b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldMergeTask.ts index 2cff0969711..5aab98bdfd9 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldMergeTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldMergeTask.ts @@ -2,6 +2,7 @@ import type { FrameGraph, FrameGraphTextureHandle, FrameGraphRenderPass } from "core/index"; import { ThinDepthOfFieldMergePostProcess } from "core/PostProcesses/thinDepthOfFieldMergePostProcess"; import { FrameGraphPostProcessTask } from "./postProcessTask"; +import { Constants } from "../../../Engines/constants"; /** * @internal @@ -25,6 +26,9 @@ export class FrameGraphDepthOfFieldMergeTask extends FrameGraphPostProcessTask { const pass = super.record(skipCreationOfDisabledPasses, undefined, (context) => { context.bindTextureHandle(this._postProcessDrawWrapper.effect!, "circleOfConfusionSampler", this.circleOfConfusionTexture); this.blurSteps.forEach((handle, index) => { + if (index === this.blurSteps.length - 1) { + context.setTextureSamplingMode(handle, Constants.TEXTURE_BILINEAR_SAMPLINGMODE); + } context.bindTextureHandle(this._postProcessDrawWrapper.effect!, "blurStep" + (this.blurSteps.length - index - 1), handle); }); }); diff --git a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldTask.ts b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldTask.ts index de978c95862..2f58ce10882 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/depthOfFieldTask.ts @@ -59,12 +59,37 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { */ public readonly hdr: boolean; - private _engine: AbstractEngine; - private _circleOfConfusion: FrameGraphCircleOfConfusionTask; - private _blurX: FrameGraphDepthOfFieldBlurTask[] = []; - private _blurY: FrameGraphDepthOfFieldBlurTask[] = []; - private _merge: FrameGraphDepthOfFieldMergeTask; - private _defaultPipelineTextureType: number; + /** + * The name of the task. + */ + public override get name() { + return this._name; + } + + public override set name(name: string) { + this._name = name; + if (this._circleOfConfusion) { + this._circleOfConfusion.name = `${name} Circle of Confusion`; + } + + if (this._blurX) { + for (let i = 0; i < this._blurX.length; i++) { + this._blurX[i].name = `${name} Blur X`; + this._blurY[i].name = `${name} Blur Y`; + } + } + + if (this._merge) { + this._merge.name = `${name} Merge`; + } + } + + private readonly _engine: AbstractEngine; + private readonly _circleOfConfusion: FrameGraphCircleOfConfusionTask; + private readonly _blurX: FrameGraphDepthOfFieldBlurTask[] = []; + private readonly _blurY: FrameGraphDepthOfFieldBlurTask[] = []; + private readonly _merge: FrameGraphDepthOfFieldMergeTask; + private readonly _defaultPipelineTextureType: number; /** * Constructs a depth of field task. @@ -103,7 +128,7 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { this._merge = new FrameGraphDepthOfFieldMergeTask(`${name} Merge`, this._frameGraph, this.depthOfField._dofMerge); - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public override isReady() { @@ -115,7 +140,7 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { throw new Error("FrameGraphDepthOfFieldTask: sourceTexture, depthTexture and camera are required"); } - const sourceTextureDescription = this._frameGraph.getTextureDescription(this.sourceTexture); + const sourceTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.sourceTexture); const textureSize = { width: sourceTextureDescription.size.width, @@ -126,19 +151,16 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { size: textureSize, options: { createMipMaps: false, - generateMipMaps: false, types: [this._defaultPipelineTextureType], formats: [circleOfConfusionTextureFormat], samples: 1, useSRGBBuffers: [false], - generateDepthBuffer: false, - generateStencilBuffer: false, - label: "", + labels: [""], }, sizeIsPercentage: false, }; - const circleOfConfusionTextureHandle = this._frameGraph.createRenderTargetTexture(this._circleOfConfusion.name, textureCreationOptions); + const circleOfConfusionTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._circleOfConfusion.name, textureCreationOptions); this._circleOfConfusion.sourceTexture = this.sourceTexture; // texture not used by the CoC shader this._circleOfConfusion.depthTexture = this.depthTexture; @@ -157,7 +179,9 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { textureSize.width = Math.floor(sourceTextureDescription.size.width * ratio); textureSize.height = Math.floor(sourceTextureDescription.size.height * ratio); - const blurYTextureHandle = this._frameGraph.createRenderTargetTexture(this._blurY[i].name, textureCreationOptions); + textureCreationOptions.options.labels![0] = "step " + (i + 1); + + const blurYTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._blurY[i].name, textureCreationOptions); this._blurY[i].sourceTexture = i === 0 ? this.sourceTexture : this._blurX[i - 1].outputTexture; this._blurY[i].sourceSamplingMode = Constants.TEXTURE_BILINEAR_SAMPLINGMODE; @@ -165,7 +189,7 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { this._blurY[i].destinationTexture = blurYTextureHandle; this._blurY[i].record(true); - const blurXTextureHandle = this._frameGraph.createRenderTargetTexture(this._blurX[i].name, textureCreationOptions); + const blurXTextureHandle = this._frameGraph.textureManager.createRenderTargetTexture(this._blurX[i].name, textureCreationOptions); this._blurX[i].sourceTexture = this._blurY[i].outputTexture; this._blurX[i].sourceSamplingMode = Constants.TEXTURE_BILINEAR_SAMPLINGMODE; @@ -176,11 +200,9 @@ export class FrameGraphDepthOfFieldTask extends FrameGraphTask { blurSteps.push(blurXTextureHandle); } - const sourceTextureCreationOptions = this._frameGraph.getTextureCreationOptions(this.sourceTexture, true); - sourceTextureCreationOptions.options.generateDepthBuffer = false; - sourceTextureCreationOptions.options.generateStencilBuffer = false; + const sourceTextureCreationOptions = this._frameGraph.textureManager.getTextureCreationOptions(this.sourceTexture); - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this._merge.name, sourceTextureCreationOptions); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this._merge.name, sourceTextureCreationOptions); this._merge.sourceTexture = this.sourceTexture; this._merge.sourceSamplingMode = this.sourceSamplingMode; diff --git a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/postProcessTask.ts b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/postProcessTask.ts index 54b2ce34427..9d3e9cc8a89 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/postProcessTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/PostProcesses/postProcessTask.ts @@ -49,7 +49,7 @@ export class FrameGraphPostProcessTask extends FrameGraphTask { this.postProcess = postProcess; this._postProcessDrawWrapper = this.postProcess.drawWrapper; - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public override isReady() { @@ -65,14 +65,12 @@ export class FrameGraphPostProcessTask extends FrameGraphTask { throw new Error(`FrameGraphPostProcessTask "${this.name}": sourceTexture is required`); } - const sourceTextureCreationOptions = this._frameGraph.getTextureCreationOptions(this.sourceTexture, true); - sourceTextureCreationOptions.options.generateDepthBuffer = false; - sourceTextureCreationOptions.options.generateStencilBuffer = false; + const sourceTextureCreationOptions = this._frameGraph.textureManager.getTextureCreationOptions(this.sourceTexture); sourceTextureCreationOptions.options.samples = 1; - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this.name, sourceTextureCreationOptions); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture, this.name, sourceTextureCreationOptions); - const outputTextureDescription = this._frameGraph.getTextureDescription(this.outputTexture); + const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.outputTexture); this._outputWidth = outputTextureDescription.size.width; this._outputHeight = outputTextureDescription.size.height; diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/cullObjectsTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/cullObjectsTask.ts index f9fe44523bb..7bdc3eb5789 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/cullObjectsTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/cullObjectsTask.ts @@ -21,7 +21,7 @@ export class FrameGraphCullObjectsTask extends FrameGraphTask { */ public readonly outputObjectList: FrameGraphObjectList; - private _scene: Scene; + private readonly _scene: Scene; /** * Creates a new cull objects task. diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/geometryRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/geometryRendererTask.ts index 49211ec92ce..f813c89c1c2 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/geometryRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/geometryRendererTask.ts @@ -176,9 +176,9 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { } } - private _engine: AbstractEngine; - private _scene: Scene; - private _renderer: ObjectRenderer; + private readonly _engine: AbstractEngine; + private readonly _scene: Scene; + private readonly _renderer: ObjectRenderer; private _textureWidth: number; private _textureHeight: number; private _clearAttachmentsLayout: Map; @@ -211,17 +211,17 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { this._clearAttachmentsLayout = new Map(); this._allAttachmentsLayout = []; - this.outputDepthTexture = this._frameGraph.createDanglingHandle(); - this.geometryViewDepthTexture = this._frameGraph.createDanglingHandle(); - this.geometryScreenDepthTexture = this._frameGraph.createDanglingHandle(); - this.geometryViewNormalTexture = this._frameGraph.createDanglingHandle(); - this.geometryWorldNormalTexture = this._frameGraph.createDanglingHandle(); - this.geometryLocalPositionTexture = this._frameGraph.createDanglingHandle(); - this.geometryWorldPositionTexture = this._frameGraph.createDanglingHandle(); - this.geometryAlbedoTexture = this._frameGraph.createDanglingHandle(); - this.geometryReflectivityTexture = this._frameGraph.createDanglingHandle(); - this.geometryVelocityTexture = this._frameGraph.createDanglingHandle(); - this.geometryLinearVelocityTexture = this._frameGraph.createDanglingHandle(); + this.outputDepthTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryViewDepthTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryScreenDepthTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryViewNormalTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryWorldNormalTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryLocalPositionTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryWorldPositionTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryAlbedoTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryReflectivityTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryVelocityTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.geometryLinearVelocityTexture = this._frameGraph.textureManager.createDanglingHandle(); } /** @@ -248,7 +248,7 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { this._registerForRenderPassId(this._renderer.renderPassId); - const outputTextureDescription = this._frameGraph.getTextureDescription(outputTextureHandle); + const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(outputTextureHandle[0]); this._textureWidth = outputTextureDescription.size.width; this._textureHeight = outputTextureDescription.size.height; @@ -260,49 +260,47 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { pass.setRenderTarget(outputTextureHandle); - let handle = outputTextureHandle + 1; for (let i = 0; i < this.textureDescriptions.length; i++) { const description = this.textureDescriptions[i]; + const handle = outputTextureHandle[i]; const index = MaterialHelperGeometryRendering.GeometryTextureDescriptions.findIndex((f) => f.type === description.type); const geometryDescription = MaterialHelperGeometryRendering.GeometryTextureDescriptions[index]; switch (geometryDescription.type) { case Constants.PREPASS_DEPTH_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryViewDepthTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryViewDepthTexture, handle); break; case Constants.PREPASS_SCREENSPACE_DEPTH_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryScreenDepthTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryScreenDepthTexture, handle); break; case Constants.PREPASS_NORMAL_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryViewNormalTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryViewNormalTexture, handle); break; case Constants.PREPASS_WORLD_NORMAL_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryWorldNormalTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryWorldNormalTexture, handle); break; case Constants.PREPASS_LOCAL_POSITION_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryLocalPositionTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryLocalPositionTexture, handle); break; case Constants.PREPASS_POSITION_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryWorldPositionTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryWorldPositionTexture, handle); break; case Constants.PREPASS_ALBEDO_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryAlbedoTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryAlbedoTexture, handle); break; case Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryReflectivityTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryReflectivityTexture, handle); break; case Constants.PREPASS_VELOCITY_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryVelocityTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryVelocityTexture, handle); break; case Constants.PREPASS_VELOCITY_LINEAR_TEXTURE_TYPE: - this._frameGraph.resolveDanglingHandle(this.geometryLinearVelocityTexture, handle++); + this._frameGraph.textureManager.resolveDanglingHandle(this.geometryLinearVelocityTexture, handle); break; } } - if (this.depthTexture !== undefined) { - pass.setRenderTargetDepth(this.depthTexture); - } + pass.setRenderTargetDepth(this.depthTexture); pass.setExecuteFunc((context) => { this._renderer.renderList = this.objectList.meshes; @@ -326,7 +324,7 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { super.dispose(); } - private _createMultiRenderTargetTexture(): FrameGraphTextureHandle { + private _createMultiRenderTargetTexture(): FrameGraphTextureHandle[] { const types: number[] = []; const formats: number[] = []; const labels: string[] = []; @@ -346,24 +344,25 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { useSRGBBuffers[i] = false; } - return this._frameGraph.createRenderTargetTexture( - this.name, - { - size: this.size, - sizeIsPercentage: this.sizeIsPercentage, - options: { - createMipMaps: false, - generateDepthBuffer: false, - textureCount: this.textureDescriptions.length, - samples: this.samples, - types, - formats, - useSRGBBuffers, - labels, - }, + const baseHandle = this._frameGraph.textureManager.createRenderTargetTexture(this.name, { + size: this.size, + sizeIsPercentage: this.sizeIsPercentage, + options: { + createMipMaps: false, + samples: this.samples, + types, + formats, + useSRGBBuffers, + labels, }, - true - ); + }); + + const handles: FrameGraphTextureHandle[] = []; + for (let i = 0; i < this.textureDescriptions.length; i++) { + handles.push(baseHandle + i); + } + + return handles; } private _checkDepthTextureCompatibility(): boolean { @@ -374,12 +373,12 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask { throw new Error(`FrameGraphGeometryRendererTask ${this.name}: the depth/stencil back buffer is not allowed as a depth texture`); } - const depthTextureDescription = this._frameGraph.getTextureDescription(this.depthTexture); + const depthTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.depthTexture); if (depthTextureDescription.options.samples !== this.samples) { throw new Error(`FrameGraphGeometryRendererTask ${this.name}: the depth texture and the output texture must have the same number of samples`); } - this._frameGraph.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); depthEnabled = true; } diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts index 0663f149c0b..a2da695b6d1 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts @@ -54,6 +54,8 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { /** * The output texture. + * This texture will point to the same texture than the destinationTexture property if it is set. + * Note, however, that the handle itself will be different! */ public readonly outputTexture: FrameGraphTextureHandle; @@ -64,8 +66,8 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { */ public readonly outputDepthTexture: FrameGraphTextureHandle; - protected _scene: Scene; - protected _renderer: ObjectRenderer; + protected readonly _scene: Scene; + protected readonly _renderer: ObjectRenderer; protected _textureWidth: number; protected _textureHeight: number; @@ -107,8 +109,8 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { } }); - this.outputTexture = this._frameGraph.createDanglingHandle(); - this.outputDepthTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.outputDepthTexture = this._frameGraph.textureManager.createDanglingHandle(); } public override isReady() { @@ -120,7 +122,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { throw new Error(`FrameGraphObjectRendererTask ${this.name}: destinationTexture and objectList are required`); } - const outputTextureDescription = this._frameGraph.getTextureDescription(this.destinationTexture); + const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.destinationTexture); let depthEnabled = false; @@ -136,7 +138,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { ); } - const depthTextureDescription = this._frameGraph.getTextureDescription(this.depthTexture); + const depthTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.depthTexture); if (depthTextureDescription.options.samples !== outputTextureDescription.options.samples) { throw new Error(`FrameGraphObjectRendererTask ${this.name}: the depth texture and the output texture must have the same number of samples`); } @@ -144,9 +146,9 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { depthEnabled = true; } - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); if (this.depthTexture !== undefined) { - this._frameGraph.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); } this._textureWidth = outputTextureDescription.size.width; @@ -155,9 +157,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { const pass = this._frameGraph.addRenderPass(this.name); pass.setRenderTarget(this.destinationTexture); - if (this.depthTexture !== undefined) { - pass.setRenderTargetDepth(this.depthTexture); - } + pass.setRenderTargetDepth(this.depthTexture); pass.setExecuteFunc((context) => { this._renderer.renderList = this.objectList.meshes; this._renderer.particleSystemList = this.objectList.particleSystems; @@ -178,9 +178,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { const passDisabled = this._frameGraph.addRenderPass(this.name + "_disabled", true); passDisabled.setRenderTarget(this.destinationTexture); - if (this.depthTexture !== undefined) { - passDisabled.setRenderTargetDepth(this.depthTexture); - } + passDisabled.setRenderTargetDepth(this.depthTexture); passDisabled.setExecuteFunc((_context) => {}); if (this.dependencies !== undefined) { diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts index d479fcd0987..0cf4daa2de8 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { FrameGraph, Scene, DrawWrapper, FrameGraphTextureCreationOptions, ObjectRendererOptions } from "core/index"; +import type { FrameGraph, Scene, DrawWrapper, FrameGraphTextureCreationOptions, ObjectRendererOptions, FrameGraphRenderTarget } from "core/index"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../frameGraphTypes"; import { FrameGraphObjectRendererTask } from "./objectRendererTask"; import { ThinTAAPostProcess } from "core/PostProcesses/thinTAAPostProcess"; @@ -39,12 +39,12 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas throw new Error(`FrameGraphTAAObjectRendererTask ${this.name}: the back buffer color/depth textures are not allowed. Use regular textures instead.`); } - const outputTextureDescription = this._frameGraph.getTextureDescription(this.destinationTexture); + const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.destinationTexture); let depthEnabled = false; if (this.depthTexture !== undefined) { - const depthTextureDescription = this._frameGraph.getTextureDescription(this.depthTexture); + const depthTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.depthTexture); if (depthTextureDescription.options.samples !== outputTextureDescription.options.samples) { throw new Error(`FrameGraphTAAObjectRendererTask ${this.name}: the depth texture and the output texture must have the same number of samples`); } @@ -59,26 +59,23 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas const textureCreationOptions: FrameGraphTextureCreationOptions = { size: outputTextureDescription.size, options: { - createMipMaps: false, - generateMipMaps: false, + createMipMaps: outputTextureDescription.options.createMipMaps, types: [Constants.TEXTURETYPE_HALF_FLOAT], - samplingModes: [Constants.TEXTURE_NEAREST_NEAREST], formats: [Constants.TEXTUREFORMAT_RGBA], samples: 1, useSRGBBuffers: [false], - generateDepthBuffer: false, - generateStencilBuffer: false, - label: "", + creationFlags: [0], + labels: [""], }, sizeIsPercentage: false, isHistoryTexture: true, }; - const pingPongHandle = this._frameGraph.createRenderTargetTexture(`${this.name} history`, textureCreationOptions); + const pingPongHandle = this._frameGraph.textureManager.createRenderTargetTexture(`${this.name} history`, textureCreationOptions); - this._frameGraph.resolveDanglingHandle(this.outputTexture, pingPongHandle); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, pingPongHandle); if (this.depthTexture !== undefined) { - this._frameGraph.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); } this._textureWidth = outputTextureDescription.size.width; @@ -86,11 +83,10 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas const pass = this._frameGraph.addRenderPass(this.name); - pass.setRenderTarget(this.destinationTexture); - if (this.depthTexture !== undefined) { - pass.setRenderTargetDepth(this.depthTexture); - } + let pingPongRenderTargetWrapper: FrameGraphRenderTarget | undefined; + pass.setRenderTarget(this.destinationTexture); + pass.setRenderTargetDepth(this.depthTexture); pass.setExecuteFunc((context) => { this._renderer.renderList = this.objectList.meshes; this._renderer.particleSystemList = this.objectList.particleSystems; @@ -110,7 +106,9 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas this._scene.activeCamera = null; - context.bindRenderTarget(pingPongHandle, "frame graph - TAA merge with history texture"); + pingPongRenderTargetWrapper = pingPongRenderTargetWrapper || context.createRenderTarget(`${this.name} ping/pong`, pingPongHandle); + + context.bindRenderTarget(pingPongRenderTargetWrapper, "frame graph - TAA merge with history texture"); if (!this.postProcess.disabled) { context.applyFullScreenEffect(this._postProcessDrawWrapper, () => { @@ -126,9 +124,7 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas const passDisabled = this._frameGraph.addRenderPass(this.name + "_disabled", true); passDisabled.setRenderTarget(this.outputTexture); - if (this.depthTexture !== undefined) { - passDisabled.setRenderTargetDepth(this.depthTexture); - } + passDisabled.setRenderTargetDepth(this.depthTexture); passDisabled.setExecuteFunc((context) => { context.copyTexture(this.destinationTexture); }); diff --git a/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts index 45cb4c97519..a52f92e5ded 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts @@ -30,29 +30,51 @@ export class FrameGraphClearTextureTask extends FrameGraphTask { /** * The texture to clear. */ - public destinationTexture: FrameGraphTextureHandle; + public destinationTexture?: FrameGraphTextureHandle; /** - * The output texture (same as destinationTexture, but the handle may be different). + * The depth attachment texture to clear. + */ + public depthTexture?: FrameGraphTextureHandle; + + /** + * The output texture (same as destinationTexture, but the handle will be different). */ public readonly outputTexture: FrameGraphTextureHandle; + /** + * The output depth texture (same as depthTexture, but the handle will be different). + */ + public readonly outputDepthTexture: FrameGraphTextureHandle; + + /** + * Constructs a new clear task. + * @param name The name of the task. + * @param frameGraph The frame graph the task belongs to. + */ constructor(name: string, frameGraph: FrameGraph) { super(name, frameGraph); - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); + this.outputDepthTexture = this._frameGraph.textureManager.createDanglingHandle(); } public record() { - if (this.destinationTexture === undefined) { - throw new Error(`FrameGraphClearTextureTask ${this.name}: destinationTexture is required`); + if (this.destinationTexture === undefined && this.depthTexture === undefined) { + throw new Error(`FrameGraphClearTextureTask ${this.name}: destinationTexture and depthTexture can't both be undefined.`); } - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + if (this.destinationTexture !== undefined) { + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + } + if (this.depthTexture !== undefined) { + this._frameGraph.textureManager.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); + } const pass = this._frameGraph.addRenderPass(this.name); pass.setRenderTarget(this.destinationTexture); + pass.setRenderTargetDepth(this.depthTexture); pass.setExecuteFunc((context) => { context.clear(this.color, !!this.clearColor, !!this.clearDepth, !!this.clearStencil); }); @@ -60,6 +82,7 @@ export class FrameGraphClearTextureTask extends FrameGraphTask { const passDisabled = this._frameGraph.addRenderPass(this.name + "_disabled", true); passDisabled.setRenderTarget(this.destinationTexture); + passDisabled.setRenderTargetDepth(this.depthTexture); passDisabled.setExecuteFunc((_context) => {}); } } diff --git a/packages/dev/core/src/FrameGraph/Tasks/Texture/copyToTextureTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Texture/copyToTextureTask.ts index eec5fe64ecc..6f673b9b49b 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Texture/copyToTextureTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Texture/copyToTextureTask.ts @@ -29,7 +29,7 @@ export class FrameGraphCopyToTextureTask extends FrameGraphTask { constructor(name: string, frameGraph: FrameGraph) { super(name, frameGraph); - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public record() { @@ -37,7 +37,7 @@ export class FrameGraphCopyToTextureTask extends FrameGraphTask { throw new Error(`FrameGraphCopyToTextureTask "${this.name}": sourceTexture and destinationTexture are required`); } - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); const pass = this._frameGraph.addRenderPass(this.name); diff --git a/packages/dev/core/src/FrameGraph/Tasks/Texture/generateMipMapsTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Texture/generateMipMapsTask.ts index f28a0e39955..a51d0d4d7a9 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Texture/generateMipMapsTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Texture/generateMipMapsTask.ts @@ -24,7 +24,7 @@ export class FrameGraphGenerateMipMapsTask extends FrameGraphTask { constructor(name: string, frameGraph: FrameGraph) { super(name, frameGraph); - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public record() { @@ -32,9 +32,9 @@ export class FrameGraphGenerateMipMapsTask extends FrameGraphTask { throw new Error(`FrameGraphGenerateMipMapsTask ${this.name}: destinationTexture is required`); } - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); - const outputTextureDescription = this._frameGraph.getTextureDescription(this.destinationTexture); + const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.destinationTexture); if (!outputTextureDescription.options.createMipMaps) { throw new Error(`FrameGraphGenerateMipMapsTask ${this.name}: destinationTexture must have createMipMaps set to true`); diff --git a/packages/dev/core/src/FrameGraph/frameGraph.ts b/packages/dev/core/src/FrameGraph/frameGraph.ts index 40bb84a387c..692245d0937 100644 --- a/packages/dev/core/src/FrameGraph/frameGraph.ts +++ b/packages/dev/core/src/FrameGraph/frameGraph.ts @@ -1,14 +1,5 @@ /* eslint-disable import/no-internal-modules */ -import type { - Scene, - AbstractEngine, - RenderTargetWrapper, - FrameGraphTextureCreationOptions, - FrameGraphTextureHandle, - FrameGraphTextureDescription, - Nullable, - FrameGraphTask, -} from "core/index"; +import type { Scene, AbstractEngine, FrameGraphTask } from "core/index"; import { FrameGraphPass } from "./Passes/pass"; import { FrameGraphRenderPass } from "./Passes/renderPass"; import { FrameGraphCullPass } from "./Passes/cullPass"; @@ -16,7 +7,6 @@ import { FrameGraphRenderContext } from "./frameGraphRenderContext"; import { FrameGraphContext } from "./frameGraphContext"; import { FrameGraphTextureManager } from "./frameGraphTextureManager"; import { Observable } from "core/Misc/observable"; -import { getDimensionsFromTextureSize, textureSizeIsObject } from "../Materials/Textures/textureCreationOptions"; enum FrameGraphPassType { Render = 0, @@ -29,14 +19,15 @@ enum FrameGraphPassType { * @experimental */ export class FrameGraph { - /** @internal */ - public readonly _passContext: FrameGraphContext; - /** @internal */ - public readonly _renderContext: FrameGraphRenderContext; + /** + * Gets the texture manager used by the frame graph + */ + public readonly textureManager: FrameGraphTextureManager; private readonly _engine: AbstractEngine; - private readonly _textureManager: FrameGraphTextureManager; - private _tasks: FrameGraphTask[] = []; + private readonly _tasks: FrameGraphTask[] = []; + private readonly _passContext: FrameGraphContext; + private readonly _renderContext: FrameGraphRenderContext; private _currentProcessedTask: FrameGraphTask | null = null; /** @@ -55,13 +46,13 @@ export class FrameGraph { * Constructs the frame graph * @param engine defines the hosting engine * @param debugTextures defines a boolean indicating that textures created by the frame graph should be visible in the inspector - * @param scene defines the scene in which debugging textures are to be created + * @param scene defines the scene the frame graph is associated with */ - constructor(engine: AbstractEngine, debugTextures = false, scene?: Scene) { + constructor(engine: AbstractEngine, debugTextures = false, scene: Scene) { this._engine = engine; - this._textureManager = new FrameGraphTextureManager(this._engine, debugTextures, scene); + this.textureManager = new FrameGraphTextureManager(this._engine, debugTextures, scene); this._passContext = new FrameGraphContext(); - this._renderContext = new FrameGraphRenderContext(this._engine, this._textureManager); + this._renderContext = new FrameGraphRenderContext(this._engine, this.textureManager, scene); } /** @@ -134,19 +125,21 @@ export class FrameGraph { * This method should be called after all tasks have been added to the frame graph (FrameGraph.addTask) and before the graph is executed (FrameGraph.execute). */ public build(): void { - this._textureManager.releaseTextures(false); + this.textureManager._releaseTextures(false); for (const task of this._tasks) { task._reset(); this._currentProcessedTask = task; + this.textureManager._isRecordingTask = true; task.record(); + this.textureManager._isRecordingTask = false; this._currentProcessedTask = null; } - this._textureManager.allocateTextures(); + this.textureManager._allocateTextures(); for (const task of this._tasks) { task._checkTask(); @@ -164,9 +157,9 @@ export class FrameGraph { public whenReadyAsync(timeout = 16): Promise { return new Promise((resolve) => { const checkReady = () => { - let ready = true; + let ready = this._renderContext._isReady(); for (const task of this._tasks) { - ready &&= task.isReady(); + ready = task.isReady() && ready; } if (ready) { resolve(); @@ -185,7 +178,7 @@ export class FrameGraph { public execute(): void { this._renderContext.bindRenderTarget(); - this._textureManager.updateHistoryTextures(); + this.textureManager._updateHistoryTextures(); for (const task of this._tasks) { const passes = task._getPasses(); @@ -196,141 +189,6 @@ export class FrameGraph { } } - /** - * Imports a texture into the frame graph - * @param name Name of the texture - * @param texture Texture to import - * @param handle Existing handle to use for the texture. If not provided (default), a new handle will be created. - * @returns The handle to the texture - */ - public importTexture(name: string, texture: RenderTargetWrapper, handle?: FrameGraphTextureHandle): FrameGraphTextureHandle { - return this._textureManager.importTexture(name, texture, handle); - } - - /** - * Gets the creation options of a texture - * @param handle Handle of the texture - * @param cloneOptions If true, the options will be cloned before being returned (default is false) - * @returns The creation options of the texture - */ - public getTextureCreationOptions(handle: FrameGraphTextureHandle, cloneOptions = false): FrameGraphTextureCreationOptions { - const creationOptions = this._textureManager.getTextureCreationOptions(handle); - - return cloneOptions - ? { - size: getDimensionsFromTextureSize(creationOptions.size), - options: { ...creationOptions.options }, - sizeIsPercentage: creationOptions.sizeIsPercentage, - } - : creationOptions; - } - - /** - * Gets the description of a texture - * @param handle Handle of the texture - * @returns The description of the texture - */ - public getTextureDescription(handle: FrameGraphTextureHandle): FrameGraphTextureDescription { - const creationOptions = this.getTextureCreationOptions(handle); - - const size = !creationOptions.sizeIsPercentage - ? textureSizeIsObject(creationOptions.size) - ? { width: creationOptions.size.width, height: creationOptions.size.height } - : { width: creationOptions.size, height: creationOptions.size } - : this._textureManager.getAbsoluteDimensions(creationOptions.size); - - return { - size, - options: { ...creationOptions.options }, - }; - } - - /** - * Gets a texture handle or creates a new texture if the handle is not provided. - * @param handle If provided, will simply return the handle - * @param newTextureName Name of the new texture to create - * @param creationOptions Options to use when creating the new texture - * @returns The handle to the texture. If handle is not provided, newTextureName and creationOptions must be provided. - */ - public getTextureHandleOrCreateTexture(handle?: FrameGraphTextureHandle, newTextureName?: string, creationOptions?: FrameGraphTextureCreationOptions): FrameGraphTextureHandle { - if (handle === undefined) { - if (newTextureName === undefined || creationOptions === undefined) { - throw new Error("getTextureHandleOrCreateTexture: Either handle or newTextureName and creationOptions must be provided."); - } - return this.createRenderTargetTexture(newTextureName, creationOptions); - } - return handle; - } - - /** - * Gets a texture from a handle - * @param handle The handle of the texture - * @returns The texture or null if not found - */ - public getTexture(handle: FrameGraphTextureHandle): Nullable { - return this._textureManager.getTextureFromHandle(handle); - } - - /** - * Creates a new render target texture - * @param name Name of the texture - * @param creationOptions Options to use when creating the texture - * @param multiTargetMode If true, the texture will be created in multi target mode (default is false). In this mode, a handle is created for each target separately, in addition to the handle created for the main render target texture itself. - * @returns The handle to the texture - */ - public createRenderTargetTexture(name: string, creationOptions: FrameGraphTextureCreationOptions, multiTargetMode = false): FrameGraphTextureHandle { - return this._textureManager.createRenderTargetTexture(name, !!this._currentProcessedTask, creationOptions, multiTargetMode); - } - - /** - * Creates a handle which is not associated with any texture. - * Call resolveDanglingHandle to associate the handle with a valid texture handle. - * @returns The dangling handle - */ - public createDanglingHandle(): FrameGraphTextureHandle { - return this._textureManager.createDanglingHandle(); - } - - /** - * Associates a texture with a dangling handle - * @param danglingHandle The dangling handle - * @param handle The handle to associate with the dangling handle (if not provided, a new texture handle will be created) - * @param newTextureName The name of the new texture to create (if handle is not provided) - * @param creationOptions The options to use when creating the new texture (if handle is not provided) - */ - public resolveDanglingHandle( - danglingHandle: FrameGraphTextureHandle, - handle?: FrameGraphTextureHandle, - newTextureName?: string, - creationOptions?: FrameGraphTextureCreationOptions - ) { - if (handle === undefined) { - if (newTextureName === undefined || creationOptions === undefined) { - throw new Error("resolveDanglingHandle: Either handle or newTextureName and creationOptions must be provided."); - } - this._textureManager.createRenderTargetTexture(newTextureName, !!this._currentProcessedTask, creationOptions, false, danglingHandle); - return; - } - - this._textureManager.resolveDanglingHandle(danglingHandle, handle); - } - - /** - * Checks if a handle is a history texture (or points to a history texture, for a dangling handle) - * @param handle The handle to check - * @returns True if the handle is a history texture, otherwise false - */ - public isHistoryTexture(handle: FrameGraphTextureHandle): boolean { - const entry = this._textureManager._textures.get(handle); - if (!entry) { - return false; - } - - handle = entry.refHandle ?? handle; - - return this._textureManager._historyTextures.has(handle); - } - /** * Clears the frame graph (remove the tasks and release the textures). * The frame graph can be built again after this method is called. @@ -341,7 +199,7 @@ export class FrameGraph { } this._tasks.length = 0; - this._textureManager.releaseTextures(); + this.textureManager._releaseTextures(); this._currentProcessedTask = null; } @@ -350,7 +208,7 @@ export class FrameGraph { */ public dispose(): void { this.clear(); - this._textureManager.dispose(); + this.textureManager._dispose(); this._renderContext._dispose(); } } diff --git a/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts b/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts index 131cd7f5192..6dcae8679c8 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphRenderContext.ts @@ -9,13 +9,14 @@ import type { FrameGraphTextureManager, ObjectRenderer, Scene, + FrameGraphRenderTarget, + InternalTexture, // eslint-disable-next-line import/no-internal-modules } from "core/index"; import { Constants } from "../Engines/constants"; import { EffectRenderer } from "../Materials/effectRenderer"; import { CopyTextureToTexture } from "../Misc/copyTextureToTexture"; import { FrameGraphContext } from "./frameGraphContext"; -import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "./frameGraphTypes"; /** * Frame graph context used render passes. @@ -23,7 +24,7 @@ import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } fro */ export class FrameGraphRenderContext extends FrameGraphContext { private readonly _effectRenderer: EffectRenderer; - private _currentRenderTargetHandle: FrameGraphTextureHandle; + private _currentRenderTarget: FrameGraphRenderTarget | undefined; private _debugMessageWhenTargetBound: string | undefined; private _debugMessageHasBeenPushed = false; private _renderTargetIsBound = true; @@ -37,12 +38,11 @@ export class FrameGraphRenderContext extends FrameGraphContext { constructor( private readonly _engine: AbstractEngine, private readonly _textureManager: FrameGraphTextureManager, - private readonly _scene?: Scene + private readonly _scene: Scene ) { super(); this._effectRenderer = new EffectRenderer(this._engine); this._copyTexture = new CopyTextureToTexture(this._engine); - this._currentRenderTargetHandle = backbufferColorTextureHandle; } /** @@ -72,6 +72,22 @@ export class FrameGraphRenderContext extends FrameGraphContext { return this._textureManager.isBackbufferDepthStencil(handle); } + /** + * Creates a (frame graph) render target wrapper + * Note that renderTargets or renderTargetDepth can be undefined, but not both at the same time! + * @param name Name of the render target wrapper + * @param renderTargets Render target handles (textures) to use + * @param renderTargetDepth Render target depth handle (texture) to use + * @returns The created render target wrapper + */ + public createRenderTarget( + name: string, + renderTargets?: FrameGraphTextureHandle | FrameGraphTextureHandle[], + renderTargetDepth?: FrameGraphTextureHandle + ): FrameGraphRenderTarget { + return this._textureManager.createRenderTarget(name, renderTargets, renderTargetDepth); + } + /** * Clears the current render buffer or the current render target (if any is set up) * @param color Defines the color to use @@ -108,18 +124,23 @@ export class FrameGraphRenderContext extends FrameGraphContext { * Generates mipmaps for the current render target */ public generateMipMaps(): void { - const texture = this._textureManager.getTextureFromHandle(this._currentRenderTargetHandle); - if (!texture) { - // Texture is backbuffer, no need to generate mipmaps + if (this._currentRenderTarget?.renderTargetWrapper === undefined) { return; } - if (this._renderTargetIsBound) { - // we can't generate the mipmaps if the texture is bound as a render target + + if (this._renderTargetIsBound && this._engine._currentRenderTarget) { + // we can't generate the mipmaps if the render target is bound this._flushDebugMessages(); - this._engine.unBindFramebuffer(texture); + this._engine.unBindFramebuffer(this._engine._currentRenderTarget); this._renderTargetIsBound = false; } - this._engine.generateMipmaps(texture.texture!); + + const textures = this._currentRenderTarget.renderTargetWrapper.textures; + if (textures) { + for (const texture of textures) { + this._engine.generateMipmaps(texture); + } + } } /** @@ -128,7 +149,7 @@ export class FrameGraphRenderContext extends FrameGraphContext { * @param samplingMode Sampling mode to set */ public setTextureSamplingMode(handle: FrameGraphTextureHandle, samplingMode: number): void { - const internalTexture = this._textureManager.getTextureFromHandle(handle)?.texture!; + const internalTexture = this._textureManager.getTextureFromHandle(handle); if (internalTexture && internalTexture.samplingMode !== samplingMode) { this._engine.updateTextureSamplingMode(samplingMode, internalTexture); } @@ -141,10 +162,24 @@ export class FrameGraphRenderContext extends FrameGraphContext { * @param handle The handle of the texture to bind */ public bindTextureHandle(effect: Effect, name: string, handle: FrameGraphTextureHandle): void { - const texture = this._textureManager.getTextureFromHandle(handle); - if (texture) { - effect._bindTexture(name, texture.texture!); + let texture: Nullable; + + const historyEntry = this._textureManager._historyTextures.get(handle); + if (historyEntry) { + texture = historyEntry.textures[historyEntry.index]; // texture we write to in this frame + if ( + this._currentRenderTarget !== undefined && + this._currentRenderTarget.renderTargetWrapper !== undefined && + this._currentRenderTarget.renderTargetWrapper.textures!.includes(texture!) + ) { + // If the current render target renders to the history write texture, we bind the read texture instead + texture = historyEntry.textures[historyEntry.index ^ 1]; + } + } else { + texture = this._textureManager._textures.get(handle)!.texture; } + + effect._bindTexture(name, texture); } /** @@ -200,7 +235,7 @@ export class FrameGraphRenderContext extends FrameGraphContext { this.bindRenderTarget(); } this._applyRenderTarget(); - this._copyTexture.copy(this._textureManager.getTextureFromHandle(sourceTexture)!.texture!); + this._copyTexture.copy(this._textureManager.getTextureFromHandle(sourceTexture)!); } /** @@ -212,8 +247,8 @@ export class FrameGraphRenderContext extends FrameGraphContext { public render(object: Layer | ObjectRenderer, viewportWidth?: number, viewportHeight?: number): void { if (FrameGraphRenderContext._IsObjectRenderer(object)) { if (object.shouldRender()) { - this._scene?.incrementRenderId(); - this._scene?.resetCachedMaterial(); + this._scene.incrementRenderId(); + this._scene.resetCachedMaterial(); object.prepareRenderList(); object.initRender(viewportWidth!, viewportHeight!); @@ -233,11 +268,14 @@ export class FrameGraphRenderContext extends FrameGraphContext { * Binds a render target texture so that upcoming draw calls will render to it * Note: it is a lazy operation, so the render target will only be bound when needed. This way, it is possible to call * this method several times with different render targets without incurring the cost of binding if no draw calls are made - * @param renderTargetHandle The handle of the render target texture to bind (default: backbufferColorTextureHandle) + * @param renderTarget The handle of the render target texture to bind (default: undefined, meaning "back buffer"). Pass an array for MRT rendering. * @param debugMessage Optional debug message to display when the render target is bound (visible in PIX, for example) */ - public bindRenderTarget(renderTargetHandle: FrameGraphTextureHandle = backbufferColorTextureHandle, debugMessage?: string) { - if (renderTargetHandle === this._currentRenderTargetHandle) { + public bindRenderTarget(renderTarget?: FrameGraphRenderTarget, debugMessage?: string) { + if ( + (renderTarget?.renderTargetWrapper === undefined && this._currentRenderTarget === undefined) || + (renderTarget && this._currentRenderTarget && renderTarget.equals(this._currentRenderTarget)) + ) { this._flushDebugMessages(); if (debugMessage !== undefined) { this._engine._debugPushGroup?.(debugMessage, 2); @@ -246,7 +284,7 @@ export class FrameGraphRenderContext extends FrameGraphContext { } return; } - this._currentRenderTargetHandle = renderTargetHandle; + this._currentRenderTarget = renderTarget?.renderTargetWrapper === undefined ? undefined : renderTarget; this._debugMessageWhenTargetBound = debugMessage; this._renderTargetIsBound = false; } @@ -259,41 +297,22 @@ export class FrameGraphRenderContext extends FrameGraphContext { } } - /** @internal */ - public _shareDepth(srcRenderTargetHandle: FrameGraphTextureHandle, dstRenderTargetHandle: FrameGraphTextureHandle) { - const srcTexture = this._textureManager.getTextureFromHandle(srcRenderTargetHandle); - const dstTexture = this._textureManager.getTextureFromHandle(dstRenderTargetHandle); - - if (srcTexture && dstTexture) { - srcTexture.shareDepth(dstTexture); - } - } - private _applyRenderTarget() { if (this._renderTargetIsBound) { return; } - const handle = this._currentRenderTargetHandle; - const textureSlot = this._textureManager._textures.get(handle)!; - - let renderTarget = textureSlot.texture; - - if (textureSlot.creationOptions.isHistoryTexture) { - const historyEntry = this._textureManager._historyTextures.get(textureSlot.refHandle ?? handle)!; - renderTarget = historyEntry.textures[historyEntry.index]; - } - this._flushDebugMessages(); - if (!renderTarget) { - if (handle === backbufferColorTextureHandle || textureSlot.refHandle === backbufferColorTextureHandle) { - this._engine.restoreDefaultFramebuffer(); - } else if (handle === backbufferDepthStencilTextureHandle || textureSlot.refHandle === backbufferDepthStencilTextureHandle) { - this._engine.restoreDefaultFramebuffer(); - } + const renderTargetWrapper = this._currentRenderTarget?.renderTargetWrapper; + + if (renderTargetWrapper === undefined) { + this._engine.restoreDefaultFramebuffer(); } else { - this._engine.bindFramebuffer(renderTarget); + if (this._engine._currentRenderTarget) { + this._engine.unBindFramebuffer(this._engine._currentRenderTarget); + } + this._engine.bindFramebuffer(renderTargetWrapper); } if (this._debugMessageWhenTargetBound !== undefined) { @@ -305,6 +324,11 @@ export class FrameGraphRenderContext extends FrameGraphContext { this._renderTargetIsBound = true; } + /** @internal */ + public _isReady(): boolean { + return this._copyTexture.isReady(); + } + /** @internal */ public _dispose() { this._effectRenderer.dispose(); diff --git a/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts b/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts new file mode 100644 index 00000000000..b39d2fb4928 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts @@ -0,0 +1,98 @@ +// eslint-disable-next-line import/no-internal-modules +import type { FrameGraphTextureHandle, FrameGraphTextureManager, IMultiRenderTargetOptions, RenderTargetWrapper } from "core/index"; + +/** + * @internal + * @experimental + */ +export class FrameGraphRenderTarget { + protected _textureManager: FrameGraphTextureManager; + protected _renderTargets: FrameGraphTextureHandle[] | undefined; + protected _renderTargetDepth: FrameGraphTextureHandle | undefined; + protected _renderTargetWrapper: RenderTargetWrapper | undefined; + protected _isBackBuffer = false; + + public readonly name: string; + + constructor( + name: string, + textureManager: FrameGraphTextureManager, + renderTargets?: FrameGraphTextureHandle | FrameGraphTextureHandle[], + renderTargetDepth?: FrameGraphTextureHandle + ) { + this.name = name; + this._textureManager = textureManager; + this._renderTargets = renderTargets === undefined ? undefined : Array.isArray(renderTargets) ? renderTargets : [renderTargets]; + this._renderTargetDepth = renderTargetDepth; + } + + public get renderTargetWrapper() { + if (this._isBackBuffer) { + return undefined; + } + + if (!this._renderTargetWrapper) { + const engine = this._textureManager.engine; + + // _renderTargets and _renderTargetDepth cannot both be undefined + const textureHandle = this._renderTargets === undefined ? this._renderTargetDepth! : this._renderTargets[0]; + + if (this._textureManager.isBackbuffer(textureHandle)) { + this._isBackBuffer = true; + return undefined; + } + + const textureDescription = this._textureManager.getTextureDescription(textureHandle); + + const creationOptionsForTexture: IMultiRenderTargetOptions = { + textureCount: this._renderTargets?.length ?? 0, + generateDepthBuffer: false, + label: this.name, + samples: textureDescription.options.samples ?? 1, + dontCreateTextures: true, + }; + + this._renderTargetWrapper = engine.createMultipleRenderTarget(textureDescription.size, creationOptionsForTexture, true); + + for (let i = 0; i < creationOptionsForTexture.textureCount!; i++) { + const handle = this._renderTargets![i]; + const texture = this._textureManager.getTextureFromHandle(handle); + + if (!texture) { + throw new Error( + `FrameGraphRenderTarget.renderTargetWrapper: Failed to get texture from handle. handle: ${handle}, name: ${this.name}, index: ${i}, renderTargets: ${this._renderTargets}` + ); + } + + this._renderTargetWrapper.setTexture(texture, i, false); + } + + if (this._renderTargetDepth !== undefined) { + this._renderTargetWrapper.depthStencilTexture = this._textureManager.getTextureFromHandle(this._renderTargetDepth); + } + } + + return this._renderTargetWrapper; + } + + public equals(other: FrameGraphRenderTarget): boolean { + const src = this._renderTargets; + const dst = other._renderTargets; + + if (src !== undefined && dst !== undefined) { + if (src.length !== dst.length) { + return false; + } + + for (let i = 0; i < src.length; i++) { + if (src[i] !== dst[i]) { + return false; + } + } + } else if ((src === undefined && dst !== undefined) || (src !== undefined && dst === undefined)) { + return false; + } + + return this._renderTargetDepth === other._renderTargetDepth; + } +} diff --git a/packages/dev/core/src/FrameGraph/frameGraphTask.ts b/packages/dev/core/src/FrameGraph/frameGraphTask.ts index 0f1ed969eba..d9b728b9ffc 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTask.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTask.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { RenderTargetWrapper, FrameGraph, FrameGraphObjectList, IFrameGraphPass, Nullable, FrameGraphTextureHandle } from "core/index"; +import type { FrameGraph, FrameGraphObjectList, IFrameGraphPass, Nullable, FrameGraphTextureHandle, InternalTexture } from "core/index"; import { FrameGraphCullPass } from "./Passes/cullPass"; import { FrameGraphRenderPass } from "./Passes/renderPass"; @@ -8,7 +8,7 @@ import { FrameGraphRenderPass } from "./Passes/renderPass"; * @experimental */ export abstract class FrameGraphTask { - protected _frameGraph: FrameGraph; + protected readonly _frameGraph: FrameGraph; private _passes: IFrameGraphPass[] = []; private _passesDisabled: IFrameGraphPass[] = []; @@ -89,8 +89,8 @@ export abstract class FrameGraphTask { /** @internal */ public _checkTask() { - let outputTexture: Nullable = null; - let outputDepthTexture: Nullable = null; + let outputTexture: Nullable[]> = null; + let outputDepthTexture: Nullable = null; let outputObjectList: FrameGraphObjectList | undefined; for (const pass of this._passes!) { @@ -99,16 +99,22 @@ export abstract class FrameGraphTask { throw new Error(`Pass "${pass.name}" is not valid. ${errMsg}`); } if (FrameGraphRenderPass.IsRenderPass(pass)) { - outputTexture = this._frameGraph.getTexture(pass.renderTarget); - outputDepthTexture = pass.renderTargetDepth !== undefined ? this._frameGraph.getTexture(pass.renderTargetDepth) : null; + const handles = Array.isArray(pass.renderTarget) ? pass.renderTarget : [pass.renderTarget]; + outputTexture = []; + for (const handle of handles) { + if (handle !== undefined) { + outputTexture.push(this._frameGraph.textureManager.getTextureFromHandle(handle)); + } + } + outputDepthTexture = pass.renderTargetDepth !== undefined ? this._frameGraph.textureManager.getTextureFromHandle(pass.renderTargetDepth) : null; } else if (FrameGraphCullPass.IsCullPass(pass)) { outputObjectList = pass.objectList; } } - let disabledOutputTexture: Nullable = null; - let disabledOutputTextureHandle: FrameGraphTextureHandle = -1; - let disabledOutputDepthTexture: Nullable = null; + let disabledOutputTexture: Nullable[]> = null; + let disabledOutputTextureHandle: (FrameGraphTextureHandle | undefined)[] = []; + let disabledOutputDepthTexture: Nullable = null; let disabledOutputObjectList: FrameGraphObjectList | undefined; for (const pass of this._passesDisabled!) { @@ -117,17 +123,30 @@ export abstract class FrameGraphTask { throw new Error(`Pass "${pass.name}" is not valid. ${errMsg}`); } if (FrameGraphRenderPass.IsRenderPass(pass)) { - disabledOutputTexture = this._frameGraph.getTexture(pass.renderTarget); - disabledOutputTextureHandle = pass.renderTarget; - disabledOutputDepthTexture = pass.renderTargetDepth !== undefined ? this._frameGraph.getTexture(pass.renderTargetDepth) : null; + const handles = Array.isArray(pass.renderTarget) ? pass.renderTarget : [pass.renderTarget]; + disabledOutputTexture = []; + for (const handle of handles) { + if (handle !== undefined) { + disabledOutputTexture.push(this._frameGraph.textureManager.getTextureFromHandle(handle)); + } + } + disabledOutputTextureHandle = handles; + disabledOutputDepthTexture = pass.renderTargetDepth !== undefined ? this._frameGraph.textureManager.getTextureFromHandle(pass.renderTargetDepth) : null; } else if (FrameGraphCullPass.IsCullPass(pass)) { disabledOutputObjectList = pass.objectList; } } if (this._passesDisabled.length > 0) { - if (outputTexture !== disabledOutputTexture) { - if (!this._frameGraph.isHistoryTexture(disabledOutputTextureHandle)) { + if (!this._checkSameRenderTarget(outputTexture, disabledOutputTexture)) { + let ok = true; + for (const handle of disabledOutputTextureHandle) { + if (handle !== undefined && !this._frameGraph.textureManager.isHistoryTexture(handle)) { + ok = false; + break; + } + } + if (!ok) { throw new Error(`The output texture of the task "${this.name}" is different when it is enabled or disabled.`); } } @@ -144,4 +163,22 @@ export abstract class FrameGraphTask { public _getPasses(): IFrameGraphPass[] { return this.disabled && this._passesDisabled.length > 0 ? this._passesDisabled : this._passes; } + + private _checkSameRenderTarget(src: Nullable[]>, dst: Nullable[]>) { + if (src === null || dst === null) { + return src === dst; + } + + if (src.length !== dst.length) { + return false; + } + + for (let i = 0; i < src.length; i++) { + if (src[i] !== dst[i]) { + return false; + } + } + + return true; + } } diff --git a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts index 6bab18a7d0c..a2134be52ef 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts @@ -1,34 +1,38 @@ -/* eslint-disable import/no-internal-modules */ import type { Scene, AbstractEngine, - RenderTargetWrapper, - RenderTargetCreationOptions, TextureSize, Nullable, FrameGraphTextureCreationOptions, FrameGraphTextureHandle, + InternalTextureCreationOptions, + InternalTexture, + FrameGraphTextureOptions, + FrameGraphTextureDescription, + RenderTargetWrapper, + // eslint-disable-next-line import/no-internal-modules } from "core/index"; -import { getDimensionsFromTextureSize } from "../Materials/Textures/textureCreationOptions"; +import { getDimensionsFromTextureSize, textureSizeIsObject } from "../Materials/Textures/textureCreationOptions"; import { Texture } from "../Materials/Textures/texture"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "./frameGraphTypes"; import { Constants } from "../Engines/constants"; +import { InternalTextureSource, GetTypeForDepthTexture, IsDepthTexture, HasStencilAspect } from "../Materials/Textures/internalTexture"; +import { FrameGraphRenderTarget } from "./frameGraphRenderTarget"; type HistoryTexture = { - textures: Array>; + textures: Array>; handles: Array; - index: number; - refHandles: Array; // (dangling) handles that reference this history texture + index: number; // current index in textures array + references: Array<{ renderTargetWrapper: RenderTargetWrapper; textureIndex: number }>; // render target wrappers that reference this history texture }; type TextureEntry = { - texture: Nullable; + texture: Nullable; name: string; - creationOptions: FrameGraphTextureCreationOptions; + creationOptions: FrameGraphTextureCreationOptions; // in TextureEntry, creationOptions.size is always an object namespace: FrameGraphTextureNamespace; + textureIndex?: number; // Index of the texture in the types, formats, etc array of FrameGraphTextureOptions (default: 0) debug?: Texture; - parentHandle?: FrameGraphTextureHandle; // Parent handle if the texture comes from a multi-target texture - parentTextureIndex?: number; // Index of the texture in the parent if the texture comes from a multi-target texture refHandle?: FrameGraphTextureHandle; // Handle of the texture this one is referencing - used for dangling handles }; @@ -39,23 +43,40 @@ enum FrameGraphTextureNamespace { } /** + * Manages the textures used by a frame graph * @experimental - * @internal */ export class FrameGraphTextureManager { private static _Counter = 2; // 0 and 1 are reserved for backbuffer textures - public _textures: Map = new Map(); - public _historyTextures: Map = new Map(); + /** @internal */ + public readonly _textures: Map = new Map(); + + /** @internal */ + public readonly _historyTextures: Map = new Map(); + + /** @internal */ + public _isRecordingTask = false; + /** + * Constructs a new instance of the texture manager + * @param engine The engine to use + * @param _debugTextures If true, debug textures will be created so that they are visible in the inspector + * @param _scene The scene the manager belongs to + */ constructor( - private _engine: AbstractEngine, - private _debugTextures = false, - private _scene?: Scene + public readonly engine: AbstractEngine, + private readonly _debugTextures = false, + private _scene: Scene ) { this._addSystemTextures(); } + /** + * Checks if a handle is a backbuffer handle (color or depth/stencil) + * @param handle The handle to check + * @returns True if the handle is a backbuffer handle + */ public isBackbuffer(handle: FrameGraphTextureHandle): boolean { if (handle === backbufferColorTextureHandle || handle === backbufferDepthStencilTextureHandle) { return true; @@ -69,6 +90,11 @@ export class FrameGraphTextureManager { return textureEntry.refHandle === backbufferColorTextureHandle || textureEntry.refHandle === backbufferDepthStencilTextureHandle; } + /** + * Checks if a handle is a backbuffer color handle + * @param handle The handle to check + * @returns True if the handle is a backbuffer color handle + */ public isBackbufferColor(handle: FrameGraphTextureHandle): boolean { if (handle === backbufferColorTextureHandle) { return true; @@ -82,6 +108,11 @@ export class FrameGraphTextureManager { return textureEntry.refHandle === backbufferColorTextureHandle; } + /** + * Checks if a handle is a backbuffer depth/stencil handle + * @param handle The handle to check + * @returns True if the handle is a backbuffer depth/stencil handle + */ public isBackbufferDepthStencil(handle: FrameGraphTextureHandle): boolean { if (handle === backbufferDepthStencilTextureHandle) { return true; @@ -95,79 +126,242 @@ export class FrameGraphTextureManager { return textureEntry.refHandle === backbufferDepthStencilTextureHandle; } + /** + * Checks if a handle is a history texture (or points to a history texture, for a dangling handle) + * @param handle The handle to check + * @returns True if the handle is a history texture, otherwise false + */ + public isHistoryTexture(handle: FrameGraphTextureHandle): boolean { + const entry = this._textures.get(handle); + if (!entry) { + return false; + } + + handle = entry.refHandle ?? handle; + + return this._historyTextures.has(handle); + } + + /** + * Gets the creation options of a texture + * @param handle Handle of the texture + * @returns The creation options of the texture + */ public getTextureCreationOptions(handle: FrameGraphTextureHandle): FrameGraphTextureCreationOptions { - return this._textures.get(handle)!.creationOptions; + const entry = this._textures.get(handle)!; + const creationOptions = entry.creationOptions; + + return { + size: textureSizeIsObject(creationOptions.size) ? { ...creationOptions.size } : creationOptions.size, + sizeIsPercentage: creationOptions.sizeIsPercentage, + options: this._cloneTextureOptions(creationOptions.options, entry.textureIndex), + isHistoryTexture: creationOptions.isHistoryTexture, + }; } - public getTextureFromHandle(handle: FrameGraphTextureHandle): Nullable { + /** + * Gets the description of a texture + * @param handle Handle of the texture + * @returns The description of the texture + */ + public getTextureDescription(handle: FrameGraphTextureHandle): FrameGraphTextureDescription { + const creationOptions = this.getTextureCreationOptions(handle); + + const size = !creationOptions.sizeIsPercentage + ? textureSizeIsObject(creationOptions.size) + ? creationOptions.size + : { width: creationOptions.size, height: creationOptions.size } + : this.getAbsoluteDimensions(creationOptions.size); + + return { + size, + options: creationOptions.options, + }; + } + + /** + * Gets a texture handle or creates a new texture if the handle is not provided. + * If handle is not provided, newTextureName and creationOptions must be provided. + * @param handle If provided, will simply return the handle + * @param newTextureName Name of the new texture to create + * @param creationOptions Options to use when creating the new texture + * @returns The handle to the texture. + */ + public getTextureHandleOrCreateTexture(handle?: FrameGraphTextureHandle, newTextureName?: string, creationOptions?: FrameGraphTextureCreationOptions): FrameGraphTextureHandle { + if (handle === undefined) { + if (newTextureName === undefined || creationOptions === undefined) { + throw new Error("getTextureHandleOrCreateTexture: Either handle or newTextureName and creationOptions must be provided."); + } + return this.createRenderTargetTexture(newTextureName, creationOptions); + } + return handle; + } + + /** + * Gets a texture from a handle. + * Note that if the texture is a history texture, the read texture for the current frame will be returned. + * @param handle The handle of the texture + * @returns The texture or null if not found + */ + public getTextureFromHandle(handle: FrameGraphTextureHandle): Nullable { const historyEntry = this._historyTextures.get(handle); if (historyEntry) { - return historyEntry.textures[historyEntry.index ^ 1]; // get the read texture + return historyEntry.textures[historyEntry.index ^ 1]; // gets the read texture } return this._textures.get(handle)!.texture; } - public importTexture(name: string, texture: RenderTargetWrapper, handle?: FrameGraphTextureHandle): FrameGraphTextureHandle { - const internalTexture = texture.texture; - - if (!internalTexture) { - throw new Error("importTexture: Texture must have an internal texture to be imported"); - } - + /** + * Imports a texture into the texture manager + * @param name Name of the texture + * @param texture Texture to import + * @param handle Existing handle to use for the texture. If not provided (default), a new handle will be created. + * @returns The handle to the texture + */ + public importTexture(name: string, texture: InternalTexture, handle?: FrameGraphTextureHandle): FrameGraphTextureHandle { if (handle !== undefined) { this._freeEntry(handle); } const creationOptions: FrameGraphTextureCreationOptions = { size: { width: texture.width, height: texture.height }, + sizeIsPercentage: false, + isHistoryTexture: false, options: { - generateMipMaps: internalTexture.generateMipMaps, - generateDepthBuffer: texture._generateDepthBuffer, - generateStencilBuffer: texture._generateStencilBuffer, - samples: internalTexture.samples, - label: internalTexture.label, - types: [internalTexture.type], - samplingModes: [internalTexture.samplingMode], - formats: [internalTexture.format], - targetTypes: [ - internalTexture.isCube - ? Constants.TEXTURE_CUBE_MAP - : internalTexture.is3D - ? Constants.TEXTURE_3D - : internalTexture.is2DArray - ? Constants.TEXTURE_2D_ARRAY - : Constants.TEXTURE_2D, - ], - useSRGBBuffers: [internalTexture._useSRGBBuffer], - labels: internalTexture.label ? [internalTexture.label] : undefined, + createMipMaps: texture.generateMipMaps, + samples: texture.samples, + types: [texture.type], + formats: [texture.format], + useSRGBBuffers: [texture._useSRGBBuffer], + creationFlags: [texture._creationFlags], + labels: texture.label ? [texture.label] : ["imported"], }, - sizeIsPercentage: false, }; - return this._createHandleForTexture(name, texture, creationOptions, FrameGraphTextureNamespace.External, false, handle); + return this._createHandleForTexture(name, texture, creationOptions, FrameGraphTextureNamespace.External, handle); } - public createRenderTargetTexture( - name: string, - taskNamespace: boolean, - creationOptions: FrameGraphTextureCreationOptions, - multiTargetMode = false, - handle?: FrameGraphTextureHandle - ): FrameGraphTextureHandle { + /** + * Creates a new render target texture + * If multiple textures are described in FrameGraphTextureCreationOptions, the handle of the first texture is returned, handle+1 is the handle of the second texture, etc. + * @param name Name of the texture + * @param creationOptions Options to use when creating the texture + * @param handle Existing handle to use for the texture. If not provided (default), a new handle will be created. + * @returns The handle to the texture + */ + public createRenderTargetTexture(name: string, creationOptions: FrameGraphTextureCreationOptions, handle?: FrameGraphTextureHandle): FrameGraphTextureHandle { return this._createHandleForTexture( name, null, - creationOptions, - taskNamespace ? FrameGraphTextureNamespace.Task : FrameGraphTextureNamespace.Graph, - multiTargetMode, + { + size: textureSizeIsObject(creationOptions.size) ? { ...creationOptions.size } : creationOptions.size, + sizeIsPercentage: creationOptions.sizeIsPercentage, + isHistoryTexture: creationOptions.isHistoryTexture, + options: this._cloneTextureOptions(creationOptions.options), + }, + this._isRecordingTask ? FrameGraphTextureNamespace.Task : FrameGraphTextureNamespace.Graph, handle ); } + /** + * Creates a (frame graph) render target wrapper + * Note that renderTargets or renderTargetDepth can be undefined, but not both at the same time! + * @param name Name of the render target wrapper + * @param renderTargets Render target handles (textures) to use + * @param renderTargetDepth Render target depth handle (texture) to use + * @returns The created render target wrapper + */ + public createRenderTarget( + name: string, + renderTargets?: FrameGraphTextureHandle | FrameGraphTextureHandle[], + renderTargetDepth?: FrameGraphTextureHandle + ): FrameGraphRenderTarget { + const renderTarget = new FrameGraphRenderTarget(name, this, renderTargets, renderTargetDepth); + + const rtw = renderTarget.renderTargetWrapper; + + if (rtw !== undefined && renderTargets) { + const handles = Array.isArray(renderTargets) ? renderTargets : [renderTargets]; + + for (let i = 0; i < handles.length; i++) { + let handle = handles[i]; + handle = this._textures.get(handle)?.refHandle ?? handle; + + const historyEntry = this._historyTextures.get(handle); + if (historyEntry) { + historyEntry.references.push({ renderTargetWrapper: rtw, textureIndex: i }); + + rtw.setTexture(historyEntry.textures[historyEntry.index]!, i, false); + } + } + } + + return renderTarget; + } + + /** + * Creates a handle which is not associated with any texture. + * Call resolveDanglingHandle to associate the handle with a valid texture handle. + * @returns The dangling handle + */ + public createDanglingHandle() { + return FrameGraphTextureManager._Counter++; + } + + /** + * Associates a texture with a dangling handle + * @param danglingHandle The dangling handle + * @param handle The handle to associate with the dangling handle (if not provided, a new texture handle will be created, using the newTextureName and creationOptions parameters) + * @param newTextureName The name of the new texture to create (if handle is not provided) + * @param creationOptions The options to use when creating the new texture (if handle is not provided) + */ + public resolveDanglingHandle( + danglingHandle: FrameGraphTextureHandle, + handle?: FrameGraphTextureHandle, + newTextureName?: string, + creationOptions?: FrameGraphTextureCreationOptions + ) { + if (handle === undefined) { + if (newTextureName === undefined || creationOptions === undefined) { + throw new Error("resolveDanglingHandle: Either handle or newTextureName and creationOptions must be provided."); + } + this.createRenderTargetTexture(newTextureName, creationOptions, danglingHandle); + return; + } + + const textureEntry = this._textures.get(handle); + + if (textureEntry === undefined) { + throw new Error(`resolveDanglingHandle: Handle ${handle} does not exist!`); + } + + this._textures.set(danglingHandle, { + texture: textureEntry.texture, + refHandle: handle, + name: textureEntry.name, + creationOptions: { + size: { ...(textureEntry.creationOptions.size as { width: number; height: number; depth?: number; layers?: number }) }, + options: this._cloneTextureOptions(textureEntry.creationOptions.options), + sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, + isHistoryTexture: false, + }, + namespace: textureEntry.namespace, + textureIndex: textureEntry.textureIndex, + }); + } + + /** + * Gets the absolute dimensions of a texture. + * @param size The size of the texture. Width and height must be expressed as a percentage of the screen size (100=100%)! + * @param screenWidth The width of the screen (default: the width of the rendering canvas) + * @param screenHeight The height of the screen (default: the height of the rendering canvas) + * @returns The absolute dimensions of the texture + */ public getAbsoluteDimensions( size: TextureSize, - screenWidth = this._engine.getRenderWidth(true), - screenHeight = this._engine.getRenderHeight(true) + screenWidth = this.engine.getRenderWidth(true), + screenHeight = this.engine.getRenderHeight(true) ): { width: number; height: number } { const { width, height } = getDimensionsFromTextureSize(size); @@ -177,28 +371,21 @@ export class FrameGraphTextureManager { }; } - public updateHistoryTextures(): void { - this._historyTextures.forEach((entry) => { - entry.index = entry.index ^ 1; - for (const refHandle of entry.refHandles) { - const textureEntry = this._textures.get(refHandle)!; - textureEntry.texture = entry.textures[entry.index]; - } - }); - } - - public dispose(): void { - this.releaseTextures(); + /** @internal */ + public _dispose(): void { + this._releaseTextures(); } - public allocateTextures() { + /** @internal */ + public _allocateTextures() { this._textures.forEach((entry) => { if (!entry.texture) { if (entry.refHandle !== undefined) { + // entry is a dangling handle which has been resolved to point to refHandle + // We simply update the texture to point to the refHandle texture const refEntry = this._textures.get(entry.refHandle)!; entry.texture = refEntry.texture; - entry.texture?.texture?.incrementReferences(); if (refEntry.refHandle === backbufferColorTextureHandle) { entry.refHandle = backbufferColorTextureHandle; @@ -207,34 +394,38 @@ export class FrameGraphTextureManager { entry.refHandle = backbufferDepthStencilTextureHandle; } } else if (entry.namespace !== FrameGraphTextureNamespace.External) { - if (entry.parentHandle !== undefined) { - const creationOptions = entry.creationOptions; - const size = creationOptions.sizeIsPercentage ? this.getAbsoluteDimensions(creationOptions.size) : creationOptions.size; - - const parentEntry = this._textures.get(entry.parentHandle)!; - const parentInternalTexture = parentEntry.texture!.textures![entry.parentTextureIndex!]; - - const creationOptionsForTexture: RenderTargetCreationOptions = { - createMipMaps: creationOptions.options.createMipMaps, - generateMipMaps: creationOptions.options.generateMipMaps, - generateDepthBuffer: creationOptions.options.generateDepthBuffer, - generateStencilBuffer: creationOptions.options.generateStencilBuffer, - samples: creationOptions.options.samples, - type: creationOptions.options.types![0], - format: creationOptions.options.formats![0], - useSRGBBuffer: creationOptions.options.useSRGBBuffers![0], - colorAttachment: parentInternalTexture, - label: creationOptions.options.label, - }; - - entry.texture = this._engine.createRenderTargetTexture(size, creationOptionsForTexture); - parentInternalTexture.incrementReferences(); - } else { - const creationOptions = entry.creationOptions; - const size = creationOptions.sizeIsPercentage ? this.getAbsoluteDimensions(creationOptions.size) : creationOptions.size; - - entry.texture = this._engine.createMultipleRenderTarget(size, creationOptions.options, false); + const creationOptions = entry.creationOptions; + const size = creationOptions.sizeIsPercentage ? this.getAbsoluteDimensions(creationOptions.size) : creationOptions.size; + const textureIndex = entry.textureIndex || 0; + + const internalTextureCreationOptions: InternalTextureCreationOptions = { + createMipMaps: creationOptions.options.createMipMaps, + samples: creationOptions.options.samples, + type: creationOptions.options.types?.[textureIndex], + format: creationOptions.options.formats?.[textureIndex], + useSRGBBuffer: creationOptions.options.useSRGBBuffers?.[textureIndex], + creationFlags: creationOptions.options.creationFlags?.[textureIndex], + label: creationOptions.options.labels?.[textureIndex] ?? `${entry.name}${textureIndex > 0 ? "#" + textureIndex : ""}`, + samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, + createMSAATexture: creationOptions.options.samples! > 1, + }; + + const isDepthTexture = IsDepthTexture(internalTextureCreationOptions.format!); + const hasStencil = HasStencilAspect(internalTextureCreationOptions.format!); + const source = + isDepthTexture && hasStencil + ? InternalTextureSource.DepthStencil + : isDepthTexture || hasStencil + ? InternalTextureSource.Depth + : InternalTextureSource.RenderTarget; + + const internalTexture = this.engine._createInternalTexture(size, internalTextureCreationOptions, false, source); + + if (isDepthTexture) { + internalTexture.type = GetTypeForDepthTexture(internalTexture.format); } + + entry.texture = internalTexture; } } @@ -251,39 +442,8 @@ export class FrameGraphTextureManager { }); } - public createDanglingHandle() { - return FrameGraphTextureManager._Counter++; - } - - public resolveDanglingHandle(danglingHandle: FrameGraphTextureHandle, handle: FrameGraphTextureHandle) { - const textureEntry = this._textures.get(handle); - - if (textureEntry === undefined) { - throw new Error(`resolveDanglingHandle: Handle ${handle} does not exist!`); - } - - this._textures.set(danglingHandle, { - texture: textureEntry.texture, - refHandle: handle, - name: textureEntry.name, - creationOptions: { - size: { ...(textureEntry.creationOptions.size as { width: number; height: number; depth?: number; layers?: number }) }, - options: { ...textureEntry.creationOptions.options, label: textureEntry.name }, - sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, - isHistoryTexture: false, - }, - namespace: textureEntry.namespace, - parentHandle: textureEntry.parentHandle, - parentTextureIndex: textureEntry.parentTextureIndex, - }); - - const historyEntry = this._historyTextures.get(handle); - if (historyEntry) { - historyEntry.refHandles.push(danglingHandle); - } - } - - public releaseTextures(releaseAll = true): void { + /** @internal */ + public _releaseTextures(releaseAll = true): void { this._textures.forEach((entry, handle) => { if (releaseAll || entry.namespace !== FrameGraphTextureNamespace.External) { entry.debug?.dispose(); @@ -315,15 +475,36 @@ export class FrameGraphTextureManager { } } + /** @internal */ + public _updateHistoryTextures(): void { + this._historyTextures.forEach((entry) => { + entry.index = entry.index ^ 1; + const currentTexture = entry.textures[entry.index]; + if (currentTexture) { + for (const { renderTargetWrapper, textureIndex } of entry.references) { + renderTargetWrapper.setTexture(currentTexture, textureIndex, false); + } + } + }); + } + private _addSystemTextures() { - const size = { width: this._engine.getRenderWidth(true), height: this._engine.getRenderHeight(true) }; + const size = { width: this.engine.getRenderWidth(true), height: this.engine.getRenderHeight(true) }; this._textures.set(backbufferColorTextureHandle, { name: "backbuffer color", texture: null, creationOptions: { size, - options: {}, + options: { + createMipMaps: false, + samples: this.engine.getCreationOptions().antialias ? 4 : 1, + types: [Constants.TEXTURETYPE_UNSIGNED_BYTE], // todo? get from engine + formats: [Constants.TEXTUREFORMAT_RGBA], // todo? get from engine + useSRGBBuffers: [false], + creationFlags: [0], + labels: ["backbuffer color"], + }, sizeIsPercentage: false, }, namespace: FrameGraphTextureNamespace.External, @@ -334,22 +515,30 @@ export class FrameGraphTextureManager { texture: null, creationOptions: { size, - options: {}, + options: { + createMipMaps: false, + samples: this.engine.getCreationOptions().antialias ? 4 : 1, + types: [Constants.TEXTURETYPE_UNSIGNED_BYTE], // todo? get from engine + formats: [Constants.TEXTUREFORMAT_DEPTH24], // todo? get from engine + useSRGBBuffers: [false], + creationFlags: [0], + labels: ["backbuffer depth/stencil"], + }, sizeIsPercentage: false, }, namespace: FrameGraphTextureNamespace.External, }); } - private _createDebugTexture(name: string, texture: RenderTargetWrapper): Texture | undefined { - if (!this._debugTextures || !this._scene) { + private _createDebugTexture(name: string, texture: InternalTexture): Texture | undefined { + if (!this._debugTextures) { return; } const textureDebug = new Texture(null, this._scene); textureDebug.name = name; - textureDebug._texture = texture.texture || texture._depthStencilTexture!; + textureDebug._texture = texture; textureDebug._texture.incrementReferences(); return textureDebug; @@ -366,30 +555,30 @@ export class FrameGraphTextureManager { private _createHandleForTexture( name: string, - texture: Nullable, + texture: Nullable, creationOptions: FrameGraphTextureCreationOptions, namespace: FrameGraphTextureNamespace, - multiTargetMode = false, handle?: FrameGraphTextureHandle, - parentHandle?: FrameGraphTextureHandle, - parentTextureIndex?: number + textureIndex?: number ): FrameGraphTextureHandle { handle = handle ?? FrameGraphTextureManager._Counter++; + textureIndex = textureIndex || 0; + + const textureName = creationOptions.isHistoryTexture ? `${name} ping` : name; - const textureName = creationOptions.isHistoryTexture ? `${name} - ping` : name; + const label = creationOptions.options.labels?.[textureIndex] ?? ""; const textureEntry: TextureEntry = { texture, - name: textureName, + name: `${textureName}${label ? " " + label : ""}`, creationOptions: { - size: getDimensionsFromTextureSize(creationOptions.size), - options: { ...creationOptions.options, label: textureName }, + size: textureSizeIsObject(creationOptions.size) ? creationOptions.size : { width: creationOptions.size, height: creationOptions.size }, + options: creationOptions.options, sizeIsPercentage: creationOptions.sizeIsPercentage, isHistoryTexture: creationOptions.isHistoryTexture, }, namespace, - parentHandle, - parentTextureIndex, + textureIndex, }; this._textures.set(handle, textureEntry); @@ -406,34 +595,50 @@ export class FrameGraphTextureManager { isHistoryTexture: false, }; - const pongTexture = this._createHandleForTexture(`${name} - pong`, null, pongCreationOptions, namespace, false); + const pongTexture = this._createHandleForTexture(`${name} pong`, null, pongCreationOptions, namespace); - this._historyTextures.set(handle, { textures: [null, null], handles: [handle, pongTexture], index: 0, refHandles: [] }); + this._historyTextures.set(handle, { textures: [null, null], handles: [handle, pongTexture], index: 0, references: [] }); return handle; } - if (multiTargetMode) { - const textureCount = creationOptions.options.textureCount ?? 1; - for (let i = 0; i < textureCount; i++) { - const label = creationOptions.options.labels?.[i] ?? `${i}`; - const textureName = `${name} - ${label}`; - const creationOptionsForTexture: FrameGraphTextureCreationOptions = { - size: getDimensionsFromTextureSize(creationOptions.size), - options: { - ...creationOptions.options, - formats: [creationOptions.options.formats![i]], - types: [creationOptions.options.types![i]], - textureCount: 1, - }, - sizeIsPercentage: creationOptions.sizeIsPercentage, - }; - this._createHandleForTexture(textureName, null, creationOptionsForTexture, namespace, false, handle + i + 1, handle, i); + if (creationOptions.options.types && creationOptions.options.types.length > 1 && textureIndex === 0) { + const textureCount = creationOptions.options.types.length; + const creationOptionsForTexture: FrameGraphTextureCreationOptions = { + size: textureSizeIsObject(creationOptions.size) ? creationOptions.size : { width: creationOptions.size, height: creationOptions.size }, + options: creationOptions.options, + sizeIsPercentage: creationOptions.sizeIsPercentage, + }; + + for (let i = 1; i < textureCount; i++) { + this._createHandleForTexture(textureName, null, creationOptionsForTexture, namespace, handle + i, i); } - FrameGraphTextureManager._Counter += textureCount; + FrameGraphTextureManager._Counter += textureCount - 1; } return handle; } + + private _cloneTextureOptions(options: FrameGraphTextureOptions, textureIndex?: number): FrameGraphTextureOptions { + return textureIndex !== undefined + ? { + createMipMaps: options.createMipMaps, + samples: options.samples, + types: options.types ? [options.types[textureIndex]] : undefined, + formats: options.formats ? [options.formats[textureIndex]] : undefined, + useSRGBBuffers: options.useSRGBBuffers ? [options.useSRGBBuffers[textureIndex]] : undefined, + creationFlags: options.creationFlags ? [options.creationFlags[textureIndex]] : undefined, + labels: options.labels ? [options.labels[textureIndex]] : undefined, + } + : { + createMipMaps: options.createMipMaps, + samples: options.samples, + types: options.types ? [...options.types] : undefined, + formats: options.formats ? [...options.formats] : undefined, + useSRGBBuffers: options.useSRGBBuffers ? [...options.useSRGBBuffers] : undefined, + creationFlags: options.creationFlags ? [...options.creationFlags] : undefined, + labels: options.labels ? [...options.labels] : undefined, + }; + } } diff --git a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts index 1eb9840e1dc..b50987c7d4b 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { Nullable, TextureSize, FrameGraphContext, IMultiRenderTargetOptions } from "core/index"; +import type { Nullable, TextureSize, FrameGraphContext } from "core/index"; /** * Represents a texture handle in the frame graph. @@ -16,20 +16,43 @@ export const backbufferColorTextureHandle: FrameGraphTextureHandle = 0; */ export const backbufferDepthStencilTextureHandle: FrameGraphTextureHandle = 1; +export type FrameGraphTextureOptions = { + /** Specifies if mipmaps must be created for the textures (default: false) */ + createMipMaps?: boolean; + + /** Defines sample count (default: 1) */ + samples?: number; + + /** Defines the types of the textures */ + types?: number[]; + + /** Defines the format of the textures (RED, RG, RGB, RGBA, ALPHA...) */ + formats?: number[]; + + /** Defines if sRGB format should be used for each of texture */ + useSRGBBuffers?: boolean[]; + + /** Defines the creation flags of the textures (Constants.TEXTURE_CREATIONFLAG_STORAGE for storage textures, for eg) */ + creationFlags?: number[]; + + /** Defines the names of the textures (used for debugging purpose) */ + labels?: string[]; +}; + /** - * Options used to create a texture in the frame graph. + * Options used to create a texture / list of textures in the frame graph. */ export type FrameGraphTextureCreationOptions = { - /** Size of the texture. If sizeIsPercentage is true, these are percentages relative to the screen size */ + /** Size of the textures. If sizeIsPercentage is true, these are percentages relative to the screen size (100 = 100%) */ size: TextureSize; - /** Options used to create the (multi) render target texture */ - options: IMultiRenderTargetOptions; + /** Options used to create the textures */ + options: FrameGraphTextureOptions; /** If true, indicates that "size" is percentages relative to the screen size */ sizeIsPercentage: boolean; - /** Indicates that the texture is a history texture */ + /** Indicates that the texture is a history texture (default: false) */ isHistoryTexture?: boolean; }; @@ -38,15 +61,11 @@ export type FrameGraphTextureCreationOptions = { * This is basically the same thing than FrameGraphTextureCreationOptions, but the size is never in percentage and always in pixels. */ export type FrameGraphTextureDescription = { - /** - * Size of the texture. - */ + /** Size of the texture */ size: { width: number; height: number }; - /** - * Options used when the (multi) render target texture had been created. - */ - options: IMultiRenderTargetOptions; + /** Options used to create the texture */ + options: FrameGraphTextureOptions; }; /** diff --git a/packages/dev/core/src/FrameGraph/index.ts b/packages/dev/core/src/FrameGraph/index.ts index d3f6e94c141..cb9a7f12ae3 100644 --- a/packages/dev/core/src/FrameGraph/index.ts +++ b/packages/dev/core/src/FrameGraph/index.ts @@ -30,6 +30,7 @@ export * from "./frameGraph"; export * from "./frameGraphContext"; export * from "./frameGraphObjectList"; export * from "./frameGraphRenderContext"; +export * from "./frameGraphRenderTarget"; export * from "./frameGraphTask"; export * from "./frameGraphTextureManager"; export * from "./frameGraphTypes"; diff --git a/packages/dev/core/src/Materials/Textures/internalTexture.ts b/packages/dev/core/src/Materials/Textures/internalTexture.ts index 14bd8cf8a9f..b9aa6746b1b 100644 --- a/packages/dev/core/src/Materials/Textures/internalTexture.ts +++ b/packages/dev/core/src/Materials/Textures/internalTexture.ts @@ -7,6 +7,7 @@ import { TextureSampler } from "./textureSampler"; import type { AbstractEngine } from "../../Engines/abstractEngine"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; import type { SphericalPolynomial } from "../../Maths/sphericalPolynomial"; +import { Constants } from "core/Engines/constants"; /** * Defines the source of the internal texture @@ -74,6 +75,59 @@ export const enum InternalTextureSource { Depth, } +/** + * Checks if a given format is a depth texture format + * @param format Format to check + * @returns True if the format is a depth texture format + */ +export function IsDepthTexture(format: number): boolean { + return ( + format === Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 || + format === Constants.TEXTUREFORMAT_DEPTH32_FLOAT || + format === Constants.TEXTUREFORMAT_DEPTH16 || + format === Constants.TEXTUREFORMAT_DEPTH24 || + format === Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 || + format === Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8 || + format === Constants.TEXTUREFORMAT_STENCIL8 + ); +} + +/** + * Gets the type of a depth texture for a given format + * @param format Format of the texture + * @returns The type of the depth texture + */ +export function GetTypeForDepthTexture(format: number): number { + switch (format) { + case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8: + case Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8: + case Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8: + case Constants.TEXTUREFORMAT_DEPTH32_FLOAT: + case Constants.TEXTUREFORMAT_DEPTH24: + return Constants.TEXTURETYPE_FLOAT; + case Constants.TEXTUREFORMAT_DEPTH16: + return Constants.TEXTURETYPE_UNSIGNED_SHORT; + case Constants.TEXTUREFORMAT_STENCIL8: + return Constants.TEXTURETYPE_UNSIGNED_BYTE; + } + + return Constants.TEXTURETYPE_UNSIGNED_BYTE; +} + +/** + * Checks if a given format has a stencil aspect + * @param format Format to check + * @returns True if the format has a stencil aspect + */ +export function HasStencilAspect(format: number): boolean { + return ( + format === Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 || + format === Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 || + format === Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8 || + format === Constants.TEXTUREFORMAT_STENCIL8 + ); +} + /** * Class used to store data associated with WebGL texture data for the engine * This class should not be used directly @@ -261,6 +315,9 @@ export class InternalTexture extends TextureSampler { /** @internal */ public _dynamicTextureSource: Nullable = null; + /** @internal */ + public _autoMSAAManagement = false; + private _engine: AbstractEngine; private _uniqueId: number; diff --git a/packages/dev/core/src/Materials/Textures/multiRenderTarget.ts b/packages/dev/core/src/Materials/Textures/multiRenderTarget.ts index f06af319b01..306813f3dad 100644 --- a/packages/dev/core/src/Materials/Textures/multiRenderTarget.ts +++ b/packages/dev/core/src/Materials/Textures/multiRenderTarget.ts @@ -104,6 +104,11 @@ export interface IMultiRenderTargetOptions { * Label of the RenderTargetWrapper (used for debugging only) */ label?: string; + /** + * Define if the textures should not be created by the MultiRenderTarget (default: false) + * If true, you will need to set the textures yourself by calling setTexture on the MultiRenderTarget. + */ + dontCreateTextures?: boolean; } /** diff --git a/packages/dev/core/src/Materials/Textures/textureCreationOptions.ts b/packages/dev/core/src/Materials/Textures/textureCreationOptions.ts index 81ccb27700a..0dc15a9778d 100644 --- a/packages/dev/core/src/Materials/Textures/textureCreationOptions.ts +++ b/packages/dev/core/src/Materials/Textures/textureCreationOptions.ts @@ -4,13 +4,9 @@ import type { InternalTexture } from "./internalTexture"; * Define options used to create an internal texture */ export interface InternalTextureCreationOptions { - /** - * Specifies if mipmaps must be created. If undefined, the value from generateMipMaps is taken instead - */ + /** Specifies if mipmaps must be created. If undefined, the value from generateMipMaps is taken instead */ createMipMaps?: boolean; - /** - * Specifies if mipmaps must be generated - */ + /** Specifies if mipmaps must be generated */ generateMipMaps?: boolean; /** Defines texture type (unsigned byte by default) */ type?: number; @@ -26,6 +22,10 @@ export interface InternalTextureCreationOptions { useSRGBBuffer?: boolean; /** Label of the texture (used for debugging only) */ label?: string; + /** If the MSAA texture must be created right away (default: false) */ + createMSAATexture?: boolean; + /** Comparison function. Used only for depth textures (default: 0) */ + comparisonFunction?: number; } /** diff --git a/packages/dev/gui/src/2D/FrameGraph/guiTask.ts b/packages/dev/gui/src/2D/FrameGraph/guiTask.ts index 87b6c64eb05..f49938f4fa0 100644 --- a/packages/dev/gui/src/2D/FrameGraph/guiTask.ts +++ b/packages/dev/gui/src/2D/FrameGraph/guiTask.ts @@ -54,7 +54,7 @@ export class FrameGraphGUITask extends FrameGraphTask { } this._adt = adt; - this.outputTexture = this._frameGraph.createDanglingHandle(); + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public override isReady() { @@ -66,7 +66,7 @@ export class FrameGraphGUITask extends FrameGraphTask { throw new Error("FrameGraphGUITask: destinationTexture is required"); } - this._frameGraph.resolveDanglingHandle(this.outputTexture, this.destinationTexture); + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); const pass = this._frameGraph.addRenderPass(this.name); diff --git a/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx b/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx index 73b6711ade5..257fcd2c99e 100644 --- a/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx +++ b/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx @@ -27,8 +27,8 @@ import { NodeRenderGraphBlock } from "core/FrameGraph/Node/nodeRenderGraphBlock" import { NodeRenderGraphOutputBlock } from "core/FrameGraph/Node/Blocks/outputBlock"; import type { NodeRenderGraphBlockConnectionPointTypes } from "core/FrameGraph/Node/Types/nodeRenderGraphTypes"; import { NodeRenderGraphInputBlock } from "core/FrameGraph/Node/Blocks/inputBlock"; -import type { RenderTargetWrapper } from "core/Engines/renderTargetWrapper"; import { Constants } from "core/Engines/constants"; +import type { InternalTexture } from "core/Materials/Textures/internalTexture"; interface IGraphEditorProps { globalState: GlobalState; @@ -68,7 +68,7 @@ export class GraphEditor extends React.Component; private _popUpWindow: Window; - private _externalTextures: RenderTargetWrapper[] = []; + private _externalTextures: InternalTexture[] = []; appendBlock(dataToAppend: NodeRenderGraphBlock | INodeData, recursion = true) { return this._graphCanvas.createNodeFromObject( @@ -251,16 +251,14 @@ export class GraphEditor extends React.Component this.props.stateManager.onRebuildRequiredObservable.notifyObservers()} /> - this.props.stateManager.onRebuildRequiredObservable.notifyObservers()} - /> + {creationOptions.options.formats && ( + { + creationOptions.options.formats![0] = value as number; + this.props.stateManager.onRebuildRequiredObservable.notifyObservers(); + }} + extractValue={() => creationOptions.options.formats![0]} + noDirectUpdate={true} + /> + )} Date: Fri, 22 Nov 2024 15:46:29 +0100 Subject: [PATCH 02/20] Add missing "readonly" and docs --- .../Engines/WebGPU/Extensions/engine.renderTarget.ts | 10 ++-------- packages/dev/core/src/FrameGraph/Passes/renderPass.ts | 4 ++-- .../Tasks/Rendering/taaObjectRendererTask.ts | 4 ++-- .../dev/core/src/FrameGraph/frameGraphRenderTarget.ts | 6 +++--- packages/dev/core/src/FrameGraph/frameGraphTask.ts | 4 ++-- .../core/src/FrameGraph/frameGraphTextureManager.ts | 2 +- packages/dev/core/src/FrameGraph/frameGraphTypes.ts | 3 +++ 7 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/dev/core/src/Engines/WebGPU/Extensions/engine.renderTarget.ts b/packages/dev/core/src/Engines/WebGPU/Extensions/engine.renderTarget.ts index 4f9e7946558..505e810da8d 100644 --- a/packages/dev/core/src/Engines/WebGPU/Extensions/engine.renderTarget.ts +++ b/packages/dev/core/src/Engines/WebGPU/Extensions/engine.renderTarget.ts @@ -1,11 +1,9 @@ -import { InternalTexture, InternalTextureSource } from "../../../Materials/Textures/internalTexture"; +import { GetTypeForDepthTexture, InternalTexture, InternalTextureSource } from "../../../Materials/Textures/internalTexture"; import type { RenderTargetCreationOptions, DepthTextureCreationOptions, TextureSize } from "../../../Materials/Textures/textureCreationOptions"; import type { Nullable } from "../../../types"; import { Constants } from "../../constants"; import type { RenderTargetWrapper } from "../../renderTargetWrapper"; -import type { WebGPUHardwareTexture } from "../webgpuHardwareTexture"; import { WebGPURenderTargetWrapper } from "../webgpuRenderTargetWrapper"; -import { WebGPUTextureHelper } from "../webgpuTextureHelper"; import "../../AbstractEngine/abstractEngine.texture"; import { ThinWebGPUEngine } from "core/Engines/thinWebGPUEngine"; @@ -130,16 +128,12 @@ ThinWebGPUEngine.prototype._createDepthStencilTexture = function (size: TextureS internalTexture.label = options.label; internalTexture.format = internalOptions.depthTextureFormat; + internalTexture.type = GetTypeForDepthTexture(internalTexture.format); this._setupDepthStencilTexture(internalTexture, size, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples); this._textureHelper.createGPUTextureForInternalTexture(internalTexture); - // Now that the hardware texture is created, we can retrieve the GPU format and set the right type to the internal texture - const gpuTextureWrapper = internalTexture._hardwareTexture as WebGPUHardwareTexture; - - internalTexture.type = WebGPUTextureHelper.GetTextureTypeFromFormat(gpuTextureWrapper.format); - this._internalTexturesCache.push(internalTexture); return internalTexture; diff --git a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts index 20a8b148c82..4b8b1ccf87c 100644 --- a/packages/dev/core/src/FrameGraph/Passes/renderPass.ts +++ b/packages/dev/core/src/FrameGraph/Passes/renderPass.ts @@ -6,10 +6,10 @@ import { FrameGraphPass } from "./pass"; * Render pass used to render objects. */ export class FrameGraphRenderPass extends FrameGraphPass { - protected _engine: AbstractEngine; + protected readonly _engine: AbstractEngine; protected _renderTarget: FrameGraphTextureHandle | FrameGraphTextureHandle[] | undefined; protected _renderTargetDepth: FrameGraphTextureHandle | undefined; - protected _usedTextures: FrameGraphTextureHandle[] = []; + protected readonly _usedTextures: FrameGraphTextureHandle[] = []; protected _frameGraphRenderTarget: FrameGraphRenderTarget | undefined; /** diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts index 0cf4daa2de8..9ee1f3f9dcb 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/taaObjectRendererTask.ts @@ -81,10 +81,10 @@ export class FrameGraphTAAObjectRendererTask extends FrameGraphObjectRendererTas this._textureWidth = outputTextureDescription.size.width; this._textureHeight = outputTextureDescription.size.height; - const pass = this._frameGraph.addRenderPass(this.name); - let pingPongRenderTargetWrapper: FrameGraphRenderTarget | undefined; + const pass = this._frameGraph.addRenderPass(this.name); + pass.setRenderTarget(this.destinationTexture); pass.setRenderTargetDepth(this.depthTexture); pass.setExecuteFunc((context) => { diff --git a/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts b/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts index b39d2fb4928..dd608f636e3 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphRenderTarget.ts @@ -6,9 +6,9 @@ import type { FrameGraphTextureHandle, FrameGraphTextureManager, IMultiRenderTar * @experimental */ export class FrameGraphRenderTarget { - protected _textureManager: FrameGraphTextureManager; - protected _renderTargets: FrameGraphTextureHandle[] | undefined; - protected _renderTargetDepth: FrameGraphTextureHandle | undefined; + protected readonly _textureManager: FrameGraphTextureManager; + protected readonly _renderTargets: FrameGraphTextureHandle[] | undefined; + protected readonly _renderTargetDepth: FrameGraphTextureHandle | undefined; protected _renderTargetWrapper: RenderTargetWrapper | undefined; protected _isBackBuffer = false; diff --git a/packages/dev/core/src/FrameGraph/frameGraphTask.ts b/packages/dev/core/src/FrameGraph/frameGraphTask.ts index d9b728b9ffc..f7757e57188 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTask.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTask.ts @@ -10,8 +10,8 @@ import { FrameGraphRenderPass } from "./Passes/renderPass"; export abstract class FrameGraphTask { protected readonly _frameGraph: FrameGraph; - private _passes: IFrameGraphPass[] = []; - private _passesDisabled: IFrameGraphPass[] = []; + private readonly _passes: IFrameGraphPass[] = []; + private readonly _passesDisabled: IFrameGraphPass[] = []; // Note: must be a getter/setter even if there's no specific processing, otherwise inherited classes can't make it a getter/setter! // Same thing for the disabled property diff --git a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts index a2134be52ef..833cd7b51bb 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts @@ -67,7 +67,7 @@ export class FrameGraphTextureManager { constructor( public readonly engine: AbstractEngine, private readonly _debugTextures = false, - private _scene: Scene + private readonly _scene: Scene ) { this._addSystemTextures(); } diff --git a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts index b50987c7d4b..c8834225b55 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTypes.ts @@ -16,6 +16,9 @@ export const backbufferColorTextureHandle: FrameGraphTextureHandle = 0; */ export const backbufferDepthStencilTextureHandle: FrameGraphTextureHandle = 1; +/** + * Options used to describe a texture to be created in the frame graph. + */ export type FrameGraphTextureOptions = { /** Specifies if mipmaps must be created for the textures (default: false) */ createMipMaps?: boolean; From 36e4fdcaa6a7e30e802649fa032185a0f0bd1f37 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 22 Nov 2024 18:18:29 +0100 Subject: [PATCH 03/20] Address comments --- .../dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts index 5235f8b2ced..cf8d6eba1cf 100644 --- a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts +++ b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts @@ -45,10 +45,6 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { */ public _currentLOD = 0; - public override get depthStencilTexture() { - return this._depthStencilTexture; - } - public override set depthStencilTexture(texture: Nullable) { this._depthStencilTexture = texture; this._generateDepthBuffer = this._generateStencilBuffer = false; From 64a3715b7373f71b728253d2af4a3deae90dab7c Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 22 Nov 2024 21:13:03 +0100 Subject: [PATCH 04/20] Fix default value for depth texture format --- .../dev/core/src/Engines/Extensions/engine.multiRender.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts index 21025be1295..e8e84c581c6 100644 --- a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts +++ b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts @@ -163,7 +163,7 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o let generateDepthBuffer = true; let generateStencilBuffer = false; let generateDepthTexture = false; - let depthTextureFormat = Constants.TEXTUREFORMAT_DEPTH16; + let depthTextureFormat: number | undefined = undefined; let textureCount = 1; let samples = 1; @@ -216,6 +216,10 @@ ThinEngine.prototype.createMultipleRenderTarget = function (size: TextureSize, o } } + if (depthTextureFormat === undefined) { + depthTextureFormat = generateStencilBuffer ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT; + } + const gl = this._gl; // Create the framebuffer const framebuffer = gl.createFramebuffer(); From 9e28a1f866ec0c793f919a4f81b0dfadfaa41c81 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 22 Nov 2024 23:49:12 +0100 Subject: [PATCH 05/20] Fix getter returning undefined --- .../dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts index cf8d6eba1cf..5235f8b2ced 100644 --- a/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts +++ b/packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts @@ -45,6 +45,10 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper { */ public _currentLOD = 0; + public override get depthStencilTexture() { + return this._depthStencilTexture; + } + public override set depthStencilTexture(texture: Nullable) { this._depthStencilTexture = texture; this._generateDepthBuffer = this._generateStencilBuffer = false; From 046a0feeec0a57c65af52c3e8c912ca5ce4a9820 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Sat, 30 Nov 2024 13:54:11 +0100 Subject: [PATCH 06/20] Add resouce container and shadow generator task --- .../Rendering/baseObjectRendererBlock.ts | 47 +++++- .../Blocks/Rendering/shadowGeneratorBlock.ts | 146 ++++++++++++++++ .../core/src/FrameGraph/Node/Blocks/index.ts | 2 + .../src/FrameGraph/Node/Blocks/inputBlock.ts | 15 +- .../Node/Blocks/resourceContainerBlock.ts | 157 ++++++++++++++++++ .../Node/Types/nodeRenderGraphTypes.ts | 28 ++-- .../nodeRenderGraphBlockConnectionPoint.ts | 29 +++- .../Tasks/Rendering/objectRendererTask.ts | 7 +- .../Tasks/Rendering/shadowGeneratorTask.ts | 155 +++++++++++++++++ .../dev/core/src/FrameGraph/frameGraph.ts | 17 +- .../FrameGraph/frameGraphTextureManager.ts | 5 +- .../src/Lights/Shadows/shadowGenerator.ts | 8 +- .../dev/core/src/Lights/directionalLight.ts | 17 +- packages/dev/core/src/Lights/shadowLight.ts | 12 +- packages/dev/core/src/Lights/spotLight.ts | 8 +- .../Materials/Textures/renderTargetTexture.ts | 27 ++- packages/dev/core/src/Misc/filesInput.ts | 12 +- packages/dev/core/src/scene.ts | 6 +- .../nodeRenderGraphEditor/public/index.js | 1 + .../nodeRenderGraphEditor/src/blockTools.ts | 37 +++++ .../components/nodeList/nodeListComponent.tsx | 17 +- .../src/components/preview/previewManager.ts | 91 +++++++--- .../nodeRenderGraphEditor/src/graphEditor.tsx | 3 + .../src/graphSystem/registerNodePortDesign.ts | 14 ++ 24 files changed, 772 insertions(+), 89 deletions(-) create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts index e9048e2380e..d657ec43953 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts @@ -1,5 +1,4 @@ import type { - NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, @@ -7,11 +6,14 @@ import type { FrameGraphObjectList, Camera, FrameGraphObjectRendererTask, + NodeRenderGraphResourceContainerBlock, + ShadowGenerator, // eslint-disable-next-line import/no-internal-modules } from "core/index"; import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import { NodeRenderGraphConnectionPoint } from "../../nodeRenderGraphBlockConnectionPoint"; /** * @internal @@ -40,13 +42,20 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock this.registerInput("camera", NodeRenderGraphBlockConnectionPointTypes.Camera); this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); this.registerInput("dependencies", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("shadowGenerators", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator, true); this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); this.registerOutput("outputDepth", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput); this.destination.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBufferDepthStencil); this.depth.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment); - this.dependencies.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer); + this.dependencies.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ResourceContainer + ); + this.shadowGenerators.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.ResourceContainer); this.output._typeConnectionSource = this.destination; this.outputDepth._typeConnectionSource = this.depth; @@ -115,6 +124,13 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock return this._inputs[4]; } + /** + * Gets the shadowGenerators input component + */ + public get shadowGenerators(): NodeRenderGraphConnectionPoint { + return this._inputs[5]; + } + /** * Gets the output component */ @@ -162,7 +178,32 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock const dependenciesConnectedPoint = this.dependencies.connectedPoint; if (dependenciesConnectedPoint) { - this._frameGraphTask.dependencies[0] = dependenciesConnectedPoint.value as FrameGraphTextureHandle; + if (dependenciesConnectedPoint.type === NodeRenderGraphBlockConnectionPointTypes.ResourceContainer) { + const container = dependenciesConnectedPoint.ownerBlock as NodeRenderGraphResourceContainerBlock; + container.inputs.forEach((input) => { + if (input.connectedPoint && input.connectedPoint.value !== undefined && NodeRenderGraphConnectionPoint.IsTextureHandle(input.connectedPoint.value)) { + this._frameGraphTask.dependencies!.push(input.connectedPoint.value as FrameGraphTextureHandle); + } + }); + } else { + this._frameGraphTask.dependencies[0] = dependenciesConnectedPoint.value as FrameGraphTextureHandle; + } + } + + this._frameGraphTask.shadowGenerators = []; + + const shadowGeneratorsConnectedPoint = this.shadowGenerators.connectedPoint; + if (shadowGeneratorsConnectedPoint) { + if (shadowGeneratorsConnectedPoint.type === NodeRenderGraphBlockConnectionPointTypes.ResourceContainer) { + const container = shadowGeneratorsConnectedPoint.ownerBlock as NodeRenderGraphResourceContainerBlock; + container.inputs.forEach((input) => { + if (input.connectedPoint && input.connectedPoint.value !== undefined && NodeRenderGraphConnectionPoint.IsShadowGenerator(input.connectedPoint.value)) { + this._frameGraphTask.shadowGenerators!.push(input.connectedPoint.value as ShadowGenerator); + } + }); + } else { + this._frameGraphTask.shadowGenerators[0] = shadowGeneratorsConnectedPoint.value as ShadowGenerator; + } } } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts new file mode 100644 index 00000000000..0b798401292 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts @@ -0,0 +1,146 @@ +// eslint-disable-next-line import/no-internal-modules +import type { NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, IShadowLight, FrameGraphObjectList } from "core/index"; +import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; +import { RegisterClass } from "../../../../Misc/typeStore"; +import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; +import { FrameGraphShadowGeneratorTask } from "../../../Tasks/Rendering/shadowGeneratorTask"; +import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; + +/** + * Block that generate shadows through a shadow generator + */ +export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { + protected override _frameGraphTask: FrameGraphShadowGeneratorTask; + + /** + * Gets the frame graph task associated with this block + */ + public override get task() { + return this._frameGraphTask; + } + + /** + * Create a new NodeRenderGraphShadowGeneratorBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this.registerInput("light", NodeRenderGraphBlockConnectionPointTypes.ShadowLight); + this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); + + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator); + + this._frameGraphTask = new FrameGraphShadowGeneratorTask(this.name, frameGraph, scene); + } + + /** Sets the size of the shadow texture */ + @editableInPropertyPage("Map size", PropertyTypeForEdition.List, "PROPERTIES", { + options: [ + { label: "128", value: 128 }, + { label: "256", value: 256 }, + { label: "512", value: 512 }, + { label: "1024", value: 1024 }, + { label: "2048", value: 2048 }, + { label: "4096", value: 4096 }, + { label: "8192", value: 8192 }, + ], + }) + public get mapSize() { + return this._frameGraphTask.mapSize; + } + + public set mapSize(value: number) { + this._frameGraphTask.mapSize = value; + } + + /** Sets the texture type to float (by default, half float is used if supported) */ + @editableInPropertyPage("Use 32 bits float texture type", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get useFloat32TextureType() { + return this._frameGraphTask.useFloat32TextureType; + } + + public set useFloat32TextureType(value: boolean) { + this._frameGraphTask.useFloat32TextureType = value; + } + + /** Sets the texture type to Red */ + @editableInPropertyPage("Use red texture format", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get useRedTextureFormat() { + return this._frameGraphTask.useRedTextureFormat; + } + + public set useRedTextureFormat(value: boolean) { + this._frameGraphTask.useRedTextureFormat = value; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphShadowGeneratorBlock"; + } + + /** + * Gets the light input component + */ + public get light(): NodeRenderGraphConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the objects input component + */ + public get objects(): NodeRenderGraphConnectionPoint { + return this._inputs[1]; + } + + /** + * Gets the output component + */ + public get output(): NodeRenderGraphConnectionPoint { + return this._outputs[0]; + } + + protected override _buildBlock(state: NodeRenderGraphBuildState) { + super._buildBlock(state); + + const lightConnectedPoint = this.light.connectedPoint; + if (lightConnectedPoint) { + this._frameGraphTask.light = lightConnectedPoint.value as IShadowLight; + } + + const objectsConnectedPoint = this.objects.connectedPoint; + if (objectsConnectedPoint) { + this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; + } + } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.mapSize = ${this.mapSize};`); + codes.push(`${this._codeVariableName}.useFloat32TextureType = ${this.useFloat32TextureType};`); + codes.push(`${this._codeVariableName}.useRedTextureFormat = ${this.useRedTextureFormat};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.mapSize = this.mapSize; + serializationObject.useFloat32TextureType = this.useFloat32TextureType; + serializationObject.useRedTextureFormat = this.useRedTextureFormat; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.mapSize = serializationObject.mapSize; + this.useFloat32TextureType = serializationObject.useFloat32TextureType; + this.useRedTextureFormat = serializationObject.useRedTextureFormat; + } +} + +RegisterClass("BABYLON.NodeRenderGraphShadowGeneratorBlock", NodeRenderGraphShadowGeneratorBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts index d3cc8f99cea..d3a32b7e9e6 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts @@ -1,6 +1,7 @@ export * from "./elbowBlock"; export * from "./inputBlock"; export * from "./outputBlock"; +export * from "./resourceContainerBlock"; export * from "./PostProcesses/blackAndWhitePostProcessBlock"; export * from "./PostProcesses/bloomPostProcessBlock"; @@ -12,6 +13,7 @@ export * from "./PostProcesses/extractHighlightsPostProcessBlock"; export * from "./Rendering/cullObjectsBlock"; export * from "./Rendering/geometryRendererBlock"; export * from "./Rendering/objectRendererBlock"; +export * from "./Rendering/shadowGeneratorBlock"; export * from "./Rendering/taaObjectRendererBlock"; export * from "./Teleport/teleportInBlock"; diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts index df490d4e017..59f6877b157 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts @@ -10,6 +10,7 @@ import type { FrameGraphTextureCreationOptions, FrameGraphTextureHandle, FrameGraphObjectList, + IShadowLight, } from "core/index"; import { Observable } from "../../../Misc/observable"; import { NodeRenderGraphBlockConnectionPointTypes } from "../Types/nodeRenderGraphTypes"; @@ -19,7 +20,7 @@ import { editableInPropertyPage, PropertyTypeForEdition } from "../../../Decorat import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../../FrameGraph/frameGraphTypes"; import { Constants } from "../../../Engines/constants"; -export type NodeRenderGraphValueType = InternalTexture | Camera | FrameGraphObjectList; +export type NodeRenderGraphValueType = InternalTexture | Camera | FrameGraphObjectList | IShadowLight; export type NodeRenderGraphInputCreationOptions = FrameGraphTextureCreationOptions; @@ -211,6 +212,14 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { return (this.type & NodeRenderGraphBlockConnectionPointTypes.ObjectList) !== 0; } + /** + * Check if the block is a shadow light + * @returns true if the block is a shadow light + */ + public isShadowLight(): boolean { + return (this.type & NodeRenderGraphBlockConnectionPointTypes.ShadowLight) !== 0; + } + protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); @@ -223,6 +232,8 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { this.output.value = this.getTypedValue(); } else if (this.isObjectList()) { this.output.value = this.getTypedValue(); + } else if (this.isShadowLight()) { + this.output.value = this.getTypedValue(); } else { if (this._storedValue === undefined || this._storedValue === null) { throw new Error(`NodeRenderGraphInputBlock: External input "${this.name}" is not set`); @@ -265,6 +276,8 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock { codes.push(`${this._codeVariableName}.value = EXTERNAL_CAMERA; // TODO: set the external camera`); } else if (this.isObjectList()) { codes.push(`${this._codeVariableName}.value = EXTERNAL_OBJECT_LIST; // TODO: set the external object list`); + } else if (this.isShadowLight()) { + codes.push(`${this._codeVariableName}.value = EXTERNAL_SHADOW_LIGHT; // TODO: set the external shadow light`); } return super._dumpPropertiesCode() + codes.join("\n"); } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts new file mode 100644 index 00000000000..a0781fd6e63 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts @@ -0,0 +1,157 @@ +// eslint-disable-next-line import/no-internal-modules +import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph, NodeRenderGraphBuildState } from "core/index"; +import { RegisterClass } from "../../../Misc/typeStore"; +import { NodeRenderGraphBlockConnectionPointTypes } from "../Types/nodeRenderGraphTypes"; +import { NodeRenderGraphBlock } from "../nodeRenderGraphBlock"; + +/** + * Block used as a resource (textures, buffers) container + */ +export class NodeRenderGraphResourceContainerBlock extends NodeRenderGraphBlock { + /** + * Creates a new NodeRenderGraphResourceContainerBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this.registerInput("resource0", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource1", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource2", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource3", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource4", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource5", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource6", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + this.registerInput("resource7", NodeRenderGraphBlockConnectionPointTypes.Texture, true); + + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.ResourceContainer); + + this.resource0.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource1.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource2.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource3.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource4.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource5.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource6.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + this.resource7.addAcceptedConnectionPointTypes( + NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer | + NodeRenderGraphBlockConnectionPointTypes.StorageTexture | + NodeRenderGraphBlockConnectionPointTypes.StorageBuffer | + NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator + ); + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphResourceContainerBlock"; + } + + /** + * Gets the resource0 component + */ + public get resource0(): NodeRenderGraphConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the resource1 component + */ + public get resource1(): NodeRenderGraphConnectionPoint { + return this._inputs[1]; + } + + /** + * Gets the resource2 component + */ + public get resource2(): NodeRenderGraphConnectionPoint { + return this._inputs[2]; + } + + /** + * Gets the resource3 component + */ + public get resource3(): NodeRenderGraphConnectionPoint { + return this._inputs[3]; + } + + /** + * Gets the resource4 component + */ + public get resource4(): NodeRenderGraphConnectionPoint { + return this._inputs[4]; + } + + /** + * Gets the resource5 component + */ + public get resource5(): NodeRenderGraphConnectionPoint { + return this._inputs[5]; + } + + /** + * Gets the resource6 component + */ + public get resource6(): NodeRenderGraphConnectionPoint { + return this._inputs[6]; + } + + /** + * Gets the resource7 component + */ + public get resource7(): NodeRenderGraphConnectionPoint { + return this._inputs[7]; + } + + /** + * Gets the output component + */ + public get output(): NodeRenderGraphConnectionPoint { + return this._outputs[0]; + } + + protected override _buildBlock(state: NodeRenderGraphBuildState) { + super._buildBlock(state); + } +} + +RegisterClass("BABYLON.NodeRenderGraphResourceContainerBlock", NodeRenderGraphResourceContainerBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts index 533dee5b8dd..579186ebdda 100644 --- a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts @@ -1,8 +1,5 @@ -import type { Color4 } from "../../../Maths/math.color"; -import type { Scene } from "../../../scene"; -import type { FrameGraphTextureHandle } from "../../../FrameGraph/frameGraphTypes"; -import type { Camera } from "../../../Cameras/camera"; -import type { FrameGraphObjectList } from "core/FrameGraph/frameGraphObjectList"; +// eslint-disable-next-line import/no-internal-modules +import type { Color4, Scene, FrameGraphTextureHandle, Camera, FrameGraphObjectList, IShadowLight, ShadowGenerator } from "core/index"; /** * Interface used to configure the node render graph editor @@ -69,13 +66,24 @@ export enum NodeRenderGraphBlockConnectionPointTypes { TextureLocalPosition = 0x00004000, /** Linear velocity geometry texture */ TextureLinearVelocity = 0x00008000, + /** Linear velocity geometry texture */ + StorageTexture = 0x00010000, /** Bit field for all textures but back buffer depth/stencil */ - TextureAllButBackBufferDepthStencil = 0x00fffffb, - /** Bit field for all textures but back buffer */ - TextureAllButBackBuffer = 0x00fffff9, - TextureAll = 0x00ffffff, + TextureAllButBackBufferDepthStencil = 0x000ffffb, + /** Bit field for all textures but back buffer color and depth/stencil */ + TextureAllButBackBuffer = 0x000ffff9, + /** Bit field for all textures */ + TextureAll = 0x000fffff, + /** Resource container */ + ResourceContainer = 0x00100000, + /** Shadow generator */ + ShadowGenerator = 0x00200000, + /** Light */ + ShadowLight = 0x00400000, + /** Storage Buffer */ + StorageBuffer = 0x00800000, /** Camera */ Camera = 0x01000000, /** List of objects (meshes, particle systems, sprites) */ @@ -116,4 +124,4 @@ export const enum NodeRenderGraphConnectionPointDirection { /** * Defines the type of a connection point value */ -export type NodeRenderGraphBlockConnectionPointValueType = FrameGraphTextureHandle | Camera | FrameGraphObjectList; +export type NodeRenderGraphBlockConnectionPointValueType = FrameGraphTextureHandle | Camera | FrameGraphObjectList | IShadowLight | ShadowGenerator; diff --git a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts index bad59ce79ee..b489fc27d53 100644 --- a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts +++ b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { Nullable, NodeRenderGraphBlock, NodeRenderGraphBlockConnectionPointValueType, NodeRenderGraphInputBlock } from "core/index"; +import type { Nullable, NodeRenderGraphBlock, NodeRenderGraphBlockConnectionPointValueType, NodeRenderGraphInputBlock, IShadowLight, ShadowGenerator } from "core/index"; import { Observable } from "../../Misc/observable"; import { NodeRenderGraphBlockConnectionPointTypes, NodeRenderGraphConnectionPointCompatibilityStates, NodeRenderGraphConnectionPointDirection } from "./Types/nodeRenderGraphTypes"; @@ -31,6 +31,33 @@ export class NodeRenderGraphConnectionPoint { return this._direction; } + /** + * Checks if the value is a texture handle + * @param value The value to check + * @returns True if the value is a texture handle + */ + public static IsTextureHandle(value: NodeRenderGraphBlockConnectionPointValueType): boolean { + return Number.isFinite(value); + } + + /** + * Checks if the value is a shadow generator + * @param value The value to check + * @returns True if the value is a shadow generator + */ + public static IsShadowGenerator(value: NodeRenderGraphBlockConnectionPointValueType): boolean { + return (value as ShadowGenerator).getShadowMap !== undefined; + } + + /** + * Checks if the value is a shadow light + * @param value The value to check + * @returns True if the value is a shadow light + */ + public static IsShadowLight(value: NodeRenderGraphBlockConnectionPointValueType): boolean { + return (value as IShadowLight).setShadowProjectionMatrix !== undefined; + } + /** * The value stored in this connection point */ diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts index a2da695b6d1..116f518ad76 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { FrameGraph, FrameGraphTextureHandle, Scene, Camera, FrameGraphObjectList, FrameGraphRenderContext, ObjectRendererOptions } from "core/index"; +import type { FrameGraph, FrameGraphTextureHandle, Scene, Camera, FrameGraphObjectList, FrameGraphRenderContext, ObjectRendererOptions, ShadowGenerator } from "core/index"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../frameGraphTypes"; import { FrameGraphTask } from "../../frameGraphTask"; import { ObjectRenderer } from "../../../Rendering/objectRenderer"; @@ -23,6 +23,11 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { */ public dependencies?: FrameGraphTextureHandle[] = []; + /** + * The shadow generators used to render the objects (optional). + */ + public shadowGenerators?: ShadowGenerator[] = []; + private _camera: Camera; /** diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts new file mode 100644 index 00000000000..2d0dbfd4b29 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts @@ -0,0 +1,155 @@ +// eslint-disable-next-line import/no-internal-modules +import type { Scene, FrameGraph, FrameGraphObjectList, IShadowLight, WritableObject, AbstractEngine } from "core/index"; +import { FrameGraphTask } from "../../frameGraphTask"; +import { ShadowGenerator } from "../../../Lights/Shadows/shadowGenerator"; + +/** + * Task used to generate shadows from a list of objects. + */ +export class FrameGraphShadowGeneratorTask extends FrameGraphTask { + /** + * The object list to cull. + */ + public objectList: FrameGraphObjectList; + + private _light: IShadowLight; + /** + * The light to generate shadows from. + */ + public get light(): IShadowLight { + return this._light; + } + + public set light(value: IShadowLight) { + if (value === this._light) { + return; + } + + this._light = value; + this._createShadowGenerator(); + } + + private _mapSize = 1024; + /** + * The size of the shadow map. + */ + public get mapSize() { + return this._mapSize; + } + + public set mapSize(value: number) { + if (value === this._mapSize) { + return; + } + + this._mapSize = value; + this._createShadowGenerator(); + } + + private _useFloat32TextureType = false; + /** + * If true, the shadow map will use a 32 bits float texture type (else, 16 bits float is used if supported). + */ + public get useFloat32TextureType() { + return this._useFloat32TextureType; + } + + public set useFloat32TextureType(value: boolean) { + if (value === this._useFloat32TextureType) { + return; + } + + this._useFloat32TextureType = value; + this._createShadowGenerator(); + } + + private _useRedTextureFormat = true; + /** + * If true, the shadow map will use a red texture format (else, a RGBA format is used). + */ + public get useRedTextureFormat() { + return this._useRedTextureFormat; + } + + public set useRedTextureFormat(value: boolean) { + if (value === this._useRedTextureFormat) { + return; + } + + this._useRedTextureFormat = value; + this._createShadowGenerator(); + } + + /** + * The output shadow generator. + */ + public readonly outputShadowGenerator: ShadowGenerator; + + private _shadowGenerator: ShadowGenerator | undefined; + + private _createShadowGenerator() { + this._shadowGenerator?.dispose(); + this._shadowGenerator = undefined; + if (this._light !== undefined) { + this._shadowGenerator = new ShadowGenerator(this._mapSize, this._light, this._useFloat32TextureType, undefined, this._useRedTextureFormat); + this._shadowGenerator.getShadowMap()!._disableEngineStages = true; + (this.outputShadowGenerator as WritableObject) = this._shadowGenerator; + } + } + + public override isReady(): boolean { + return !!this._shadowGenerator && !!this._shadowGenerator.getShadowMap()?.isReadyForRendering(); + } + + private _engine: AbstractEngine; + + /** + * Creates a new shadow generator task. + * @param name The name of the task. + * @param frameGraph The frame graph the task belongs to. + * @param scene The scene to create the shadow generator for. + */ + constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph); + + this._engine = scene.getEngine(); + } + + public record() { + if (this.light === undefined || this.objectList === undefined) { + throw new Error(`FrameGraphShadowGeneratorTask ${this.name}: light and objectList are required`); + } + + const pass = this._frameGraph.addPass(this.name); + + pass.setExecuteFunc((_context) => { + const shadowMap = this._shadowGenerator!.getShadowMap()!; + + shadowMap.renderList = this.objectList.meshes; + shadowMap.particleSystemList = this.objectList.particleSystems; + + const currentRenderTarget = this._engine._currentRenderTarget; + + if (this.light.isEnabled() && this.light.shadowEnabled) { + shadowMap.render(); + } + + if (this._engine._currentRenderTarget !== currentRenderTarget) { + if (!currentRenderTarget) { + this._engine.restoreDefaultFramebuffer(); + } else { + this._engine.bindFramebuffer(currentRenderTarget); + } + } + }); + + const passDisabled = this._frameGraph.addPass(this.name + "_disabled", true); + + passDisabled.setExecuteFunc((_context) => {}); + } + + public override dispose() { + this._shadowGenerator?.dispose(); + this._shadowGenerator = undefined; + } +} diff --git a/packages/dev/core/src/FrameGraph/frameGraph.ts b/packages/dev/core/src/FrameGraph/frameGraph.ts index 692245d0937..79b920c6a9c 100644 --- a/packages/dev/core/src/FrameGraph/frameGraph.ts +++ b/packages/dev/core/src/FrameGraph/frameGraph.ts @@ -9,9 +9,10 @@ import { FrameGraphTextureManager } from "./frameGraphTextureManager"; import { Observable } from "core/Misc/observable"; enum FrameGraphPassType { - Render = 0, - Cull = 1, - Compute = 2, + Normal = 0, + Render = 1, + Cull = 2, + Compute = 3, } /** @@ -76,6 +77,16 @@ export class FrameGraph { this._tasks.push(task); } + /** + * Adds a pass to a task. This method can only be called during a Task.record execution. + * @param name The name of the pass + * @param whenTaskDisabled If true, the pass will be added to the list of passes to execute when the task is disabled (default is false) + * @returns The render pass created + */ + public addPass(name: string, whenTaskDisabled = false): FrameGraphRenderPass { + return this._addPass(name, FrameGraphPassType.Normal, whenTaskDisabled) as FrameGraphRenderPass; + } + /** * Adds a render pass to a task. This method can only be called during a Task.record execution. * @param name The name of the pass diff --git a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts index 833cd7b51bb..fcc90b66b8e 100644 --- a/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts +++ b/packages/dev/core/src/FrameGraph/frameGraphTextureManager.ts @@ -566,7 +566,10 @@ export class FrameGraphTextureManager { const textureName = creationOptions.isHistoryTexture ? `${name} ping` : name; - const label = creationOptions.options.labels?.[textureIndex] ?? ""; + let label = creationOptions.options.labels?.[textureIndex] ?? ""; + if (label === textureName) { + label = ""; + } const textureEntry: TextureEntry = { texture, diff --git a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts index 28c921eb0ec..54887eb198e 100644 --- a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts +++ b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts @@ -1288,9 +1288,7 @@ export class ShadowGenerator implements IShadowGenerator { } const camera = this._getCamera(); - if (camera) { - effect.setFloat2("depthValuesSM", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera)); - } + effect.setFloat2("depthValuesSM", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera)); if (isTransparent && this.enableSoftTransparentShadow) { effect.setFloat2("softTransparentShadowSM", effectiveMesh.visibility * material.alpha, this._opacityTexture?.getAlphaFromRGB ? 1 : 0); @@ -1830,10 +1828,6 @@ export class ShadowGenerator implements IShadowGenerator { } const camera = this._getCamera(); - if (!camera) { - return; - } - const shadowMap = this.getShadowMap(); if (!shadowMap) { diff --git a/packages/dev/core/src/Lights/directionalLight.ts b/packages/dev/core/src/Lights/directionalLight.ts index bb1847bfe75..e6485dc6361 100644 --- a/packages/dev/core/src/Lights/directionalLight.ts +++ b/packages/dev/core/src/Lights/directionalLight.ts @@ -8,6 +8,7 @@ import { Light } from "./light"; import { ShadowLight } from "./shadowLight"; import type { Effect } from "../Materials/effect"; import { RegisterClass } from "../Misc/typeStore"; +import type { Nullable } from "../types"; Node.AddNodeConstructor("Light_Type_1", (name, scene) => { return () => new DirectionalLight(name, Vector3.Zero(), scene); @@ -202,10 +203,6 @@ export class DirectionalLight extends ShadowLight { protected _setDefaultAutoExtendShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array): void { const activeCamera = this.getScene().activeCamera; - if (!activeCamera) { - return; - } - // Check extends if (this.autoUpdateExtends || this._orthoLeft === Number.MAX_VALUE) { const tempVector3 = Vector3.Zero(); @@ -264,8 +261,8 @@ export class DirectionalLight extends ShadowLight { const xOffset = this._orthoRight - this._orthoLeft; const yOffset = this._orthoTop - this._orthoBottom; - const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ; - const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ; + const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; + const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; const useReverseDepthBuffer = this.getScene().getEngine().useReverseDepthBuffer; @@ -321,11 +318,11 @@ export class DirectionalLight extends ShadowLight { * Values are fixed on directional lights as it relies on an ortho projection hence the need to convert being * -1 and 1 to 0 and 1 doing (depth + min) / (min + max) -> (depth + 1) / (1 + 1) -> (depth * 0.5) + 0.5. * (when not using reverse depth buffer / NDC half Z range) - * @param activeCamera The camera we are returning the min for + * @param _activeCamera The camera we are returning the min for (not used) * @returns the depth min z */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - public override getDepthMinZ(activeCamera: Camera): number { + public override getDepthMinZ(_activeCamera: Nullable): number { const engine = this._scene.getEngine(); return !engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1; } @@ -336,11 +333,11 @@ export class DirectionalLight extends ShadowLight { * Values are fixed on directional lights as it relies on an ortho projection hence the need to convert being * -1 and 1 to 0 and 1 doing (depth + min) / (min + max) -> (depth + 1) / (1 + 1) -> (depth * 0.5) + 0.5. * (when not using reverse depth buffer / NDC half Z range) - * @param activeCamera The camera we are returning the max for + * @param _activeCamera The camera we are returning the max for * @returns the depth max z */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - public override getDepthMaxZ(activeCamera: Camera): number { + public override getDepthMaxZ(_activeCamera: Nullable): number { const engine = this._scene.getEngine(); return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1; } diff --git a/packages/dev/core/src/Lights/shadowLight.ts b/packages/dev/core/src/Lights/shadowLight.ts index 517355b8027..7081bf3cc85 100644 --- a/packages/dev/core/src/Lights/shadowLight.ts +++ b/packages/dev/core/src/Lights/shadowLight.ts @@ -107,14 +107,14 @@ export interface IShadowLight extends Light { * @param activeCamera The camera we are returning the min for * @returns the depth min z */ - getDepthMinZ(activeCamera: Camera): number; + getDepthMinZ(activeCamera: Nullable): number; /** * Gets the maxZ used for shadow according to both the scene and the light. * @param activeCamera The camera we are returning the max for * @returns the depth max z */ - getDepthMaxZ(activeCamera: Camera): number; + getDepthMaxZ(activeCamera: Nullable): number; } /** @@ -360,8 +360,8 @@ export abstract class ShadowLight extends Light implements IShadowLight { * @param activeCamera The camera we are returning the min for * @returns the depth min z */ - public getDepthMinZ(activeCamera: Camera): number { - return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ; + public getDepthMinZ(activeCamera: Nullable): number { + return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; } /** @@ -369,8 +369,8 @@ export abstract class ShadowLight extends Light implements IShadowLight { * @param activeCamera The camera we are returning the max for * @returns the depth max z */ - public getDepthMaxZ(activeCamera: Camera): number { - return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ; + public getDepthMaxZ(activeCamera: Nullable): number { + return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; } /** diff --git a/packages/dev/core/src/Lights/spotLight.ts b/packages/dev/core/src/Lights/spotLight.ts index 30b3fed8d9c..57c33f0c3f8 100644 --- a/packages/dev/core/src/Lights/spotLight.ts +++ b/packages/dev/core/src/Lights/spotLight.ts @@ -446,9 +446,9 @@ export class SpotLight extends ShadowLight { * @param activeCamera The camera we are returning the min for * @returns the depth min z */ - public override getDepthMinZ(activeCamera: Camera): number { + public override getDepthMinZ(activeCamera: Nullable): number { const engine = this._scene.getEngine(); - const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ; + const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? minZ : this._scene.getEngine().isNDCHalfZRange ? 0 : minZ; } @@ -458,9 +458,9 @@ export class SpotLight extends ShadowLight { * @param activeCamera The camera we are returning the max for * @returns the depth max z */ - public override getDepthMaxZ(activeCamera: Camera): number { + public override getDepthMaxZ(activeCamera: Nullable): number { const engine = this._scene.getEngine(); - const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ; + const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : maxZ; } diff --git a/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts b/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts index b28a6135eb2..e3c6af20821 100644 --- a/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts +++ b/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts @@ -492,6 +492,9 @@ export class RenderTargetTexture extends Texture implements IRenderTargetTexture return this._renderTarget?._depthStencilTexture ?? null; } + /** @internal */ + public _disableEngineStages = false; + /** * Instantiate a render target texture. This is mainly used to render of screen the scene to for instance apply post process * or used a shadow, depth texture... @@ -609,8 +612,10 @@ export class RenderTargetTexture extends Texture implements IRenderTargetTexture this._objectRenderer.onBeforeRenderingManagerRenderObservable.add(() => { // Before clear - for (const step of this._scene!._beforeRenderTargetClearStage) { - step.action(this, this._currentFaceIndex, this._currentLayer); + if (!this._disableEngineStages) { + for (const step of this._scene!._beforeRenderTargetClearStage) { + step.action(this, this._currentFaceIndex, this._currentLayer); + } } // Clear @@ -625,15 +630,19 @@ export class RenderTargetTexture extends Texture implements IRenderTargetTexture } // Before Camera Draw - for (const step of this._scene!._beforeRenderTargetDrawStage) { - step.action(this, this._currentFaceIndex, this._currentLayer); + if (!this._disableEngineStages) { + for (const step of this._scene!._beforeRenderTargetDrawStage) { + step.action(this, this._currentFaceIndex, this._currentLayer); + } } }); this._objectRenderer.onAfterRenderingManagerRenderObservable.add(() => { // After Camera Draw - for (const step of this._scene!._afterRenderTargetDrawStage) { - step.action(this, this._currentFaceIndex, this._currentLayer); + if (!this._disableEngineStages) { + for (const step of this._scene!._afterRenderTargetDrawStage) { + step.action(this, this._currentFaceIndex, this._currentLayer); + } } const saveGenerateMipMaps = this._texture?.generateMipMaps ?? false; @@ -650,8 +659,10 @@ export class RenderTargetTexture extends Texture implements IRenderTargetTexture this._scene!.postProcessManager._finalizeFrame(false, this._renderTarget ?? undefined, this._currentFaceIndex); } - for (const step of this._scene!._afterRenderTargetPostProcessStage) { - step.action(this, this._currentFaceIndex, this._currentLayer); + if (!this._disableEngineStages) { + for (const step of this._scene!._afterRenderTargetPostProcessStage) { + step.action(this, this._currentFaceIndex, this._currentLayer); + } } if (this._texture) { diff --git a/packages/dev/core/src/Misc/filesInput.ts b/packages/dev/core/src/Misc/filesInput.ts index a35cd056162..2d348e1f9cf 100644 --- a/packages/dev/core/src/Misc/filesInput.ts +++ b/packages/dev/core/src/Misc/filesInput.ts @@ -65,6 +65,7 @@ export class FilesInput { * @param onReloadCallback callback called when a reload is requested * @param errorCallback callback call if an error occurs * @param useAppend defines if the file loaded must be appended (true) or have the scene replaced (false, default behavior) + * @param dontInjectRenderLoop defines if the render loop mustn't be injected into engine (default is false). Used only if useAppend is false. */ constructor( engine: AbstractEngine, @@ -76,7 +77,8 @@ export class FilesInput { startingProcessingFilesCallback: Nullable<(files?: File[]) => void>, onReloadCallback: Nullable<(sceneFile: File) => void>, errorCallback: Nullable<(sceneFile: File, scene: Nullable, message: string) => void>, - public readonly useAppend = false + public readonly useAppend = false, + public readonly dontInjectRenderLoop = false ) { this._engine = engine; this._currentScene = scene; @@ -324,9 +326,11 @@ export class FilesInput { if (this.displayLoadingUI) { this._engine.hideLoadingUI(); } - this._engine.runRenderLoop(() => { - this._renderFunction(); - }); + if (!this.dontInjectRenderLoop) { + this._engine.runRenderLoop(() => { + this._renderFunction(); + }); + } }); } else { if (this.displayLoadingUI) { diff --git a/packages/dev/core/src/scene.ts b/packages/dev/core/src/scene.ts index 0f3aadab800..20e2964b079 100644 --- a/packages/dev/core/src/scene.ts +++ b/packages/dev/core/src/scene.ts @@ -1124,7 +1124,11 @@ export class Scene implements IAnimatable, IClipPlanesHolder, IAssetContainer { * @returns the computed eye position */ public bindEyePosition(effect: Nullable, variableName = "vEyePosition", isVector3 = false): Vector4 { - const eyePosition = this._forcedViewPosition ? this._forcedViewPosition : this._mirroredCameraPosition ? this._mirroredCameraPosition : this.activeCamera!.globalPosition; + const eyePosition = this._forcedViewPosition + ? this._forcedViewPosition + : this._mirroredCameraPosition + ? this._mirroredCameraPosition + : (this.activeCamera?.globalPosition ?? Vector3.ZeroReadOnly); const invertNormal = this.useRightHandedSystem === (this._mirroredCameraPosition != null); diff --git a/packages/tools/nodeRenderGraphEditor/public/index.js b/packages/tools/nodeRenderGraphEditor/public/index.js index 5d344a5f227..6d79026d840 100644 --- a/packages/tools/nodeRenderGraphEditor/public/index.js +++ b/packages/tools/nodeRenderGraphEditor/public/index.js @@ -199,6 +199,7 @@ checkBabylonVersionAsync().then(() => { let scene = new BABYLON.Scene(engine); new BABYLON.Camera("camera", new BABYLON.Vector3(0, 0, 0), scene); new BABYLON.HemisphericLight("light #0", new BABYLON.Vector3(0, 1, 0), scene); + new BABYLON.DirectionalLight("light #1", new BABYLON.Vector3(0, 1, 0), scene); nodeRenderGraph = new BABYLON.NodeRenderGraph("node", scene); nodeRenderGraph.setToDefault(); diff --git a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts index 336afdb6f52..3816f4dbb3f 100644 --- a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts +++ b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts @@ -19,6 +19,8 @@ import { NodeRenderGraphGeometryRendererBlock } from "core/FrameGraph/Node/Block import { NodeRenderGraphCullObjectsBlock } from "core/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock"; import { NodeRenderGraphGUIBlock } from "gui/2D/FrameGraph/renderGraphGUIBlock"; import { NodeRenderGraphTAAObjectRendererBlock } from "core/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock"; +import { NodeRenderGraphResourceContainerBlock } from "core/FrameGraph/Node/Blocks/resourceContainerBlock"; +import { NodeRenderGraphShadowGeneratorBlock } from "core/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock"; import type { FrameGraph } from "core/FrameGraph/frameGraph"; /** @@ -35,6 +37,8 @@ export class BlockTools { return new NodeRenderGraphOutputBlock("Output", frameGraph, scene); case "ElbowBlock": return new NodeRenderGraphElbowBlock("", frameGraph, scene); + case "ResourceContainerBlock": + return new NodeRenderGraphResourceContainerBlock("Resources", frameGraph, scene); case "TextureBlock": { return new NodeRenderGraphInputBlock("Texture", frameGraph, scene, NodeRenderGraphBlockConnectionPointTypes.Texture); } @@ -58,6 +62,9 @@ export class BlockTools { case "ObjectListBlock": { return new NodeRenderGraphInputBlock("Object list", frameGraph, scene, NodeRenderGraphBlockConnectionPointTypes.ObjectList); } + case "ShadowLightBlock": { + return new NodeRenderGraphInputBlock("Shadow light", frameGraph, scene, NodeRenderGraphBlockConnectionPointTypes.ShadowLight); + } case "ClearBlock": { return new NodeRenderGraphClearBlock("Clear", frameGraph, scene); } @@ -100,6 +107,9 @@ export class BlockTools { case "ExtractHighlightsBlock": { return new NodeRenderGraphExtractHighlightsPostProcessBlock("Extract Highlights", frameGraph, scene); } + case "ShadowGeneratorBlock": { + return new NodeRenderGraphShadowGeneratorBlock("Shadow Generator", frameGraph, scene); + } } return null; @@ -156,6 +166,13 @@ export class BlockTools { case NodeRenderGraphBlockConnectionPointTypes.TextureLinearVelocity: color = "#c451e5"; break; + case NodeRenderGraphBlockConnectionPointTypes.StorageTexture: + case NodeRenderGraphBlockConnectionPointTypes.ResourceContainer: + case NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator: + case NodeRenderGraphBlockConnectionPointTypes.ShadowLight: + case NodeRenderGraphBlockConnectionPointTypes.StorageBuffer: + color = "#000000"; + break; case NodeRenderGraphBlockConnectionPointTypes.BasedOnInput: color = "#f28e0a"; // Used by the teleport blocks break; @@ -203,6 +220,16 @@ export class BlockTools { return NodeRenderGraphBlockConnectionPointTypes.TextureWorldNormal; case "TextureLinearVelocity": return NodeRenderGraphBlockConnectionPointTypes.TextureLinearVelocity; + case "StorageTexture": + return NodeRenderGraphBlockConnectionPointTypes.StorageTexture; + case "ResourceContainer": + return NodeRenderGraphBlockConnectionPointTypes.ResourceContainer; + case "ShadowGenerator": + return NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator; + case "ShadowLight": + return NodeRenderGraphBlockConnectionPointTypes.ShadowLight; + case "StorageBuffer": + return NodeRenderGraphBlockConnectionPointTypes.StorageBuffer; } return NodeRenderGraphBlockConnectionPointTypes.AutoDetect; @@ -242,6 +269,16 @@ export class BlockTools { return "TextureWorldNormal"; case NodeRenderGraphBlockConnectionPointTypes.TextureLinearVelocity: return "TextureLinearVelocity"; + case NodeRenderGraphBlockConnectionPointTypes.StorageTexture: + return "StorageTexture"; + case NodeRenderGraphBlockConnectionPointTypes.ResourceContainer: + return "ResourceContainer"; + case NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator: + return "ShadowGenerator"; + case NodeRenderGraphBlockConnectionPointTypes.ShadowLight: + return "ShadowLight"; + case NodeRenderGraphBlockConnectionPointTypes.StorageBuffer: + return "StorageBuffer"; } return ""; diff --git a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx index d76459337f9..259a597d789 100644 --- a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx @@ -46,6 +46,9 @@ export class NodeListComponent extends React.Component { this._prepareLights(); + this._createNodeRenderGraph(); }); this._onUpdateRequiredObserver = globalState.stateManager.onUpdateRequiredObservable.add(() => { @@ -120,13 +122,15 @@ export class PreviewManager { this._scene = scene; + this._prepareBackgroundHDR(); + this._globalState.filesInput?.dispose(); this._globalState.filesInput = new FilesInput( this._engine, null, (_, scene) => { + this._scene.dispose(); this._initScene(scene); - this._prepareScene(); }, null, null, @@ -136,7 +140,8 @@ export class PreviewManager { () => { this._reset(); }, - false + false, + true ); this._lightParent = new TransformNode("LightParent", this._scene); @@ -150,6 +155,8 @@ export class PreviewManager { } }); + this._prepareScene(); + this._createNodeRenderGraph(); } @@ -181,6 +188,9 @@ export class PreviewManager { dir0.diffuse = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549); dir0.specular = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549); dir0.parent = this._lightParent; + dir0.shadowMinZ = 0; + dir0.shadowMaxZ = 3; + dir0.position.scaleInPlace(1.5); } if (this._globalState.directionalLight1) { @@ -189,7 +199,14 @@ export class PreviewManager { dir1.specular = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432); dir1.diffuse = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432); dir1.parent = this._lightParent; + dir1.shadowMinZ = 0; + dir1.shadowMaxZ = 3; + dir1.position.scaleInPlace(1.5); } + + this._scene.meshes.forEach((m) => { + m.receiveShadows = true; + }); } private _createNodeRenderGraph() { @@ -236,6 +253,15 @@ export class PreviewManager { this._scene.cameras.length = 0; this._scene.cameraToUseForPointers = null; + const directionalLights: DirectionalLight[] = []; + for (const light of this._scene.lights) { + if (light instanceof DirectionalLight) { + directionalLights.push(light); + } + } + + let curLightIndex = 0; + // Set default external inputs const allInputs = this._nodeRenderGraph.getInputBlocks(); for (const input of allInputs) { @@ -268,6 +294,11 @@ export class PreviewManager { input.value = camera; } else if (input.isObjectList()) { input.value = { meshes: this._scene.meshes, particleSystems: this._scene.particleSystems }; + } else if (input.isShadowLight()) { + if (curLightIndex < directionalLights.length) { + input.value = directionalLights[curLightIndex++]; + curLightIndex = curLightIndex % directionalLights.length; + } } } @@ -350,41 +381,54 @@ export class PreviewManager { } private _prepareBackgroundHDR() { - this._scene.environmentTexture = this._hdrTexture; - } - - private _prepareScene() { - this._globalState.onIsLoadingChanged.notifyObservers(false); - - this._prepareLights(); + this._hdrTexture = null as any; - this._frameCamera(); - this._prepareBackgroundHDR(); - } - - public static DefaultEnvironmentURL = "https://assets.babylonjs.com/environments/environmentSpecular.env"; + let newHDRTexture: Nullable = null; - private _refreshPreviewMesh(force?: boolean) { switch (this._globalState.envType) { case PreviewType.Room: - this._hdrTexture = new CubeTexture(PreviewManager.DefaultEnvironmentURL, this._scene); - if (this._hdrTexture) { - this._prepareBackgroundHDR(); - } + newHDRTexture = new CubeTexture(PreviewManager.DefaultEnvironmentURL, this._scene); break; case PreviewType.Custom: { const blob = new Blob([this._globalState.envFile], { type: "octet/stream" }); const reader = new FileReader(); reader.onload = (evt) => { const dataurl = evt.target!.result as string; - this._hdrTexture = new CubeTexture(dataurl, this._scene, undefined, false, undefined, undefined, undefined, undefined, undefined, ".env"); - this._prepareBackgroundHDR(); + newHDRTexture = new CubeTexture(dataurl, this._scene, undefined, false, undefined, undefined, undefined, undefined, undefined, ".env"); }; reader.readAsDataURL(blob); break; } } + if (!newHDRTexture) { + return; + } + + this._hdrTexture = newHDRTexture; + + newHDRTexture.onLoadObservable.add(() => { + if (this._hdrTexture !== newHDRTexture) { + // The HDR texture has been changed in the meantime, so we don't need this one anymore + newHDRTexture!.dispose(); + } + }); + + this._hdrTexture = newHDRTexture; + this._scene.environmentTexture = this._hdrTexture; + } + + private _prepareScene() { + this._globalState.onIsLoadingChanged.notifyObservers(false); + + this._prepareLights(); + + this._frameCamera(); + } + + public static DefaultEnvironmentURL = "https://assets.babylonjs.com/environments/environmentSpecular.env"; + + private _refreshPreviewMesh(force?: boolean) { if (this._currentType === this._globalState.previewType && this._currentType !== PreviewType.Custom && !force) { return; } @@ -399,32 +443,27 @@ export class PreviewManager { case PreviewType.Box: SceneLoader.LoadAsync("https://assets.babylonjs.com/meshes/", "roundedCube.glb").then((scene) => { this._initScene(scene); - this._prepareScene(); }); return; case PreviewType.Sphere: SceneLoader.LoadAsync("https://assets.babylonjs.com/meshes/", "previewSphere.glb").then((scene) => { this._initScene(scene); - this._prepareScene(); }); break; case PreviewType.Cylinder: SceneLoader.LoadAsync("https://assets.babylonjs.com/meshes/", "roundedCylinder.glb").then((scene) => { this._initScene(scene); - this._prepareScene(); }); return; case PreviewType.Plane: { SceneLoader.LoadAsync("https://assets.babylonjs.com/meshes/", "highPolyPlane.glb").then((scene) => { this._initScene(scene); - this._prepareScene(); }); break; } case PreviewType.ShaderBall: SceneLoader.LoadAsync("https://assets.babylonjs.com/meshes/", "shaderBall.glb").then((scene) => { this._initScene(scene); - this._prepareScene(); }); return; case PreviewType.Custom: diff --git a/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx b/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx index b0707bb8f37..bca1bef1e32 100644 --- a/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx +++ b/packages/tools/nodeRenderGraphEditor/src/graphEditor.tsx @@ -31,6 +31,7 @@ import type { InternalTexture } from "core/Materials/Textures/internalTexture"; import { SplitContainer } from "shared-ui-components/split/splitContainer"; import { Splitter } from "shared-ui-components/split/splitter"; import { ControlledSize, SplitDirection } from "shared-ui-components/split/splitContext"; +import type { IShadowLight } from "core/Lights"; interface IGraphEditorProps { globalState: GlobalState; @@ -268,6 +269,8 @@ export class GraphEditor extends React.Component { img.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMSAyMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5WZWN0b3IzPC90aXRsZT48ZyBpZD0iTGF5ZXJfNSIgZGF0YS1uYW1lPSJMYXllciA1Ij48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0zLjU3LDEzLjMxLDkuNSw5Ljg5VjNBNy41MSw3LjUxLDAsMCwwLDMsMTAuNDYsNy4zMiw3LjMyLDAsMCwwLDMuNTcsMTMuMzFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMTYuNDMsMTUsMTAuNSwxMS42Miw0LjU3LDE1YTcuNDgsNy40OCwwLDAsMCwxMS44NiwwWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE4LDEwLjQ2QTcuNTEsNy41MSwwLDAsMCwxMS41LDNWOS44OWw1LjkzLDMuNDJBNy4zMiw3LjMyLDAsMCwwLDE4LDEwLjQ2WiIvPjwvZz48L3N2Zz4="; break; + case NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator: + img.src = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMSAyMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5NYXRyaXg8L3RpdGxlPjxnIGlkPSJMYXllcl81IiBkYXRhLW5hbWU9IkxheWVyIDUiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjUsNi4xMVY5LjVoMy4zOUE0LjUxLDQuNTEsMCwwLDAsMTEuNSw2LjExWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjUsMTQuODlhNC41MSw0LjUxLDAsMCwwLDMuMzktMy4zOUgxMS41WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjUsMy4wN3YyQTUuNTQsNS41NCwwLDAsMSwxNS45Miw5LjVoMkE3LjUxLDcuNTEsMCwwLDAsMTEuNSwzLjA3WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTE1LjkyLDExLjVhNS41NCw1LjU0LDAsMCwxLTQuNDIsNC40MnYyYTcuNTEsNy41MSwwLDAsMCw2LjQzLTYuNDNaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNS4wOCwxMS41aC0yQTcuNTEsNy41MSwwLDAsMCw5LjUsMTcuOTN2LTJBNS41NCw1LjU0LDAsMCwxLDUuMDgsMTEuNVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik05LjUsMy4wN0E3LjUxLDcuNTEsMCwwLDAsMy4wNyw5LjVoMkE1LjU0LDUuNTQsMCwwLDEsOS41LDUuMDhaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNOS41LDExLjVINi4xMUE0LjUxLDQuNTEsMCwwLDAsOS41LDE0Ljg5WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTkuNSw2LjExQTQuNTEsNC41MSwwLDAsMCw2LjExLDkuNUg5LjVaIi8+PC9nPjwvc3ZnPg=="; + break; + case NodeRenderGraphBlockConnectionPointTypes.ShadowLight: + img.src = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMSAyMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5WZWN0b3IxPC90aXRsZT48ZyBpZD0iTGF5ZXJfNSIgZGF0YS1uYW1lPSJMYXllciA1Ij48Y2lyY2xlIGNsYXNzPSJjbHMtMSIgY3g9IjEwLjUiIGN5PSIxMC41IiByPSI3LjUiLz48L2c+PC9zdmc+"; + break; case NodeRenderGraphBlockConnectionPointTypes.TextureBackBufferDepthStencilAttachment: case NodeRenderGraphBlockConnectionPointTypes.TextureBackBuffer: case NodeRenderGraphBlockConnectionPointTypes.Texture: @@ -37,6 +45,12 @@ export const RegisterNodePortDesign = (stateManager: StateManager) => { img.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMSAyMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5WZWN0b3IxPC90aXRsZT48ZyBpZD0iTGF5ZXJfNSIgZGF0YS1uYW1lPSJMYXllciA1Ij48Y2lyY2xlIGNsYXNzPSJjbHMtMSIgY3g9IjEwLjUiIGN5PSIxMC41IiByPSI3LjUiLz48L2c+PC9zdmc+"; break; + case NodeRenderGraphBlockConnectionPointTypes.ResourceContainer: + img.src = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMSIgaGVpZ2h0PSIyMSIgdmlld0JveD0iMCAwIDIxIDIxIj48Y2lyY2xlIGN4PSI3LjEiIGN5PSIxMy4wOCIgcj0iMy4yNSIgc3R5bGU9ImZpbGw6I2ZmZiIvPjxwYXRoIGQ9Ik0xMC40OSwzQTcuNTIsNy41MiwwLDAsMCwzLDEwYTUuMTMsNS4xMywwLDEsMSw2LDcuODUsNy42MSw3LjYxLDAsMCwwLDEuNTIuMTYsNy41Miw3LjUyLDAsMCwwLDAtMTVaIiBzdHlsZT0iZmlsbDojZmZmIi8+PC9zdmc+"; + img.style.width = "100%"; // it's so that the svg is correctly centered inside the outer circle + img.style.height = "100%"; + break; } }; }; From 11a55642e304e5df31818634e46a42012df92b50 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Sat, 30 Nov 2024 15:29:05 +0100 Subject: [PATCH 07/20] Calculate a suitable light position for better shadows --- .../src/components/preview/previewManager.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts b/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts index 0c9ea62bc49..99320a37aa8 100644 --- a/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts +++ b/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts @@ -23,9 +23,11 @@ import { FilesInput } from "core/Misc/filesInput"; import { Color3 } from "core/Maths/math.color"; import { WebGPUEngine } from "core/Engines/webgpuEngine"; import { NodeRenderGraphBlockConnectionPointTypes } from "core/FrameGraph/Node/Types/nodeRenderGraphTypes"; +import { BoundingBox } from "core/Culling/boundingBox"; const useWebGPU = false; const debugTextures = false; +const logErrorTrace = true; export class PreviewManager { private _nodeRenderGraph: NodeRenderGraph; @@ -182,6 +184,14 @@ export class PreviewManager { new HemisphericLight("Hemispheric light", new Vector3(0, 1, 0), this._scene); } + const worldExtends = this._scene.getWorldExtends(); + const diag = worldExtends.max.subtract(worldExtends.min).length(); + + const findLightPosition = (lightDir: Vector3) => { + const bb = new BoundingBox(worldExtends.min, worldExtends.max); + return bb.center.add(lightDir.scale(-diag * 0.5)); + }; + if (this._globalState.directionalLight0) { const dir0 = new DirectionalLight("Directional light #0", new Vector3(0.841626576496605, -0.2193391004130599, -0.49351298337996535), this._scene); dir0.intensity = 0.9; @@ -189,8 +199,8 @@ export class PreviewManager { dir0.specular = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549); dir0.parent = this._lightParent; dir0.shadowMinZ = 0; - dir0.shadowMaxZ = 3; - dir0.position.scaleInPlace(1.5); + dir0.shadowMaxZ = diag; + dir0.position = findLightPosition(dir0.direction); } if (this._globalState.directionalLight1) { @@ -200,8 +210,8 @@ export class PreviewManager { dir1.diffuse = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432); dir1.parent = this._lightParent; dir1.shadowMinZ = 0; - dir1.shadowMaxZ = 3; - dir1.position.scaleInPlace(1.5); + dir1.shadowMaxZ = diag; + dir1.position = findLightPosition(dir1.direction); } this._scene.meshes.forEach((m) => { @@ -351,6 +361,9 @@ export class PreviewManager { await this._nodeRenderGraph.whenReadyAsync(); this._scene.frameGraph = this._nodeRenderGraph.frameGraph; } catch (err) { + if (logErrorTrace) { + (console as any).log(err); + } this._globalState.onLogRequiredObservable.notifyObservers(new LogEntry("From preview manager: " + err, true)); } } From a0f613a82b0dd7ead83e49f16dc739892163853e Mon Sep 17 00:00:00 2001 From: Popov72 Date: Sat, 30 Nov 2024 15:29:20 +0100 Subject: [PATCH 08/20] Add editable properties to shadow generator --- .../Blocks/Rendering/shadowGeneratorBlock.ts | 38 ++++++++++++++++++ .../Tasks/Rendering/shadowGeneratorTask.ts | 40 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts index 0b798401292..29fa6173386 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts @@ -5,6 +5,7 @@ import { RegisterClass } from "../../../../Misc/typeStore"; import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; import { FrameGraphShadowGeneratorTask } from "../../../Tasks/Rendering/shadowGeneratorTask"; import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import { ShadowGenerator } from "../../../../Lights/Shadows/shadowGenerator"; /** * Block that generate shadows through a shadow generator @@ -76,6 +77,37 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { this._frameGraphTask.useRedTextureFormat = value; } + /** Sets the bias */ + @editableInPropertyPage("Bias", PropertyTypeForEdition.Float, "PROPERTIES") + public get bias() { + return this._frameGraphTask.bias; + } + + public set bias(value: number) { + this._frameGraphTask.bias = value; + } + + /** Sets the filter method */ + @editableInPropertyPage("Filter", PropertyTypeForEdition.List, "PROPERTIES", { + options: [ + { label: "None", value: ShadowGenerator.FILTER_NONE }, + { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP }, + { label: "Poisson Sampling", value: ShadowGenerator.FILTER_POISSONSAMPLING }, + { label: "Blur exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP }, + { label: "Close exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP }, + { label: "Blur close exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP }, + { label: "PCF", value: ShadowGenerator.FILTER_PCF }, + { label: "PCSS", value: ShadowGenerator.FILTER_PCSS }, + ], + }) + public get filter() { + return this._frameGraphTask.filter; + } + + public set filter(value: number) { + this._frameGraphTask.filter = value; + } + /** * Gets the current class name * @returns the class name @@ -124,6 +156,8 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { codes.push(`${this._codeVariableName}.mapSize = ${this.mapSize};`); codes.push(`${this._codeVariableName}.useFloat32TextureType = ${this.useFloat32TextureType};`); codes.push(`${this._codeVariableName}.useRedTextureFormat = ${this.useRedTextureFormat};`); + codes.push(`${this._codeVariableName}.bias = ${this.bias};`); + codes.push(`${this._codeVariableName}.filter = ${this.filter};`); return super._dumpPropertiesCode() + codes.join("\n"); } @@ -132,6 +166,8 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { serializationObject.mapSize = this.mapSize; serializationObject.useFloat32TextureType = this.useFloat32TextureType; serializationObject.useRedTextureFormat = this.useRedTextureFormat; + serializationObject.bias = this.bias; + serializationObject.filter = this.filter; return serializationObject; } @@ -140,6 +176,8 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { this.mapSize = serializationObject.mapSize; this.useFloat32TextureType = serializationObject.useFloat32TextureType; this.useRedTextureFormat = serializationObject.useRedTextureFormat; + this.bias = serializationObject.bias; + this.filter = serializationObject.filter; } } diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts index 2d0dbfd4b29..7fdaf6d9b22 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts @@ -80,6 +80,44 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { this._createShadowGenerator(); } + private _bias = 0.01; + /** + * The bias to apply to the shadow map. + */ + public get bias() { + return this._bias; + } + + public set bias(value: number) { + if (value === this._bias) { + return; + } + + this._bias = value; + if (this._shadowGenerator) { + this._shadowGenerator.bias = value; + } + } + + private _filter = ShadowGenerator.FILTER_PCF; + /** + * The filter to apply to the shadow map. + */ + public get filter() { + return this._filter; + } + + public set filter(value: number) { + if (value === this._filter) { + return; + } + + this._filter = value; + if (this._shadowGenerator) { + this._shadowGenerator.filter = value; + } + } + /** * The output shadow generator. */ @@ -92,6 +130,8 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { this._shadowGenerator = undefined; if (this._light !== undefined) { this._shadowGenerator = new ShadowGenerator(this._mapSize, this._light, this._useFloat32TextureType, undefined, this._useRedTextureFormat); + this._shadowGenerator.bias = this._bias; + this._shadowGenerator.filter = this._filter; this._shadowGenerator.getShadowMap()!._disableEngineStages = true; (this.outputShadowGenerator as WritableObject) = this._shadowGenerator; } From 1481a2e3524dced7c88c67b83c24dc8f96e49841 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Mon, 2 Dec 2024 15:44:40 +0100 Subject: [PATCH 09/20] Use a dummy light for ShadowLight when necessary --- .../src/components/preview/previewManager.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts b/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts index 99320a37aa8..ea41c44c510 100644 --- a/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts +++ b/packages/tools/nodeRenderGraphEditor/src/components/preview/previewManager.ts @@ -1,6 +1,7 @@ import type { GlobalState } from "../../globalState"; import type { Nullable } from "core/types"; import type { Observer } from "core/Misc/observable"; +import type { IShadowLight } from "core/Lights/shadowLight"; import { Engine } from "core/Engines/engine"; import { Scene } from "core/scene"; import { Vector3 } from "core/Maths/math.vector"; @@ -95,6 +96,7 @@ export class PreviewManager { await (this._engine as WebGPUEngine).initAsync(); } else { this._engine = new Engine(targetCanvas, true, { forceSRGBBufferSupportState: true }); + this._engine.getCaps().parallelShaderCompile = undefined; } const canvas = this._engine.getRenderingCanvas(); @@ -179,6 +181,10 @@ export class PreviewManager { light.dispose(); } + // Create a dummy light, which will be used for a ShadowLight input in case no directional light is selected in the UI + const dummyLight = new DirectionalLight("dummy", new Vector3(0, 1, 0), this._scene); + dummyLight.intensity = 0.0; + // Create new lights based on settings if (this._globalState.hemisphericLight) { new HemisphericLight("Hemispheric light", new Vector3(0, 1, 0), this._scene); @@ -263,9 +269,11 @@ export class PreviewManager { this._scene.cameras.length = 0; this._scene.cameraToUseForPointers = null; + const dummyLight = this._scene.getLightByName("dummy") as IShadowLight; + const directionalLights: DirectionalLight[] = []; for (const light of this._scene.lights) { - if (light instanceof DirectionalLight) { + if (light instanceof DirectionalLight && light.name !== "dummy") { directionalLights.push(light); } } @@ -308,6 +316,8 @@ export class PreviewManager { if (curLightIndex < directionalLights.length) { input.value = directionalLights[curLightIndex++]; curLightIndex = curLightIndex % directionalLights.length; + } else { + input.value = dummyLight; } } } From 4da574cb09d376c3abfc1568092351ba1c7e7b07 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Mon, 2 Dec 2024 19:20:23 +0100 Subject: [PATCH 10/20] Fix framebuffer not reset to the right one after MSAA resolve --- packages/dev/core/src/Engines/Extensions/engine.multiRender.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts index 00b0185a6d3..a39c723ec0f 100644 --- a/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts +++ b/packages/dev/core/src/Engines/Extensions/engine.multiRender.ts @@ -568,4 +568,5 @@ ThinEngine.prototype.resolveMultiFramebuffer = function (texture: RenderTargetWr } gl.drawBuffers(attachments); + gl.bindFramebuffer(this._gl.FRAMEBUFFER, rtWrapper._MSAAFramebuffer); }; From a67c2258aaed616719709ed8bbdbd6bde8273a2b Mon Sep 17 00:00:00 2001 From: Popov72 Date: Mon, 2 Dec 2024 19:21:37 +0100 Subject: [PATCH 11/20] Set a better default graph --- .../src/FrameGraph/Node/nodeRenderGraph.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts index 151f8e11651..ca613d51767 100644 --- a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts +++ b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts @@ -24,6 +24,7 @@ import { Tools } from "../../Misc/tools"; import { Engine } from "../../Engines/engine"; import { NodeRenderGraphBlockConnectionPointTypes } from "./Types/nodeRenderGraphTypes"; import { NodeRenderGraphClearBlock } from "./Blocks/Textures/clearBlock"; +import { NodeRenderGraphObjectRendererBlock } from "./Blocks/Rendering/objectRendererBlock"; import { NodeRenderGraphBuildState } from "./nodeRenderGraphBuildState"; // declare NODERENDERGRAPHEDITOR namespace for compilation issue @@ -581,17 +582,35 @@ export class NodeRenderGraph { this.editorData = null; - // Source - const backBuffer = new NodeRenderGraphInputBlock("BackBuffer color", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.TextureBackBuffer); + // Source textures + const colorTexture = new NodeRenderGraphInputBlock("Color Texture", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.Texture); + colorTexture.creationOptions.options.samples = 4; + + const depthTexture = new NodeRenderGraphInputBlock("Depth Texture", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment); + depthTexture.creationOptions.options.samples = 4; // Clear texture const clear = new NodeRenderGraphClearBlock("Clear", this._frameGraph, this._scene); + clear.clearDepth = true; + clear.clearStencil = true; + + colorTexture.output.connectTo(clear.texture); + depthTexture.output.connectTo(clear.depth); + + // Render objects + const camera = new NodeRenderGraphInputBlock("Camera", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.Camera); + const objectList = new NodeRenderGraphInputBlock("Object List", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.ObjectList); + + const mainRendering = new NodeRenderGraphObjectRendererBlock("Main Rendering", this._frameGraph, this._scene); - backBuffer.output.connectTo(clear.texture); + camera.output.connectTo(mainRendering.camera); + objectList.output.connectTo(mainRendering.objects); + clear.output.connectTo(mainRendering.destination); + clear.outputDepth.connectTo(mainRendering.depth); // Final output const output = new NodeRenderGraphOutputBlock("Output", this._frameGraph, this._scene); - clear.output.connectTo(output.texture); + mainRendering.output.connectTo(output.texture); this.outputBlock = output; } From 835093df0bb06f62fd1b6625373fb58af56579b5 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Wed, 4 Dec 2024 18:55:52 +0100 Subject: [PATCH 12/20] Factor and simplify code --- .../blackAndWhitePostProcessBlock.ts | 13 ++-------- .../PostProcesses/bloomPostProcessBlock.ts | 13 ++-------- .../PostProcesses/blurPostProcessBlock.ts | 13 ++-------- .../circleOfConfusionPostProcessBlock.ts | 25 +++---------------- .../depthOfFieldPostProcessBlock.ts | 25 +++---------------- .../extractHighlightsPostProcessBlock.ts | 13 ++-------- .../Rendering/baseObjectRendererBlock.ts | 25 +++---------------- .../Node/Blocks/Rendering/cullObjectsBlock.ts | 13 ++-------- .../Blocks/Rendering/geometryRendererBlock.ts | 19 +++----------- .../Node/Blocks/Textures/clearBlock.ts | 13 ++-------- .../Node/Blocks/Textures/copyTextureBlock.ts | 13 ++-------- .../Blocks/Textures/generateMipmapsBlock.ts | 7 +----- .../Node/Blocks/resourceContainerBlock.ts | 4 --- .../FrameGraph/Node/nodeRenderGraphBlock.ts | 17 +++++++++++-- .../Tasks/Rendering/objectRendererTask.ts | 12 +++++---- .../Tasks/Texture/clearTextureTask.ts | 9 +++++++ 16 files changed, 61 insertions(+), 173 deletions(-) diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blackAndWhitePostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blackAndWhitePostProcessBlock.ts index e882283a0ce..407d42c5eb9 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blackAndWhitePostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blackAndWhitePostProcessBlock.ts @@ -94,19 +94,10 @@ export class NodeRenderGraphBlackAndWhitePostProcessBlock extends NodeRenderGrap protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/bloomPostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/bloomPostProcessBlock.ts index d46f4bbaa58..52f49e47687 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/bloomPostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/bloomPostProcessBlock.ts @@ -151,19 +151,10 @@ export class NodeRenderGraphBloomPostProcessBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blurPostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blurPostProcessBlock.ts index 7d0a5467d78..a09bf5f9a20 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blurPostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/blurPostProcessBlock.ts @@ -105,19 +105,10 @@ export class NodeRenderGraphBlurPostProcessBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/circleOfConfusionPostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/circleOfConfusionPostProcessBlock.ts index 6e6b368dde2..7204e8bf3fe 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/circleOfConfusionPostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/circleOfConfusionPostProcessBlock.ts @@ -154,29 +154,12 @@ export class NodeRenderGraphCircleOfConfusionPostProcessBlock extends NodeRender protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const geomViewDepthConnectedPoint = this.geomViewDepth.connectedPoint; - if (geomViewDepthConnectedPoint) { - this._frameGraphTask.depthTexture = geomViewDepthConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } - - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.depthTexture = this.geomViewDepth.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/depthOfFieldPostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/depthOfFieldPostProcessBlock.ts index 28cdf453c98..be09781b3e7 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/depthOfFieldPostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/depthOfFieldPostProcessBlock.ts @@ -201,29 +201,12 @@ export class NodeRenderGraphDepthOfFieldPostProcessBlock extends NodeRenderGraph protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const geomViewDepthConnectedPoint = this.geomViewDepth.connectedPoint; - if (geomViewDepthConnectedPoint) { - this._frameGraphTask.depthTexture = geomViewDepthConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } - - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.depthTexture = this.geomViewDepth.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/extractHighlightsPostProcessBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/extractHighlightsPostProcessBlock.ts index 720928a2a13..eb29375b823 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/extractHighlightsPostProcessBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/extractHighlightsPostProcessBlock.ts @@ -94,19 +94,10 @@ export class NodeRenderGraphExtractHighlightsPostProcessBlock extends NodeRender protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts index d657ec43953..eaf3d186eed 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts @@ -148,31 +148,14 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task this.outputDepth.value = this._frameGraphTask.outputDepthTexture; // the value of the outputDepth connection point is the "outputDepth" texture of the task - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } - - const depthConnectedPoint = this.depth.connectedPoint; - if (depthConnectedPoint) { - this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; - } - - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } - - const objectsConnectedPoint = this.objects.connectedPoint; - if (objectsConnectedPoint) { - this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; - } + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.depthTexture = this.depth.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; + this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; this._frameGraphTask.dependencies = []; diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock.ts index ecfb1d65da8..6ef7093e671 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/cullObjectsBlock.ts @@ -69,19 +69,10 @@ export class NodeRenderGraphCullObjectsBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputObjectList; - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } - - const objectsConnectedPoint = this.objects.connectedPoint; - if (objectsConnectedPoint) { - this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; - } + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; + this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock.ts index 54b479fef92..68785d9e2c2 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock.ts @@ -323,8 +323,6 @@ export class NodeRenderGraphGeometryRendererBlock extends NodeRenderGraphBlock { throw new Error("NodeRenderGraphGeometryRendererBlock: At least one output geometry buffer must be connected"); } - this._frameGraphTask.name = this.name; - this.outputDepth.value = this._frameGraphTask.outputDepthTexture; this.geomViewDepth.value = this._frameGraphTask.geometryViewDepthTexture; this.geomScreenDepth.value = this._frameGraphTask.geometryScreenDepthTexture; @@ -337,20 +335,9 @@ export class NodeRenderGraphGeometryRendererBlock extends NodeRenderGraphBlock { this.geomVelocity.value = this._frameGraphTask.geometryVelocityTexture; this.geomLinearVelocity.value = this._frameGraphTask.geometryLinearVelocityTexture; - const depthConnectedPoint = this.depth.connectedPoint; - if (depthConnectedPoint) { - this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; - } - - const cameraConnectedPoint = this.camera.connectedPoint; - if (cameraConnectedPoint) { - this._frameGraphTask.camera = cameraConnectedPoint.value as Camera; - } - - const objectsConnectedPoint = this.objects.connectedPoint; - if (objectsConnectedPoint) { - this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; - } + this._frameGraphTask.depthTexture = this.depth.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; + this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; this._frameGraphTask.textureDescriptions = []; diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts index d282a01b8ea..b1b3a3c891d 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/clearBlock.ts @@ -123,20 +123,11 @@ export class NodeRenderGraphClearBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this._propagateInputValueToOutput(this.texture, this.output); this._propagateInputValueToOutput(this.depth, this.outputDepth); - const textureConnectedPoint = this.texture.connectedPoint; - if (textureConnectedPoint) { - this._frameGraphTask.destinationTexture = textureConnectedPoint.value as FrameGraphTextureHandle; - } - - const depthConnectedPoint = this.depth.connectedPoint; - if (depthConnectedPoint) { - this._frameGraphTask.depthTexture = depthConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.destinationTexture = this.texture.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.depthTexture = this.depth.connectedPoint?.value as FrameGraphTextureHandle; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/copyTextureBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/copyTextureBlock.ts index d517fd393db..7cf5a84bd07 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/copyTextureBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/copyTextureBlock.ts @@ -69,19 +69,10 @@ export class NodeRenderGraphCopyTextureBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this.output.value = this._frameGraphTask.outputTexture; // the value of the output connection point is the "output" texture of the task - const sourceConnectedPoint = this.source.connectedPoint; - if (sourceConnectedPoint) { - this._frameGraphTask.sourceTexture = sourceConnectedPoint.value as FrameGraphTextureHandle; - } - - const destinationConnectedPoint = this.destination.connectedPoint; - if (destinationConnectedPoint) { - this._frameGraphTask.destinationTexture = destinationConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.sourceTexture = this.source.connectedPoint?.value as FrameGraphTextureHandle; + this._frameGraphTask.destinationTexture = this.destination.connectedPoint?.value as FrameGraphTextureHandle; } } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/generateMipmapsBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/generateMipmapsBlock.ts index b23385c160e..8883e2a063b 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/generateMipmapsBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Textures/generateMipmapsBlock.ts @@ -60,14 +60,9 @@ export class NodeRenderGraphGenerateMipmapsBlock extends NodeRenderGraphBlock { protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - this._frameGraphTask.name = this.name; - this._propagateInputValueToOutput(this.texture, this.output); - const textureConnectedPoint = this.texture.connectedPoint; - if (textureConnectedPoint) { - this._frameGraphTask.destinationTexture = textureConnectedPoint.value as FrameGraphTextureHandle; - } + this._frameGraphTask.destinationTexture = this.texture.connectedPoint?.value as FrameGraphTextureHandle; } } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts index a0781fd6e63..955ce957ea7 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts @@ -148,10 +148,6 @@ export class NodeRenderGraphResourceContainerBlock extends NodeRenderGraphBlock public get output(): NodeRenderGraphConnectionPoint { return this._outputs[0]; } - - protected override _buildBlock(state: NodeRenderGraphBuildState) { - super._buildBlock(state); - } } RegisterClass("BABYLON.NodeRenderGraphResourceContainerBlock", NodeRenderGraphResourceContainerBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlock.ts b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlock.ts index 6ba177aaba3..29223bcd0af 100644 --- a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlock.ts @@ -1,5 +1,13 @@ -// eslint-disable-next-line import/no-internal-modules -import type { NodeRenderGraphBuildState, Nullable, NodeRenderGraphInputBlock, AbstractEngine, Scene, FrameGraphTask, FrameGraph } from "core/index"; +import type { + NodeRenderGraphBuildState, + Nullable, + NodeRenderGraphInputBlock, + AbstractEngine, + Scene, + FrameGraphTask, + FrameGraph, + // eslint-disable-next-line import/no-internal-modules +} from "core/index"; import { GetClass } from "../../Misc/typeStore"; import { serialize } from "../../Misc/decorators"; import { UniqueIdGenerator } from "../../Misc/uniqueIdGenerator"; @@ -322,7 +330,12 @@ export class NodeRenderGraphBlock { Logger.Log(`Building ${this.name} [${this.getClassName()}]`); } + if (this._frameGraphTask) { + this._frameGraphTask.name = this.name; + } + this._buildBlock(state); + if (this._frameGraphTask) { this._frameGraph.addTask(this._frameGraphTask); } diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts index 116f518ad76..0bc875b8432 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts @@ -71,11 +71,6 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { */ public readonly outputDepthTexture: FrameGraphTextureHandle; - protected readonly _scene: Scene; - protected readonly _renderer: ObjectRenderer; - protected _textureWidth: number; - protected _textureHeight: number; - /** * The object renderer used to render the objects. */ @@ -94,6 +89,13 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { } } + protected readonly _scene: Scene; + protected readonly _renderer: ObjectRenderer; + protected _textureWidth: number; + protected _textureHeight: number; + protected _onBeforeRenderObservable: Nullable> = null; + protected _onAfterRenderObservable: Nullable> = null; + /** * Constructs a new object renderer task. * @param name The name of the task. diff --git a/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts index a52f92e5ded..f5a4bf4b51c 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Texture/clearTextureTask.ts @@ -64,13 +64,22 @@ export class FrameGraphClearTextureTask extends FrameGraphTask { throw new Error(`FrameGraphClearTextureTask ${this.name}: destinationTexture and depthTexture can't both be undefined.`); } + let textureSamples = 0; + let depthSamples = 0; + if (this.destinationTexture !== undefined) { + textureSamples = this._frameGraph.textureManager.getTextureDescription(this.destinationTexture).options.samples || 1; this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.destinationTexture); } if (this.depthTexture !== undefined) { + depthSamples = this._frameGraph.textureManager.getTextureDescription(this.depthTexture).options.samples || 1; this._frameGraph.textureManager.resolveDanglingHandle(this.outputDepthTexture, this.depthTexture); } + if (textureSamples !== depthSamples && textureSamples !== 0 && depthSamples !== 0) { + throw new Error(`FrameGraphClearTextureTask ${this.name}: the depth texture and the output texture must have the same number of samples.`); + } + const pass = this._frameGraph.addRenderPass(this.name); pass.setRenderTarget(this.destinationTexture); From fc2e46dbf28ff0070f120e4fb5a6eaf597cf2b38 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Wed, 4 Dec 2024 18:58:29 +0100 Subject: [PATCH 13/20] Finish implementation of the shadow generator block and task --- .../Blocks/Rendering/shadowGeneratorBlock.ts | 27 ++++++---- .../Tasks/Rendering/objectRendererTask.ts | 50 ++++++++++++++++++- .../Tasks/Rendering/shadowGeneratorTask.ts | 26 ++++++++-- 3 files changed, 86 insertions(+), 17 deletions(-) diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts index 29fa6173386..6abb54a5c47 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts @@ -32,7 +32,8 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { this.registerInput("light", NodeRenderGraphBlockConnectionPointTypes.ShadowLight); this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); - this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator); + this.registerOutput("generator", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator); + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.Texture); this._frameGraphTask = new FrameGraphShadowGeneratorTask(this.name, frameGraph, scene); } @@ -131,24 +132,28 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { } /** - * Gets the output component + * Gets the shadow generator component */ - public get output(): NodeRenderGraphConnectionPoint { + public get generator(): NodeRenderGraphConnectionPoint { return this._outputs[0]; } + /** + * Gets the output texture component + */ + public get output(): NodeRenderGraphConnectionPoint { + return this._outputs[1]; + } + protected override _buildBlock(state: NodeRenderGraphBuildState) { super._buildBlock(state); - const lightConnectedPoint = this.light.connectedPoint; - if (lightConnectedPoint) { - this._frameGraphTask.light = lightConnectedPoint.value as IShadowLight; - } + this._frameGraphTask.light = this.light.connectedPoint?.value as IShadowLight; + this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; - const objectsConnectedPoint = this.objects.connectedPoint; - if (objectsConnectedPoint) { - this._frameGraphTask.objectList = objectsConnectedPoint.value as FrameGraphObjectList; - } + // Important: the shadow generator object is created by the task when we set the light, that's why we must set generator.value after setting the light! + this.generator.value = this._frameGraphTask.outputShadowGenerator; + this.output.value = this._frameGraphTask.outputTexture; } protected override _dumpPropertiesCode() { diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts index 0bc875b8432..b846a85ab62 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts @@ -1,5 +1,17 @@ -// eslint-disable-next-line import/no-internal-modules -import type { FrameGraph, FrameGraphTextureHandle, Scene, Camera, FrameGraphObjectList, FrameGraphRenderContext, ObjectRendererOptions, ShadowGenerator } from "core/index"; +import type { + FrameGraph, + FrameGraphTextureHandle, + Scene, + Camera, + FrameGraphObjectList, + FrameGraphRenderContext, + ObjectRendererOptions, + ShadowGenerator, + Light, + Nullable, + Observer, + // eslint-disable-next-line import/no-internal-modules +} from "core/index"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../frameGraphTypes"; import { FrameGraphTask } from "../../frameGraphTask"; import { ObjectRenderer } from "../../../Rendering/objectRenderer"; @@ -161,6 +173,8 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { this._textureWidth = outputTextureDescription.size.width; this._textureHeight = outputTextureDescription.size.height; + this._setLightsForShadow(); + const pass = this._frameGraph.addRenderPass(this.name); pass.setRenderTarget(this.destinationTexture); @@ -200,4 +214,36 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { this._renderer.dispose(); super.dispose(); } + + private _setLightsForShadow() { + const lightsForShadow: Set = new Set(); + const shadowEnabled: Map = new Map(); + + if (this.shadowGenerators) { + for (const shadowGenerator of this.shadowGenerators) { + const light = shadowGenerator.getLight(); + if (light.isEnabled() && light.shadowEnabled) { + lightsForShadow.add(light); + light._shadowGenerators!.set(null, shadowGenerator); + } + } + } + + this._renderer.onBeforeRenderObservable.remove(this._onBeforeRenderObservable); + this._onBeforeRenderObservable = this._renderer.onBeforeRenderObservable.add(() => { + for (let i = 0; i < this._scene.lights.length; i++) { + const light = this._scene.lights[i]; + shadowEnabled.set(light, light.shadowEnabled); + light.shadowEnabled = lightsForShadow.has(light); + } + }); + + this._renderer.onAfterRenderObservable.remove(this._onAfterRenderObservable); + this._onAfterRenderObservable = this._renderer.onAfterRenderObservable.add(() => { + for (let i = 0; i < this._scene.lights.length; i++) { + const light = this._scene.lights[i]; + light.shadowEnabled = shadowEnabled.get(light)!; + } + }); + } } diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts index 7fdaf6d9b22..f1e0b3ad86a 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { Scene, FrameGraph, FrameGraphObjectList, IShadowLight, WritableObject, AbstractEngine } from "core/index"; +import type { Scene, FrameGraph, FrameGraphObjectList, IShadowLight, WritableObject, AbstractEngine, FrameGraphTextureHandle } from "core/index"; import { FrameGraphTask } from "../../frameGraphTask"; import { ShadowGenerator } from "../../../Lights/Shadows/shadowGenerator"; @@ -123,6 +123,11 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { */ public readonly outputShadowGenerator: ShadowGenerator; + /** + * The shadow map texture. + */ + public readonly outputTexture: FrameGraphTextureHandle; + private _shadowGenerator: ShadowGenerator | undefined; private _createShadowGenerator() { @@ -142,6 +147,7 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } private _engine: AbstractEngine; + private _scene: Scene; /** * Creates a new shadow generator task. @@ -153,6 +159,9 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { super(name, frameGraph); this._engine = scene.getEngine(); + this._scene = scene; + + this.outputTexture = this._frameGraph.textureManager.createDanglingHandle(); } public record() { @@ -160,9 +169,17 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { throw new Error(`FrameGraphShadowGeneratorTask ${this.name}: light and objectList are required`); } + const shadowMap = this._frameGraph.textureManager.importTexture(`${this.name} shadowmap`, this._shadowGenerator!.getShadowMap()!.getInternalTexture()!); + + this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, shadowMap); + const pass = this._frameGraph.addPass(this.name); pass.setExecuteFunc((_context) => { + if (!this.light.isEnabled() || !this.light.shadowEnabled) { + return; + } + const shadowMap = this._shadowGenerator!.getShadowMap()!; shadowMap.renderList = this.objectList.meshes; @@ -170,9 +187,10 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { const currentRenderTarget = this._engine._currentRenderTarget; - if (this.light.isEnabled() && this.light.shadowEnabled) { - shadowMap.render(); - } + this._scene.incrementRenderId(); + this._scene.resetCachedMaterial(); + + shadowMap.render(); if (this._engine._currentRenderTarget !== currentRenderTarget) { if (!currentRenderTarget) { From d37a270e9fc28249eb402b0763392a5d61ca6a70 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 6 Dec 2024 17:21:06 +0100 Subject: [PATCH 14/20] Add the cascaded shadow generator task and block --- .../Debug/directionalLightFrustumViewer.ts | 9 +- .../Rendering/baseObjectRendererBlock.ts | 6 +- .../Rendering/baseShadowGeneratorBlock.ts | 267 ++++++++++++++++++ .../Rendering/csmShadowGeneratorBlock.ts | 182 ++++++++++++ .../Blocks/Rendering/objectRendererBlock.ts | 10 + .../Blocks/Rendering/shadowGeneratorBlock.ts | 163 +---------- .../core/src/FrameGraph/Node/Blocks/index.ts | 1 + .../Node/Blocks/resourceContainerBlock.ts | 2 +- .../Node/Types/nodeRenderGraphTypes.ts | 4 +- .../src/FrameGraph/Node/nodeRenderGraph.ts | 15 + .../nodeRenderGraphBlockConnectionPoint.ts | 15 +- .../Tasks/Rendering/csmShadowGeneratorTask.ts | 217 ++++++++++++++ .../Tasks/Rendering/objectRendererTask.ts | 21 +- .../Tasks/Rendering/shadowGeneratorTask.ts | 156 +++++++++- packages/dev/core/src/FrameGraph/index.ts | 2 + .../src/Lights/Shadows/shadowGenerator.ts | 2 +- .../dev/core/src/PostProcesses/postProcess.ts | 10 +- .../src/PostProcesses/postProcessManager.ts | 2 +- ...commonShadowLightPropertyGridComponent.tsx | 13 +- .../directionalLightPropertyGridComponent.tsx | 12 +- .../nodeRenderGraphEditor/src/blockTools.ts | 4 + .../components/nodeList/nodeListComponent.tsx | 3 +- .../src/components/preview/previewManager.ts | 9 +- 23 files changed, 914 insertions(+), 211 deletions(-) create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseShadowGeneratorBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/csmShadowGeneratorBlock.ts create mode 100644 packages/dev/core/src/FrameGraph/Tasks/Rendering/csmShadowGeneratorTask.ts diff --git a/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts b/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts index 73227e3e0ca..5fc4c5f1838 100644 --- a/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts +++ b/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts @@ -1,3 +1,4 @@ +import type { Nullable } from "core/types"; import type { Camera } from "../Cameras/camera"; import type { DirectionalLight } from "../Lights/directionalLight"; import { StandardMaterial } from "../Materials/standardMaterial"; @@ -18,7 +19,7 @@ import type { Scene } from "../scene"; export class DirectionalLightFrustumViewer { private _scene: Scene; private _light: DirectionalLight; - private _camera: Camera; + private _camera: Nullable; private _inverseViewMatrix: Matrix; private _visible: boolean; @@ -101,7 +102,7 @@ export class DirectionalLightFrustumViewer { * @param light directional light to display the frustum for * @param camera camera used to retrieve the minZ / maxZ values if the shadowMinZ/shadowMaxZ values of the light are not setup */ - constructor(light: DirectionalLight, camera: Camera) { + constructor(light: DirectionalLight, camera: Nullable = null) { this._scene = light.getScene(); this._light = light; this._camera = camera; @@ -158,8 +159,8 @@ export class DirectionalLightFrustumViewer { this._oldMinZ = this._light.shadowMinZ; this._oldMaxZ = this._light.shadowMaxZ; - TmpVectors.Vector3[0].set(this._light.orthoLeft, this._light.orthoBottom, this._light.shadowMinZ !== undefined ? this._light.shadowMinZ : this._camera.minZ); // min light extents - TmpVectors.Vector3[1].set(this._light.orthoRight, this._light.orthoTop, this._light.shadowMaxZ !== undefined ? this._light.shadowMaxZ : this._camera.maxZ); // max light extents + TmpVectors.Vector3[0].set(this._light.orthoLeft, this._light.orthoBottom, this._light.shadowMinZ !== undefined ? this._light.shadowMinZ : (this._camera?.minZ ?? 1)); // min light extents + TmpVectors.Vector3[1].set(this._light.orthoRight, this._light.orthoTop, this._light.shadowMaxZ !== undefined ? this._light.shadowMaxZ : (this._camera?.maxZ ?? 10000)); // max light extents const invLightView = this._getInvertViewMatrix(); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts index eaf3d186eed..deff27ea437 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts @@ -7,7 +7,7 @@ import type { Camera, FrameGraphObjectRendererTask, NodeRenderGraphResourceContainerBlock, - ShadowGenerator, + FrameGraphShadowGeneratorTask, // eslint-disable-next-line import/no-internal-modules } from "core/index"; import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; @@ -181,11 +181,11 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock const container = shadowGeneratorsConnectedPoint.ownerBlock as NodeRenderGraphResourceContainerBlock; container.inputs.forEach((input) => { if (input.connectedPoint && input.connectedPoint.value !== undefined && NodeRenderGraphConnectionPoint.IsShadowGenerator(input.connectedPoint.value)) { - this._frameGraphTask.shadowGenerators!.push(input.connectedPoint.value as ShadowGenerator); + this._frameGraphTask.shadowGenerators!.push(input.connectedPoint.value as FrameGraphShadowGeneratorTask); } }); } else { - this._frameGraphTask.shadowGenerators[0] = shadowGeneratorsConnectedPoint.value as ShadowGenerator; + this._frameGraphTask.shadowGenerators[0] = shadowGeneratorsConnectedPoint.value as FrameGraphShadowGeneratorTask; } } } diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseShadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseShadowGeneratorBlock.ts new file mode 100644 index 00000000000..8349fb62335 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseShadowGeneratorBlock.ts @@ -0,0 +1,267 @@ +// eslint-disable-next-line import/no-internal-modules +import type { NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, IShadowLight, FrameGraphObjectList, FrameGraphShadowGeneratorTask } from "core/index"; +import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; +import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; +import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import { ShadowGenerator } from "../../../../Lights/Shadows/shadowGenerator"; + +/** + * @internal + */ +export class NodeRenderGraphBaseShadowGeneratorBlock extends NodeRenderGraphBlock { + protected override _frameGraphTask: FrameGraphShadowGeneratorTask; + + /** + * Gets the frame graph task associated with this block + */ + public override get task() { + return this._frameGraphTask; + } + + /** + * Create a new NodeRenderGraphBaseShadowGeneratorBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this.registerInput("light", NodeRenderGraphBlockConnectionPointTypes.ShadowLight); + this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); + + this.registerOutput("generator", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator); + this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.Texture); + } + + /** Sets the size of the shadow texture */ + @editableInPropertyPage("Map size", PropertyTypeForEdition.List, "PROPERTIES", { + options: [ + { label: "128", value: 128 }, + { label: "256", value: 256 }, + { label: "512", value: 512 }, + { label: "1024", value: 1024 }, + { label: "2048", value: 2048 }, + { label: "4096", value: 4096 }, + { label: "8192", value: 8192 }, + ], + }) + public get mapSize() { + return this._frameGraphTask.mapSize; + } + + public set mapSize(value: number) { + this._frameGraphTask.mapSize = value; + } + + /** Sets the texture type to float (by default, half float is used if supported) */ + @editableInPropertyPage("Use 32 bits float texture type", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get useFloat32TextureType() { + return this._frameGraphTask.useFloat32TextureType; + } + + public set useFloat32TextureType(value: boolean) { + this._frameGraphTask.useFloat32TextureType = value; + } + + /** Sets the texture type to Red */ + @editableInPropertyPage("Use red texture format", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get useRedTextureFormat() { + return this._frameGraphTask.useRedTextureFormat; + } + + public set useRedTextureFormat(value: boolean) { + this._frameGraphTask.useRedTextureFormat = value; + } + + /** Sets the bias */ + @editableInPropertyPage("Bias", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0, max: 1 }) + public get bias() { + return this._frameGraphTask.bias; + } + + public set bias(value: number) { + this._frameGraphTask.bias = value; + } + + /** Sets the normal bias */ + @editableInPropertyPage("Normal bias", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0, max: 1 }) + public get normalBias() { + return this._frameGraphTask.normalBias; + } + + public set normalBias(value: number) { + this._frameGraphTask.normalBias = value; + } + + /** Sets the darkness of the shadows */ + @editableInPropertyPage("Darkness", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0, max: 1 }) + public get darkness() { + return this._frameGraphTask.darkness; + } + + public set darkness(value: number) { + this._frameGraphTask.darkness = value; + } + + /** Sets the filter method */ + @editableInPropertyPage("Filter", PropertyTypeForEdition.List, "PROPERTIES", { + options: [ + { label: "None", value: ShadowGenerator.FILTER_NONE }, + { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP }, + { label: "Poisson Sampling", value: ShadowGenerator.FILTER_POISSONSAMPLING }, + { label: "Blur exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP }, + { label: "Close exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP }, + { label: "Blur close exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP }, + { label: "PCF", value: ShadowGenerator.FILTER_PCF }, + { label: "PCSS", value: ShadowGenerator.FILTER_PCSS }, + ], + }) + public get filter() { + return this._frameGraphTask.filter; + } + + public set filter(value: number) { + this._frameGraphTask.filter = value; + } + + /** Sets the filter quality (for PCF and PCSS) */ + @editableInPropertyPage("Filter quality", PropertyTypeForEdition.List, "PROPERTIES", { + options: [ + { label: "Low", value: ShadowGenerator.QUALITY_LOW }, + { label: "Medium", value: ShadowGenerator.QUALITY_MEDIUM }, + { label: "High", value: ShadowGenerator.QUALITY_HIGH }, + ], + }) + public get filteringQuality() { + return this._frameGraphTask.filteringQuality; + } + + public set filteringQuality(value: number) { + this._frameGraphTask.filteringQuality = value; + } + + /** Gets or sets the ability to have transparent shadow */ + @editableInPropertyPage("Transparency shadow", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get transparencyShadow() { + return this._frameGraphTask.transparencyShadow; + } + + public set transparencyShadow(value: boolean) { + this._frameGraphTask.transparencyShadow = value; + } + + /** Enables or disables shadows with varying strength based on the transparency */ + @editableInPropertyPage("Enable soft transparent shadows", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get enableSoftTransparentShadow() { + return this._frameGraphTask.enableSoftTransparentShadow; + } + + public set enableSoftTransparentShadow(value: boolean) { + this._frameGraphTask.enableSoftTransparentShadow = value; + } + + /** If this is true, use the opacity texture's alpha channel for transparent shadows instead of the diffuse one */ + @editableInPropertyPage("Use opacity texture for transparent shadows", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get useOpacityTextureForTransparentShadow() { + return this._frameGraphTask.useOpacityTextureForTransparentShadow; + } + + public set useOpacityTextureForTransparentShadow(value: boolean) { + this._frameGraphTask.useOpacityTextureForTransparentShadow = value; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphBaseShadowGeneratorBlock"; + } + + /** + * Gets the light input component + */ + public get light(): NodeRenderGraphConnectionPoint { + return this._inputs[0]; + } + + /** + * Gets the objects input component + */ + public get objects(): NodeRenderGraphConnectionPoint { + return this._inputs[1]; + } + + /** + * Gets the shadow generator component + */ + public get generator(): NodeRenderGraphConnectionPoint { + return this._outputs[0]; + } + + /** + * Gets the output texture component + */ + public get output(): NodeRenderGraphConnectionPoint { + return this._outputs[1]; + } + + protected override _buildBlock(state: NodeRenderGraphBuildState) { + super._buildBlock(state); + + this._frameGraphTask.light = this.light.connectedPoint?.value as IShadowLight; + this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; + + // Important: the shadow generator object is created by the task when we set the light, that's why we must set generator.value after setting the light! + this.generator.value = this._frameGraphTask; + this.output.value = this._frameGraphTask.outputTexture; + } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.mapSize = ${this.mapSize};`); + codes.push(`${this._codeVariableName}.useFloat32TextureType = ${this.useFloat32TextureType};`); + codes.push(`${this._codeVariableName}.useRedTextureFormat = ${this.useRedTextureFormat};`); + codes.push(`${this._codeVariableName}.bias = ${this.bias};`); + codes.push(`${this._codeVariableName}.normalBias = ${this.normalBias};`); + codes.push(`${this._codeVariableName}.darkness = ${this.darkness};`); + codes.push(`${this._codeVariableName}.filter = ${this.filter};`); + codes.push(`${this._codeVariableName}.filteringQuality = ${this.filteringQuality};`); + codes.push(`${this._codeVariableName}.transparencyShadow = ${this.transparencyShadow};`); + codes.push(`${this._codeVariableName}.enableSoftTransparentShadow = ${this.enableSoftTransparentShadow};`); + codes.push(`${this._codeVariableName}.useOpacityTextureForTransparentShadow = ${this.useOpacityTextureForTransparentShadow};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.mapSize = this.mapSize; + serializationObject.useFloat32TextureType = this.useFloat32TextureType; + serializationObject.useRedTextureFormat = this.useRedTextureFormat; + serializationObject.bias = this.bias; + serializationObject.normalBias = this.normalBias; + serializationObject.darkness = this.darkness; + serializationObject.filter = this.filter; + serializationObject.filteringQuality = this.filteringQuality; + serializationObject.transparencyShadow = this.transparencyShadow; + serializationObject.enableSoftTransparentShadow = this.enableSoftTransparentShadow; + serializationObject.useOpacityTextureForTransparentShadow = this.useOpacityTextureForTransparentShadow; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.mapSize = serializationObject.mapSize; + this.useFloat32TextureType = serializationObject.useFloat32TextureType; + this.useRedTextureFormat = serializationObject.useRedTextureFormat; + this.bias = serializationObject.bias; + this.normalBias = serializationObject.normalBias; + this.darkness = serializationObject.darkness; + this.filter = serializationObject.filter; + this.filteringQuality = serializationObject.filteringQuality; + this.transparencyShadow = serializationObject.transparencyShadow; + this.enableSoftTransparentShadow = serializationObject.enableSoftTransparentShadow; + this.useOpacityTextureForTransparentShadow = serializationObject.useOpacityTextureForTransparentShadow; + } +} diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/csmShadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/csmShadowGeneratorBlock.ts new file mode 100644 index 00000000000..26003c4927e --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/csmShadowGeneratorBlock.ts @@ -0,0 +1,182 @@ +// eslint-disable-next-line import/no-internal-modules +import type { Scene, FrameGraph, NodeRenderGraphConnectionPoint, NodeRenderGraphBuildState, Camera } from "core/index"; +import { NodeRenderGraphBaseShadowGeneratorBlock } from "./baseShadowGeneratorBlock"; +import { RegisterClass } from "../../../../Misc/typeStore"; +import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; +import { FrameGraphCascadedShadowGeneratorTask } from "../../../Tasks/Rendering/csmShadowGeneratorTask"; +import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; + +/** + * Block that generates shadows through a shadow generator + */ +export class NodeRenderGraphCascadedShadowGeneratorBlock extends NodeRenderGraphBaseShadowGeneratorBlock { + protected override _frameGraphTask: FrameGraphCascadedShadowGeneratorTask; + + /** + * Gets the frame graph task associated with this block + */ + public override get task() { + return this._frameGraphTask; + } + + /** + * Create a new NodeRenderGraphCascadedShadowGeneratorBlock + * @param name defines the block name + * @param frameGraph defines the hosting frame graph + * @param scene defines the hosting scene + */ + public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { + super(name, frameGraph, scene); + + this.registerInput("camera", NodeRenderGraphBlockConnectionPointTypes.Camera); + + this._frameGraphTask = new FrameGraphCascadedShadowGeneratorTask(this.name, frameGraph, scene); + } + + /** Sets the number of cascades */ + @editableInPropertyPage("Number of cascades", PropertyTypeForEdition.List, "CSM PROPERTIES", { + options: [ + { label: "2", value: 2 }, + { label: "3", value: 3 }, + { label: "4", value: 4 }, + ], + }) + public get numCascades() { + return this._frameGraphTask.numCascades; + } + + public set numCascades(value: number) { + this._frameGraphTask.numCascades = value; + } + + /** Gets or sets a value indicating whether the shadow generator should display the cascades. */ + @editableInPropertyPage("Debug mode", PropertyTypeForEdition.Boolean, "CSM PROPERTIES") + public get debug() { + return this._frameGraphTask.debug; + } + + public set debug(value: boolean) { + this._frameGraphTask.debug = value; + } + + /** Gets or sets a value indicating whether the shadow generator should stabilize the cascades. */ + @editableInPropertyPage("Stabilize cascades", PropertyTypeForEdition.Boolean, "CSM PROPERTIES") + public get stabilizeCascades() { + return this._frameGraphTask.stabilizeCascades; + } + + public set stabilizeCascades(value: boolean) { + this._frameGraphTask.stabilizeCascades = value; + } + + /** Gets or sets the lambda parameter of the shadow generator. */ + @editableInPropertyPage("Lambda", PropertyTypeForEdition.Float, "CSM PROPERTIES", { min: 0, max: 1 }) + public get lambda() { + return this._frameGraphTask.lambda; + } + + public set lambda(value: number) { + this._frameGraphTask.lambda = value; + } + + /** Gets or sets the cascade blend percentage. */ + @editableInPropertyPage("Cascade blend", PropertyTypeForEdition.Float, "CSM PROPERTIES", { min: 0, max: 1 }) + public get cascadeBlendPercentage() { + return this._frameGraphTask.cascadeBlendPercentage; + } + + public set cascadeBlendPercentage(value: number) { + this._frameGraphTask.cascadeBlendPercentage = value; + } + + /** Gets or sets a value indicating whether the shadow generator should use depth clamping. */ + @editableInPropertyPage("Depth clamp", PropertyTypeForEdition.Boolean, "CSM PROPERTIES") + public get depthClamp() { + return this._frameGraphTask.depthClamp; + } + + public set depthClamp(value: boolean) { + this._frameGraphTask.depthClamp = value; + } + + /** Gets or sets a value indicating whether the shadow generator should automatically calculate the depth bounds. */ + @editableInPropertyPage("Auto-Calc depth bounds", PropertyTypeForEdition.Boolean, "CSM PROPERTIES") + public get autoCalcDepthBounds() { + return this._frameGraphTask.autoCalcDepthBounds; + } + + public set autoCalcDepthBounds(value: boolean) { + this._frameGraphTask.autoCalcDepthBounds = value; + } + + /** Gets or sets the maximum shadow Z value. */ + @editableInPropertyPage("Shadow maxZ", PropertyTypeForEdition.Float, "CSM PROPERTIES") + public get shadowMaxZ() { + return this._frameGraphTask.shadowMaxZ; + } + + public set shadowMaxZ(value: number) { + this._frameGraphTask.shadowMaxZ = value; + } + + /** + * Gets the current class name + * @returns the class name + */ + public override getClassName() { + return "NodeRenderGraphCascadedShadowGeneratorBlock"; + } + + /** + * Gets the camera input component + */ + public get camera(): NodeRenderGraphConnectionPoint { + return this._inputs[2]; + } + + protected override _buildBlock(state: NodeRenderGraphBuildState) { + super._buildBlock(state); + + this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera; + } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.numCascades = ${this.numCascades};`); + codes.push(`${this._codeVariableName}.debug = ${this.debug};`); + codes.push(`${this._codeVariableName}.stabilizeCascades = ${this.stabilizeCascades};`); + codes.push(`${this._codeVariableName}.lambda = ${this.lambda};`); + codes.push(`${this._codeVariableName}.cascadeBlendPercentage = ${this.cascadeBlendPercentage};`); + codes.push(`${this._codeVariableName}.depthClamp = ${this.depthClamp};`); + codes.push(`${this._codeVariableName}.autoCalcDepthBounds = ${this.autoCalcDepthBounds};`); + codes.push(`${this._codeVariableName}.shadowMaxZ = ${this.shadowMaxZ};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.numCascades = this.numCascades; + serializationObject.debug = this.debug; + serializationObject.stabilizeCascades = this.stabilizeCascades; + serializationObject.lambda = this.lambda; + serializationObject.cascadeBlendPercentage = this.cascadeBlendPercentage; + serializationObject.depthClamp = this.depthClamp; + serializationObject.autoCalcDepthBounds = this.autoCalcDepthBounds; + serializationObject.shadowMaxZ = this.shadowMaxZ; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.numCascades = serializationObject.numCascades; + this.debug = serializationObject.debug; + this.stabilizeCascades = serializationObject.stabilizeCascades; + this.lambda = serializationObject.lambda; + this.cascadeBlendPercentage = serializationObject.cascadeBlendPercentage; + this.depthClamp = serializationObject.depthClamp; + this.autoCalcDepthBounds = serializationObject.autoCalcDepthBounds; + this.shadowMaxZ = serializationObject.shadowMaxZ; + } +} + +RegisterClass("BABYLON.NodeRenderGraphCascadedShadowGeneratorBlock", NodeRenderGraphCascadedShadowGeneratorBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts index 55da231f402..ed363b6f32e 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts @@ -36,6 +36,16 @@ export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBaseObjec this._additionalConstructionParameters = [value]; } + /** Indicates if shadows must be enabled or disabled */ + @editableInPropertyPage("Disable shadows", PropertyTypeForEdition.Boolean, "PROPERTIES") + public get disableShadows() { + return this._frameGraphTask.disableShadows; + } + + public set disableShadows(value: boolean) { + this._frameGraphTask.disableShadows = value; + } + /** * Gets the current class name * @returns the class name diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts index 6abb54a5c47..827313a88d8 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock.ts @@ -1,25 +1,13 @@ // eslint-disable-next-line import/no-internal-modules -import type { NodeRenderGraphConnectionPoint, Scene, NodeRenderGraphBuildState, FrameGraph, IShadowLight, FrameGraphObjectList } from "core/index"; -import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock"; +import type { Scene, FrameGraph } from "core/index"; +import { NodeRenderGraphBaseShadowGeneratorBlock } from "./baseShadowGeneratorBlock"; import { RegisterClass } from "../../../../Misc/typeStore"; -import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes"; import { FrameGraphShadowGeneratorTask } from "../../../Tasks/Rendering/shadowGeneratorTask"; -import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator"; -import { ShadowGenerator } from "../../../../Lights/Shadows/shadowGenerator"; /** * Block that generate shadows through a shadow generator */ -export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { - protected override _frameGraphTask: FrameGraphShadowGeneratorTask; - - /** - * Gets the frame graph task associated with this block - */ - public override get task() { - return this._frameGraphTask; - } - +export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBaseShadowGeneratorBlock { /** * Create a new NodeRenderGraphShadowGeneratorBlock * @param name defines the block name @@ -29,86 +17,9 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { public constructor(name: string, frameGraph: FrameGraph, scene: Scene) { super(name, frameGraph, scene); - this.registerInput("light", NodeRenderGraphBlockConnectionPointTypes.ShadowLight); - this.registerInput("objects", NodeRenderGraphBlockConnectionPointTypes.ObjectList); - - this.registerOutput("generator", NodeRenderGraphBlockConnectionPointTypes.ShadowGenerator); - this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.Texture); - this._frameGraphTask = new FrameGraphShadowGeneratorTask(this.name, frameGraph, scene); } - /** Sets the size of the shadow texture */ - @editableInPropertyPage("Map size", PropertyTypeForEdition.List, "PROPERTIES", { - options: [ - { label: "128", value: 128 }, - { label: "256", value: 256 }, - { label: "512", value: 512 }, - { label: "1024", value: 1024 }, - { label: "2048", value: 2048 }, - { label: "4096", value: 4096 }, - { label: "8192", value: 8192 }, - ], - }) - public get mapSize() { - return this._frameGraphTask.mapSize; - } - - public set mapSize(value: number) { - this._frameGraphTask.mapSize = value; - } - - /** Sets the texture type to float (by default, half float is used if supported) */ - @editableInPropertyPage("Use 32 bits float texture type", PropertyTypeForEdition.Boolean, "PROPERTIES") - public get useFloat32TextureType() { - return this._frameGraphTask.useFloat32TextureType; - } - - public set useFloat32TextureType(value: boolean) { - this._frameGraphTask.useFloat32TextureType = value; - } - - /** Sets the texture type to Red */ - @editableInPropertyPage("Use red texture format", PropertyTypeForEdition.Boolean, "PROPERTIES") - public get useRedTextureFormat() { - return this._frameGraphTask.useRedTextureFormat; - } - - public set useRedTextureFormat(value: boolean) { - this._frameGraphTask.useRedTextureFormat = value; - } - - /** Sets the bias */ - @editableInPropertyPage("Bias", PropertyTypeForEdition.Float, "PROPERTIES") - public get bias() { - return this._frameGraphTask.bias; - } - - public set bias(value: number) { - this._frameGraphTask.bias = value; - } - - /** Sets the filter method */ - @editableInPropertyPage("Filter", PropertyTypeForEdition.List, "PROPERTIES", { - options: [ - { label: "None", value: ShadowGenerator.FILTER_NONE }, - { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP }, - { label: "Poisson Sampling", value: ShadowGenerator.FILTER_POISSONSAMPLING }, - { label: "Blur exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP }, - { label: "Close exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP }, - { label: "Blur close exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP }, - { label: "PCF", value: ShadowGenerator.FILTER_PCF }, - { label: "PCSS", value: ShadowGenerator.FILTER_PCSS }, - ], - }) - public get filter() { - return this._frameGraphTask.filter; - } - - public set filter(value: number) { - this._frameGraphTask.filter = value; - } - /** * Gets the current class name * @returns the class name @@ -116,74 +27,6 @@ export class NodeRenderGraphShadowGeneratorBlock extends NodeRenderGraphBlock { public override getClassName() { return "NodeRenderGraphShadowGeneratorBlock"; } - - /** - * Gets the light input component - */ - public get light(): NodeRenderGraphConnectionPoint { - return this._inputs[0]; - } - - /** - * Gets the objects input component - */ - public get objects(): NodeRenderGraphConnectionPoint { - return this._inputs[1]; - } - - /** - * Gets the shadow generator component - */ - public get generator(): NodeRenderGraphConnectionPoint { - return this._outputs[0]; - } - - /** - * Gets the output texture component - */ - public get output(): NodeRenderGraphConnectionPoint { - return this._outputs[1]; - } - - protected override _buildBlock(state: NodeRenderGraphBuildState) { - super._buildBlock(state); - - this._frameGraphTask.light = this.light.connectedPoint?.value as IShadowLight; - this._frameGraphTask.objectList = this.objects.connectedPoint?.value as FrameGraphObjectList; - - // Important: the shadow generator object is created by the task when we set the light, that's why we must set generator.value after setting the light! - this.generator.value = this._frameGraphTask.outputShadowGenerator; - this.output.value = this._frameGraphTask.outputTexture; - } - - protected override _dumpPropertiesCode() { - const codes: string[] = []; - codes.push(`${this._codeVariableName}.mapSize = ${this.mapSize};`); - codes.push(`${this._codeVariableName}.useFloat32TextureType = ${this.useFloat32TextureType};`); - codes.push(`${this._codeVariableName}.useRedTextureFormat = ${this.useRedTextureFormat};`); - codes.push(`${this._codeVariableName}.bias = ${this.bias};`); - codes.push(`${this._codeVariableName}.filter = ${this.filter};`); - return super._dumpPropertiesCode() + codes.join("\n"); - } - - public override serialize(): any { - const serializationObject = super.serialize(); - serializationObject.mapSize = this.mapSize; - serializationObject.useFloat32TextureType = this.useFloat32TextureType; - serializationObject.useRedTextureFormat = this.useRedTextureFormat; - serializationObject.bias = this.bias; - serializationObject.filter = this.filter; - return serializationObject; - } - - public override _deserialize(serializationObject: any) { - super._deserialize(serializationObject); - this.mapSize = serializationObject.mapSize; - this.useFloat32TextureType = serializationObject.useFloat32TextureType; - this.useRedTextureFormat = serializationObject.useRedTextureFormat; - this.bias = serializationObject.bias; - this.filter = serializationObject.filter; - } } RegisterClass("BABYLON.NodeRenderGraphShadowGeneratorBlock", NodeRenderGraphShadowGeneratorBlock); diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts index d3a32b7e9e6..3eb3f5ec156 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/index.ts @@ -10,6 +10,7 @@ export * from "./PostProcesses/circleOfConfusionPostProcessBlock"; export * from "./PostProcesses/depthOfFieldPostProcessBlock"; export * from "./PostProcesses/extractHighlightsPostProcessBlock"; +export * from "./Rendering/csmShadowGeneratorBlock"; export * from "./Rendering/cullObjectsBlock"; export * from "./Rendering/geometryRendererBlock"; export * from "./Rendering/objectRendererBlock"; diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts index 955ce957ea7..09afa9e66a5 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/resourceContainerBlock.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph, NodeRenderGraphBuildState } from "core/index"; +import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph } from "core/index"; import { RegisterClass } from "../../../Misc/typeStore"; import { NodeRenderGraphBlockConnectionPointTypes } from "../Types/nodeRenderGraphTypes"; import { NodeRenderGraphBlock } from "../nodeRenderGraphBlock"; diff --git a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts index 579186ebdda..98c43947d72 100644 --- a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-internal-modules -import type { Color4, Scene, FrameGraphTextureHandle, Camera, FrameGraphObjectList, IShadowLight, ShadowGenerator } from "core/index"; +import type { Color4, Scene, FrameGraphTextureHandle, Camera, FrameGraphObjectList, IShadowLight, FrameGraphShadowGeneratorTask } from "core/index"; /** * Interface used to configure the node render graph editor @@ -124,4 +124,4 @@ export const enum NodeRenderGraphConnectionPointDirection { /** * Defines the type of a connection point value */ -export type NodeRenderGraphBlockConnectionPointValueType = FrameGraphTextureHandle | Camera | FrameGraphObjectList | IShadowLight | ShadowGenerator; +export type NodeRenderGraphBlockConnectionPointValueType = FrameGraphTextureHandle | Camera | FrameGraphObjectList | IShadowLight | FrameGraphShadowGeneratorTask; diff --git a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts index ca613d51767..93a747685fe 100644 --- a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts +++ b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts @@ -10,6 +10,7 @@ import type { INodeRenderGraphEditorOptions, Scene, WritableObject, + IShadowLight, } from "core/index"; import { Observable } from "../../Misc/observable"; import { NodeRenderGraphOutputBlock } from "./Blocks/outputBlock"; @@ -302,7 +303,16 @@ export class NodeRenderGraph { private _autoFillExternalInputs() { const allInputs = this.getInputBlocks(); + + const shadowLights: IShadowLight[] = []; + for (const light of this._scene.lights) { + if ((light as IShadowLight).setShadowProjectionMatrix !== undefined) { + shadowLights.push(light as IShadowLight); + } + } + let cameraIndex = 0; + let lightIndex = 0; for (const input of allInputs) { if (!input.isExternal) { continue; @@ -321,6 +331,11 @@ export class NodeRenderGraph { input.value = camera; } else if (input.isObjectList()) { input.value = { meshes: this._scene.meshes, particleSystems: this._scene.particleSystems }; + } else if (input.isShadowLight()) { + if (lightIndex < shadowLights.length) { + input.value = shadowLights[lightIndex++]; + lightIndex = lightIndex % shadowLights.length; + } } } } diff --git a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts index 361c427bb35..5cd6033a794 100644 --- a/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts +++ b/packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlockConnectionPoint.ts @@ -1,5 +1,12 @@ -// eslint-disable-next-line import/no-internal-modules -import type { Nullable, NodeRenderGraphBlock, NodeRenderGraphBlockConnectionPointValueType, NodeRenderGraphInputBlock, IShadowLight, ShadowGenerator } from "core/index"; +import type { + Nullable, + NodeRenderGraphBlock, + NodeRenderGraphBlockConnectionPointValueType, + NodeRenderGraphInputBlock, + IShadowLight, + FrameGraphShadowGeneratorTask, + // eslint-disable-next-line import/no-internal-modules +} from "core/index"; import { Observable } from "../../Misc/observable"; import { NodeRenderGraphBlockConnectionPointTypes, NodeRenderGraphConnectionPointCompatibilityStates, NodeRenderGraphConnectionPointDirection } from "./Types/nodeRenderGraphTypes"; @@ -41,12 +48,12 @@ export class NodeRenderGraphConnectionPoint { } /** - * Checks if the value is a shadow generator + * Checks if the value is a shadow generator task * @param value The value to check * @returns True if the value is a shadow generator */ public static IsShadowGenerator(value: NodeRenderGraphBlockConnectionPointValueType): boolean { - return (value as ShadowGenerator).getShadowMap !== undefined; + return (value as FrameGraphShadowGeneratorTask).mapSize !== undefined; } /** diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/csmShadowGeneratorTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/csmShadowGeneratorTask.ts new file mode 100644 index 00000000000..1229c745cb5 --- /dev/null +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/csmShadowGeneratorTask.ts @@ -0,0 +1,217 @@ +// eslint-disable-next-line import/no-internal-modules +import type { Camera } from "core/index"; +import { CascadedShadowGenerator } from "../../../Lights/Shadows/cascadedShadowGenerator"; +import { FrameGraphShadowGeneratorTask } from "./shadowGeneratorTask"; +import { DirectionalLight } from "../../../Lights/directionalLight"; + +/** + * Task used to generate a cascaded shadow map from a list of objects. + */ +export class FrameGraphCascadedShadowGeneratorTask extends FrameGraphShadowGeneratorTask { + protected override _shadowGenerator: CascadedShadowGenerator | undefined; + + /** + * Checks if a shadow generator task is a cascaded shadow generator task. + * @param task The task to check. + * @returns True if the task is a cascaded shadow generator task, else false. + */ + public static IsCascadedShadowGenerator(task: FrameGraphShadowGeneratorTask): task is FrameGraphCascadedShadowGeneratorTask { + return (task as FrameGraphCascadedShadowGeneratorTask).numCascades !== undefined; + } + + private _numCascades = CascadedShadowGenerator.DEFAULT_CASCADES_COUNT; + /** + * The number of cascades. + */ + public get numCascades() { + return this._numCascades; + } + + public set numCascades(value: number) { + if (value === this._numCascades) { + return; + } + + this._numCascades = value; + this._setupShadowGenerator(); + } + + private _debug = false; + /** + * Gets or sets a value indicating whether the shadow generator should display the cascades. + */ + public get debug() { + return this._debug; + } + + public set debug(value: boolean) { + if (value === this._debug) { + return; + } + + this._debug = value; + if (this._shadowGenerator) { + this._shadowGenerator.debug = value; + } + } + + private _stabilizeCascades = false; + /** + * Gets or sets a value indicating whether the shadow generator should stabilize the cascades. + */ + public get stabilizeCascades() { + return this._stabilizeCascades; + } + + public set stabilizeCascades(value: boolean) { + if (value === this._stabilizeCascades) { + return; + } + + this._stabilizeCascades = value; + if (this._shadowGenerator) { + this._shadowGenerator.stabilizeCascades = value; + } + } + + private _lambda = 0.5; + /** + * Gets or sets the lambda parameter of the shadow generator. + */ + public get lambda() { + return this._lambda; + } + + public set lambda(value: number) { + if (value === this._lambda) { + return; + } + + this._lambda = value; + if (this._shadowGenerator) { + this._shadowGenerator.lambda = value; + } + } + + private _cascadeBlendPercentage = 0.1; + /** + * Gets or sets the cascade blend percentage. + */ + public get cascadeBlendPercentage() { + return this._cascadeBlendPercentage; + } + + public set cascadeBlendPercentage(value: number) { + if (value === this._cascadeBlendPercentage) { + return; + } + + this._cascadeBlendPercentage = value; + if (this._shadowGenerator) { + this._shadowGenerator.cascadeBlendPercentage = value; + } + } + + private _depthClamp = true; + /** + * Gets or sets a value indicating whether the shadow generator should use depth clamping. + */ + public get depthClamp() { + return this._depthClamp; + } + + public set depthClamp(value: boolean) { + if (value === this._depthClamp) { + return; + } + + this._depthClamp = value; + if (this._shadowGenerator) { + this._shadowGenerator.depthClamp = value; + } + } + + private _autoCalcDepthBounds = false; + /** + * Gets or sets a value indicating whether the shadow generator should automatically calculate the depth bounds. + */ + public get autoCalcDepthBounds() { + return this._autoCalcDepthBounds; + } + + public set autoCalcDepthBounds(value: boolean) { + if (value === this._autoCalcDepthBounds) { + return; + } + + this._autoCalcDepthBounds = value; + if (this._shadowGenerator) { + this._shadowGenerator.autoCalcDepthBounds = value; + } + } + + private _shadowMaxZ = 10000; + /** + * Gets or sets the maximum shadow Z value. + */ + public get shadowMaxZ() { + return this._shadowMaxZ; + } + + public set shadowMaxZ(value: number) { + if (value === this._shadowMaxZ) { + return; + } + + this._shadowMaxZ = value; + if (this._shadowGenerator) { + this._shadowGenerator.shadowMaxZ = value; + } + } + + private _camera: Camera; + /** + * Gets or sets the camera used to generate the shadow generator. + */ + public get camera() { + return this._camera; + } + + public set camera(camera: Camera) { + this._camera = camera; + this._setupShadowGenerator(); + } + + public override record() { + if (this.camera === undefined) { + throw new Error(`FrameGraphCascadedShadowGeneratorTask ${this.name}: camera is required`); + } + + super.record(); + } + + protected override _createShadowGenerator() { + if (!(this.light instanceof DirectionalLight)) { + throw new Error(`FrameGraphCascadedShadowGeneratorTask ${this.name}: the CSM shadow generator only supports directional lights.`); + } + this._shadowGenerator = new CascadedShadowGenerator(this.mapSize, this.light, this.useFloat32TextureType, this._camera, this.useRedTextureFormat); + this._shadowGenerator.numCascades = this._numCascades; + } + + protected override _setupShadowGenerator() { + super._setupShadowGenerator(); + + const shadowGenerator = this._shadowGenerator as CascadedShadowGenerator | undefined; + if (shadowGenerator === undefined) { + return; + } + + shadowGenerator.debug = this._debug; + shadowGenerator.stabilizeCascades = this._stabilizeCascades; + shadowGenerator.lambda = this._lambda; + shadowGenerator.cascadeBlendPercentage = this._cascadeBlendPercentage; + shadowGenerator.depthClamp = this._depthClamp; + shadowGenerator.autoCalcDepthBounds = this._autoCalcDepthBounds; + shadowGenerator.shadowMaxZ = this._shadowMaxZ; + } +} diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts index b846a85ab62..d28dbccc13e 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/objectRendererTask.ts @@ -6,15 +6,16 @@ import type { FrameGraphObjectList, FrameGraphRenderContext, ObjectRendererOptions, - ShadowGenerator, Light, Nullable, Observer, + FrameGraphShadowGeneratorTask, // eslint-disable-next-line import/no-internal-modules } from "core/index"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "../../frameGraphTypes"; import { FrameGraphTask } from "../../frameGraphTask"; import { ObjectRenderer } from "../../../Rendering/objectRenderer"; +import { FrameGraphCascadedShadowGeneratorTask } from "./csmShadowGeneratorTask"; /** * Task used to render objects to a texture. @@ -38,7 +39,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { /** * The shadow generators used to render the objects (optional). */ - public shadowGenerators?: ShadowGenerator[] = []; + public shadowGenerators?: FrameGraphShadowGeneratorTask[] = []; private _camera: Camera; @@ -69,6 +70,11 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { */ public depthWrite = true; + /** + * If shadows should be disabled (default is false). + */ + public disableShadows = false; + /** * The output texture. * This texture will point to the same texture than the destinationTexture property if it is set. @@ -220,11 +226,16 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { const shadowEnabled: Map = new Map(); if (this.shadowGenerators) { - for (const shadowGenerator of this.shadowGenerators) { + for (const shadowGeneratorTask of this.shadowGenerators) { + const shadowGenerator = shadowGeneratorTask.shadowGenerator; const light = shadowGenerator.getLight(); if (light.isEnabled() && light.shadowEnabled) { lightsForShadow.add(light); - light._shadowGenerators!.set(null, shadowGenerator); + if (FrameGraphCascadedShadowGeneratorTask.IsCascadedShadowGenerator(shadowGeneratorTask)) { + light._shadowGenerators!.set(shadowGeneratorTask.camera, shadowGenerator); + } else { + light._shadowGenerators!.set(null, shadowGenerator); + } } } } @@ -234,7 +245,7 @@ export class FrameGraphObjectRendererTask extends FrameGraphTask { for (let i = 0; i < this._scene.lights.length; i++) { const light = this._scene.lights[i]; shadowEnabled.set(light, light.shadowEnabled); - light.shadowEnabled = lightsForShadow.has(light); + light.shadowEnabled = !this.disableShadows && lightsForShadow.has(light); } }); diff --git a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts index f1e0b3ad86a..149962e2732 100644 --- a/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts +++ b/packages/dev/core/src/FrameGraph/Tasks/Rendering/shadowGeneratorTask.ts @@ -8,7 +8,7 @@ import { ShadowGenerator } from "../../../Lights/Shadows/shadowGenerator"; */ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { /** - * The object list to cull. + * The object list that generates shadows. */ public objectList: FrameGraphObjectList; @@ -26,7 +26,7 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } this._light = value; - this._createShadowGenerator(); + this._setupShadowGenerator(); } private _mapSize = 1024; @@ -43,7 +43,7 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } this._mapSize = value; - this._createShadowGenerator(); + this._setupShadowGenerator(); } private _useFloat32TextureType = false; @@ -60,7 +60,7 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } this._useFloat32TextureType = value; - this._createShadowGenerator(); + this._setupShadowGenerator(); } private _useRedTextureFormat = true; @@ -77,7 +77,7 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } this._useRedTextureFormat = value; - this._createShadowGenerator(); + this._setupShadowGenerator(); } private _bias = 0.01; @@ -99,6 +99,101 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } } + private _normalBias = 0; + /** + * The normal bias to apply to the shadow map. + */ + public get normalBias() { + return this._normalBias; + } + + public set normalBias(value: number) { + if (value === this._normalBias) { + return; + } + + this._normalBias = value; + if (this._shadowGenerator) { + this._shadowGenerator.normalBias = value; + } + } + + private _darkness = 0; + /** + * The darkness of the shadows. + */ + public get darkness() { + return this._darkness; + } + + public set darkness(value: number) { + if (value === this._darkness) { + return; + } + + this._darkness = value; + if (this._shadowGenerator) { + this._shadowGenerator.darkness = value; + } + } + + private _transparencyShadow = false; + /** + * Gets or sets the ability to have transparent shadow + */ + public get transparencyShadow() { + return this._transparencyShadow; + } + + public set transparencyShadow(value: boolean) { + if (value === this._transparencyShadow) { + return; + } + + this._transparencyShadow = value; + if (this._shadowGenerator) { + this._shadowGenerator.transparencyShadow = value; + } + } + + private _enableSoftTransparentShadow = false; + /** + * Enables or disables shadows with varying strength based on the transparency + */ + public get enableSoftTransparentShadow() { + return this._enableSoftTransparentShadow; + } + + public set enableSoftTransparentShadow(value: boolean) { + if (value === this._enableSoftTransparentShadow) { + return; + } + + this._enableSoftTransparentShadow = value; + if (this._shadowGenerator) { + this._shadowGenerator.enableSoftTransparentShadow = value; + } + } + + private _useOpacityTextureForTransparentShadow = false; + /** + * If this is true, use the opacity texture's alpha channel for transparent shadows instead of the diffuse one + */ + public get useOpacityTextureForTransparentShadow() { + return this._useOpacityTextureForTransparentShadow; + } + + public set useOpacityTextureForTransparentShadow(value: boolean) { + if (value === this._useOpacityTextureForTransparentShadow) { + return; + } + + this._useOpacityTextureForTransparentShadow = value; + if (this._shadowGenerator) { + this._shadowGenerator.useOpacityTextureForTransparentShadow = value; + } + } + private _filter = ShadowGenerator.FILTER_PCF; /** * The filter to apply to the shadow map. @@ -118,27 +213,60 @@ export class FrameGraphShadowGeneratorTask extends FrameGraphTask { } } + private _filteringQuality = ShadowGenerator.QUALITY_HIGH; /** - * The output shadow generator. + * The filtering quality to apply to the filter. */ - public readonly outputShadowGenerator: ShadowGenerator; + public get filteringQuality() { + return this._filteringQuality; + } + + public set filteringQuality(value: number) { + if (value === this._filteringQuality) { + return; + } + + this._filteringQuality = value; + if (this._shadowGenerator) { + this._shadowGenerator.filteringQuality = value; + } + } + + /** + * The shadow generator. + */ + public readonly shadowGenerator: ShadowGenerator; /** * The shadow map texture. */ public readonly outputTexture: FrameGraphTextureHandle; - private _shadowGenerator: ShadowGenerator | undefined; + protected _shadowGenerator: ShadowGenerator | undefined; - private _createShadowGenerator() { + protected _createShadowGenerator() { + this._shadowGenerator = new ShadowGenerator(this._mapSize, this._light, this._useFloat32TextureType, undefined, this._useRedTextureFormat); + } + + protected _setupShadowGenerator() { this._shadowGenerator?.dispose(); this._shadowGenerator = undefined; if (this._light !== undefined) { - this._shadowGenerator = new ShadowGenerator(this._mapSize, this._light, this._useFloat32TextureType, undefined, this._useRedTextureFormat); - this._shadowGenerator.bias = this._bias; - this._shadowGenerator.filter = this._filter; - this._shadowGenerator.getShadowMap()!._disableEngineStages = true; - (this.outputShadowGenerator as WritableObject) = this._shadowGenerator; + this._createShadowGenerator(); + const shadowGenerator = this._shadowGenerator as ShadowGenerator | undefined; + if (shadowGenerator === undefined) { + return; + } + shadowGenerator.bias = this._bias; + shadowGenerator.normalBias = this._normalBias; + shadowGenerator.darkness = this._darkness; + shadowGenerator.transparencyShadow = this._transparencyShadow; + shadowGenerator.enableSoftTransparentShadow = this._enableSoftTransparentShadow; + shadowGenerator.useOpacityTextureForTransparentShadow = this._useOpacityTextureForTransparentShadow; + shadowGenerator.filter = this._filter; + shadowGenerator.filteringQuality = this._filteringQuality; + shadowGenerator.getShadowMap()!._disableEngineStages = true; + (this.shadowGenerator as WritableObject) = shadowGenerator; } } diff --git a/packages/dev/core/src/FrameGraph/index.ts b/packages/dev/core/src/FrameGraph/index.ts index cb9a7f12ae3..bdee7d38396 100644 --- a/packages/dev/core/src/FrameGraph/index.ts +++ b/packages/dev/core/src/FrameGraph/index.ts @@ -21,9 +21,11 @@ export * from "./Tasks/Texture/clearTextureTask"; export * from "./Tasks/Texture/copyToBackbufferColorTask"; export * from "./Tasks/Texture/copyToTextureTask"; +export * from "./Tasks/Rendering/csmShadowGeneratorTask"; export * from "./Tasks/Rendering/cullObjectsTask"; export * from "./Tasks/Rendering/geometryRendererTask"; export * from "./Tasks/Rendering/objectRendererTask"; +export * from "./Tasks/Rendering/shadowGeneratorTask"; export * from "./Tasks/Rendering/taaObjectRendererTask"; export * from "./frameGraph"; diff --git a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts index 0849600bd6b..433c20b1d63 100644 --- a/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts +++ b/packages/dev/core/src/Lights/Shadows/shadowGenerator.ts @@ -665,7 +665,7 @@ export class ShadowGenerator implements IShadowGenerator { protected _transparencyShadow = false; - /** Gets or sets the ability to have transparent shadow */ + /** Gets or sets the ability to have transparent shadow */ public get transparencyShadow() { return this._transparencyShadow; } diff --git a/packages/dev/core/src/PostProcesses/postProcess.ts b/packages/dev/core/src/PostProcesses/postProcess.ts index 97659a70a15..64f92fe91b6 100644 --- a/packages/dev/core/src/PostProcesses/postProcess.ts +++ b/packages/dev/core/src/PostProcesses/postProcess.ts @@ -929,15 +929,15 @@ export class PostProcess { /** * Activates the post process by intializing the textures to be used when executed. Notifies onActivateObservable. * When this post process is used in a pipeline, this is call will bind the input texture of this post process to the output of the previous. - * @param camera The camera that will be used in the post process. This camera will be used when calling onActivateObservable. + * @param cameraOrScene The camera that will be used in the post process. This camera will be used when calling onActivateObservable. You can also pass the scene if no camera is available. * @param sourceTexture The source texture to be inspected to get the width and height if not specified in the post process constructor. (default: null) * @param forceDepthStencil If true, a depth and stencil buffer will be generated. (default: false) * @returns The render target wrapper that was bound to be written to. */ - public activate(camera: Nullable, sourceTexture: Nullable = null, forceDepthStencil?: boolean): RenderTargetWrapper { - camera = camera || this._camera; + public activate(cameraOrScene: Nullable | Scene, sourceTexture: Nullable = null, forceDepthStencil?: boolean): RenderTargetWrapper { + const camera = cameraOrScene === null || (cameraOrScene as Camera).cameraRigMode !== undefined ? (cameraOrScene as Camera) || this._camera : null; - const scene = camera.getScene(); + const scene = camera?.getScene() ?? (cameraOrScene as Scene); const engine = scene.getEngine(); const maxSize = engine.getCaps().maxTextureSize; @@ -1003,7 +1003,7 @@ export class PostProcess { this._engine._debugInsertMarker?.(`post process ${this.name} input`); - this.onActivateObservable.notifyObservers(camera); + this.onActivateObservable.notifyObservers(camera!); // Clear if (this.autoClear && (this.alphaMode === Constants.ALPHA_DISABLE || this.forceAutoClearInAlphaMode)) { diff --git a/packages/dev/core/src/PostProcesses/postProcessManager.ts b/packages/dev/core/src/PostProcesses/postProcessManager.ts index f1bcbfbcce9..372c9e2c861 100644 --- a/packages/dev/core/src/PostProcesses/postProcessManager.ts +++ b/packages/dev/core/src/PostProcesses/postProcessManager.ts @@ -120,7 +120,7 @@ export class PostProcessManager { for (let index = 0; index < postProcesses.length; index++) { if (index < postProcesses.length - 1) { - postProcesses[index + 1].activate(this._scene.activeCamera, targetTexture?.texture); + postProcesses[index + 1].activate(this._scene.activeCamera || this._scene, targetTexture?.texture); } else { if (targetTexture) { engine.bindFramebuffer(targetTexture, faceIndex, undefined, undefined, forceFullscreenViewport, lodLevel); diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx index 4f863951f26..8a0b2014089 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx @@ -63,10 +63,19 @@ export class CommonShadowLightPropertyGridComponent extends React.Component 0) { + generator = shadowGenerators.values().next().value as ShadowGenerator | CascadedShadowGenerator; + } + } + + const csmGenerator = generator instanceof CascadedShadowGenerator; + const typeGeneratorOptions = [{ label: "Shadow Generator", value: 0 }]; if (light instanceof DirectionalLight) { diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx index d57aec5b559..2c7fd0e062f 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx @@ -37,7 +37,7 @@ export class DirectionalLightPropertyGridComponent extends React.Component { dlh.update(); @@ -47,8 +47,16 @@ export class DirectionalLightPropertyGridComponent extends React.Component 0) { + generator = shadowGenerators.values().next().value as ShadowGenerator | CascadedShadowGenerator; + } + } const hideAutoCalcShadowZBounds = generator instanceof CascadedShadowGenerator; const displayFrustum = (light as any)._displayFrustum ?? false; diff --git a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts index 3816f4dbb3f..8f238cf1146 100644 --- a/packages/tools/nodeRenderGraphEditor/src/blockTools.ts +++ b/packages/tools/nodeRenderGraphEditor/src/blockTools.ts @@ -21,6 +21,7 @@ import { NodeRenderGraphGUIBlock } from "gui/2D/FrameGraph/renderGraphGUIBlock"; import { NodeRenderGraphTAAObjectRendererBlock } from "core/FrameGraph/Node/Blocks/Rendering/taaObjectRendererBlock"; import { NodeRenderGraphResourceContainerBlock } from "core/FrameGraph/Node/Blocks/resourceContainerBlock"; import { NodeRenderGraphShadowGeneratorBlock } from "core/FrameGraph/Node/Blocks/Rendering/shadowGeneratorBlock"; +import { NodeRenderGraphCascadedShadowGeneratorBlock } from "core/FrameGraph/Node/Blocks/Rendering/csmShadowGeneratorBlock"; import type { FrameGraph } from "core/FrameGraph/frameGraph"; /** @@ -110,6 +111,9 @@ export class BlockTools { case "ShadowGeneratorBlock": { return new NodeRenderGraphShadowGeneratorBlock("Shadow Generator", frameGraph, scene); } + case "CascadedShadowGeneratorBlock": { + return new NodeRenderGraphCascadedShadowGeneratorBlock("Cascaded Shadow Generator", frameGraph, scene); + } } return null; diff --git a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx index 259a597d789..3b277315531 100644 --- a/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx +++ b/packages/tools/nodeRenderGraphEditor/src/components/nodeList/nodeListComponent.tsx @@ -49,6 +49,7 @@ export class NodeListComponent extends React.Component { - this._createNodeRenderGraph(); - }); - this._onRebuildRequiredObserver = globalState.stateManager.onRebuildRequiredObservable.add(() => { this._createNodeRenderGraph(); }); @@ -200,7 +196,7 @@ export class PreviewManager { if (this._globalState.directionalLight0) { const dir0 = new DirectionalLight("Directional light #0", new Vector3(0.841626576496605, -0.2193391004130599, -0.49351298337996535), this._scene); - dir0.intensity = 0.9; + dir0.intensity = 0.7; dir0.diffuse = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549); dir0.specular = new Color3(0.9294117647058824, 0.9725490196078431, 0.996078431372549); dir0.parent = this._lightParent; @@ -211,7 +207,7 @@ export class PreviewManager { if (this._globalState.directionalLight1) { const dir1 = new DirectionalLight("Directional light #1", new Vector3(-0.9519937437504213, -0.24389315636999764, -0.1849974057546125), this._scene); - dir1.intensity = 1.2; + dir1.intensity = 0.7; dir1.specular = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432); dir1.diffuse = new Color3(0.9803921568627451, 0.9529411764705882, 0.7725490196078432); dir1.parent = this._lightParent; @@ -393,6 +389,7 @@ export class PreviewManager { arcRotateCamera.lowerRadiusLimit = null; arcRotateCamera.upperRadiusLimit = null; framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max); + arcRotateCamera.maxZ = worldExtends.max.subtract(worldExtends.min).length() * 2; } arcRotateCamera.pinchPrecision = 200 / arcRotateCamera.radius; From 9b9ce6a2416bd75184bd636e9cd7dd04ccc0c4a0 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 6 Dec 2024 17:39:58 +0100 Subject: [PATCH 15/20] Fix properties of object renderer not saved --- .../Blocks/Rendering/objectRendererBlock.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts index ed363b6f32e..751c5641c33 100644 --- a/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts +++ b/packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/objectRendererBlock.ts @@ -53,6 +53,26 @@ export class NodeRenderGraphObjectRendererBlock extends NodeRenderGraphBaseObjec public override getClassName() { return "NodeRenderGraphObjectRendererBlock"; } + + protected override _dumpPropertiesCode() { + const codes: string[] = []; + codes.push(`${this._codeVariableName}.doNotChangeAspectRatio = ${this.doNotChangeAspectRatio};`); + codes.push(`${this._codeVariableName}.disableShadows = ${this.disableShadows};`); + return super._dumpPropertiesCode() + codes.join("\n"); + } + + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.doNotChangeAspectRatio = this.doNotChangeAspectRatio; + serializationObject.disableShadows = this.disableShadows; + return serializationObject; + } + + public override _deserialize(serializationObject: any) { + super._deserialize(serializationObject); + this.doNotChangeAspectRatio = serializationObject.doNotChangeAspectRatio; + this.disableShadows = serializationObject.disableShadows; + } } RegisterClass("BABYLON.NodeRenderGraphObjectRendererBlock", NodeRenderGraphObjectRendererBlock); From 056121796bb86a135bbb9ca1ae421197693d216a Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 6 Dec 2024 17:40:18 +0100 Subject: [PATCH 16/20] Fix crash when effectWrapper / effect not built --- packages/dev/core/src/Misc/copyTextureToTexture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/core/src/Misc/copyTextureToTexture.ts b/packages/dev/core/src/Misc/copyTextureToTexture.ts index f0de9348d11..acafd1826a2 100644 --- a/packages/dev/core/src/Misc/copyTextureToTexture.ts +++ b/packages/dev/core/src/Misc/copyTextureToTexture.ts @@ -106,7 +106,7 @@ export class CopyTextureToTexture { * @returns true if "copy" can be called without delay, else false */ public isReady(): boolean { - return this._shadersLoaded && this._effectWrapper.effect.isReady(); + return this._shadersLoaded && this._effectWrapper?.effect?.isReady(); } /** From 6e5a402fa26896d75fdacdde10926bca34c0c925 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 6 Dec 2024 17:45:29 +0100 Subject: [PATCH 17/20] Remove wrong code --- packages/dev/core/src/Engines/renderTargetWrapper.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/dev/core/src/Engines/renderTargetWrapper.ts b/packages/dev/core/src/Engines/renderTargetWrapper.ts index f238c912e18..b8c67d93ff6 100644 --- a/packages/dev/core/src/Engines/renderTargetWrapper.ts +++ b/packages/dev/core/src/Engines/renderTargetWrapper.ts @@ -55,17 +55,6 @@ export class RenderTargetWrapper { return this._depthStencilTexture; } - public set depthStencilTexture(texture: Nullable) { - this._depthStencilTexture = texture; - - this._generateDepthBuffer = this._generateStencilBuffer = false; - - if (texture) { - this._generateDepthBuffer = true; - this._generateStencilBuffer = HasStencilAspect(texture.format); - } - } - /** * Sets the depth/stencil texture * @param texture The depth/stencil texture to set From 2b3c32e6e10eeb9018dad78914773a74cdfe9d5e Mon Sep 17 00:00:00 2001 From: Popov72 Date: Fri, 6 Dec 2024 18:05:10 +0100 Subject: [PATCH 18/20] Fix typos --- .../dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts | 2 +- .../lights/commonShadowLightPropertyGridComponent.tsx | 2 +- .../lights/directionalLightPropertyGridComponent.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts index 98c43947d72..a52efd39ede 100644 --- a/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts +++ b/packages/dev/core/src/FrameGraph/Node/Types/nodeRenderGraphTypes.ts @@ -66,7 +66,7 @@ export enum NodeRenderGraphBlockConnectionPointTypes { TextureLocalPosition = 0x00004000, /** Linear velocity geometry texture */ TextureLinearVelocity = 0x00008000, - /** Linear velocity geometry texture */ + /** Storage texture */ StorageTexture = 0x00010000, /** Bit field for all textures but back buffer depth/stencil */ diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx index 8a0b2014089..c246c382200 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx @@ -67,7 +67,7 @@ export class CommonShadowLightPropertyGridComponent extends React.Component 0) { generator = shadowGenerators.values().next().value as ShadowGenerator | CascadedShadowGenerator; diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx index 2c7fd0e062f..82cad9275e3 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx @@ -51,7 +51,7 @@ export class DirectionalLightPropertyGridComponent extends React.Component 0) { generator = shadowGenerators.values().next().value as ShadowGenerator | CascadedShadowGenerator; From bf28df43cff9f1385ab7d4212ebee6d785e75b5d Mon Sep 17 00:00:00 2001 From: Popov72 Date: Sat, 7 Dec 2024 14:13:40 +0100 Subject: [PATCH 19/20] Use constants instead of hard values --- .../core/src/Debug/directionalLightFrustumViewer.ts | 13 +++++++++++-- packages/dev/core/src/Engines/constants.ts | 9 +++++++++ packages/dev/core/src/Lights/directionalLight.ts | 5 +++-- packages/dev/core/src/Lights/shadowLight.ts | 5 +++-- packages/dev/core/src/Lights/spotLight.ts | 5 +++-- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts b/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts index 5fc4c5f1838..739c916f2a2 100644 --- a/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts +++ b/packages/dev/core/src/Debug/directionalLightFrustumViewer.ts @@ -10,6 +10,7 @@ import { Mesh } from "../Meshes/mesh"; import { VertexData } from "../Meshes/mesh.vertexData"; import { TransformNode } from "../Meshes/transformNode"; import type { Scene } from "../scene"; +import { Constants } from "core/Engines/constants"; /** * Class used to render a debug view of the frustum for a directional light @@ -159,8 +160,16 @@ export class DirectionalLightFrustumViewer { this._oldMinZ = this._light.shadowMinZ; this._oldMaxZ = this._light.shadowMaxZ; - TmpVectors.Vector3[0].set(this._light.orthoLeft, this._light.orthoBottom, this._light.shadowMinZ !== undefined ? this._light.shadowMinZ : (this._camera?.minZ ?? 1)); // min light extents - TmpVectors.Vector3[1].set(this._light.orthoRight, this._light.orthoTop, this._light.shadowMaxZ !== undefined ? this._light.shadowMaxZ : (this._camera?.maxZ ?? 10000)); // max light extents + TmpVectors.Vector3[0].set( + this._light.orthoLeft, + this._light.orthoBottom, + this._light.shadowMinZ !== undefined ? this._light.shadowMinZ : (this._camera?.minZ ?? Constants.ShadowMinZ) + ); // min light extents + TmpVectors.Vector3[1].set( + this._light.orthoRight, + this._light.orthoTop, + this._light.shadowMaxZ !== undefined ? this._light.shadowMaxZ : (this._camera?.maxZ ?? Constants.ShadowMaxZ) + ); // max light extents const invLightView = this._getInvertViewMatrix(); diff --git a/packages/dev/core/src/Engines/constants.ts b/packages/dev/core/src/Engines/constants.ts index 2b942196d63..ae6a51a7ca9 100644 --- a/packages/dev/core/src/Engines/constants.ts +++ b/packages/dev/core/src/Engines/constants.ts @@ -948,4 +948,13 @@ export class Constants { * Additional matrix weights (for bones) */ public static MatricesWeightsExtraKind = "matricesWeightsExtra"; + + /** + * The default minZ value for the near plane of a frustum light + */ + public static ShadowMinZ = 0; + /** + * The default maxZ value for the far plane of a frustum light + */ + public static ShadowMaxZ = 10000; } diff --git a/packages/dev/core/src/Lights/directionalLight.ts b/packages/dev/core/src/Lights/directionalLight.ts index e6485dc6361..264266fc8ef 100644 --- a/packages/dev/core/src/Lights/directionalLight.ts +++ b/packages/dev/core/src/Lights/directionalLight.ts @@ -9,6 +9,7 @@ import { ShadowLight } from "./shadowLight"; import type { Effect } from "../Materials/effect"; import { RegisterClass } from "../Misc/typeStore"; import type { Nullable } from "../types"; +import { Constants } from "core/Engines/constants"; Node.AddNodeConstructor("Light_Type_1", (name, scene) => { return () => new DirectionalLight(name, Vector3.Zero(), scene); @@ -261,8 +262,8 @@ export class DirectionalLight extends ShadowLight { const xOffset = this._orthoRight - this._orthoLeft; const yOffset = this._orthoTop - this._orthoBottom; - const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; - const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; + const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || Constants.ShadowMinZ; + const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || Constants.ShadowMaxZ; const useReverseDepthBuffer = this.getScene().getEngine().useReverseDepthBuffer; diff --git a/packages/dev/core/src/Lights/shadowLight.ts b/packages/dev/core/src/Lights/shadowLight.ts index 7081bf3cc85..c20a17f2a92 100644 --- a/packages/dev/core/src/Lights/shadowLight.ts +++ b/packages/dev/core/src/Lights/shadowLight.ts @@ -6,6 +6,7 @@ import type { AbstractMesh } from "../Meshes/abstractMesh"; import { Light } from "./light"; import { Axis } from "../Maths/math.axis"; import type { Nullable } from "core/types"; +import { Constants } from "core/Engines/constants"; /** * Interface describing all the common properties and methods a shadow light needs to implement. * This helps both the shadow generator and materials to generate the corresponding shadow maps @@ -361,7 +362,7 @@ export abstract class ShadowLight extends Light implements IShadowLight { * @returns the depth min z */ public getDepthMinZ(activeCamera: Nullable): number { - return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; + return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || Constants.ShadowMinZ; } /** @@ -370,7 +371,7 @@ export abstract class ShadowLight extends Light implements IShadowLight { * @returns the depth max z */ public getDepthMaxZ(activeCamera: Nullable): number { - return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; + return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || Constants.ShadowMaxZ; } /** diff --git a/packages/dev/core/src/Lights/spotLight.ts b/packages/dev/core/src/Lights/spotLight.ts index 8effd897515..7dd11771008 100644 --- a/packages/dev/core/src/Lights/spotLight.ts +++ b/packages/dev/core/src/Lights/spotLight.ts @@ -12,6 +12,7 @@ import { Texture } from "../Materials/Textures/texture"; import type { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralTexture"; import type { Camera } from "../Cameras/camera"; import { RegisterClass } from "../Misc/typeStore"; +import { Constants } from "core/Engines/constants"; Node.AddNodeConstructor("Light_Type_2", (name, scene) => { return () => new SpotLight(name, Vector3.Zero(), Vector3.Zero(), 0, 0, scene); @@ -480,7 +481,7 @@ export class SpotLight extends ShadowLight { */ public override getDepthMinZ(activeCamera: Nullable): number { const engine = this._scene.getEngine(); - const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera?.minZ || 1; + const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : (activeCamera?.minZ ?? Constants.ShadowMinZ); return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? minZ : this._scene.getEngine().isNDCHalfZRange ? 0 : minZ; } @@ -492,7 +493,7 @@ export class SpotLight extends ShadowLight { */ public override getDepthMaxZ(activeCamera: Nullable): number { const engine = this._scene.getEngine(); - const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera?.maxZ || 10000; + const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : (activeCamera?.maxZ ?? Constants.ShadowMaxZ); return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : maxZ; } From d4a36feb0b18d40c848a742357071eba17a297d0 Mon Sep 17 00:00:00 2001 From: Popov72 Date: Mon, 9 Dec 2024 17:50:08 +0100 Subject: [PATCH 20/20] Add TODO for the _disabledEngineStages property --- packages/dev/core/src/Materials/Textures/renderTargetTexture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts b/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts index f4a5aea22bc..9f78d104906 100644 --- a/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts +++ b/packages/dev/core/src/Materials/Textures/renderTargetTexture.ts @@ -493,7 +493,7 @@ export class RenderTargetTexture extends Texture implements IRenderTargetTexture } /** @internal */ - public _disableEngineStages = false; + public _disableEngineStages = false; // TODO: remove this when the shadow generator task (frame graph) is reworked (see https://github.com/BabylonJS/Babylon.js/pull/15962#discussion_r1874417607) /** * Instantiate a render target texture. This is mainly used to render of screen the scene to for instance apply post process