Skip to content

Commit 9e365f3

Browse files
authored
FrameGraph: add new volumetric lighting task (#17515)
Also in this PR: * Fix crash when depth/stencil attachment has no depth in WebGPU * Slightly refactor `IStencilState` * Fix wrong error message when the last disabled pass is not a render pass * `LightingVolume`: Fix crash in WebGL when updating the **buildFullVolume** property
1 parent 591d62d commit 9e365f3

29 files changed

+1137
-31
lines changed

packages/dev/core/src/Engines/abstractEngine.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import type { ThinTexture } from "../Materials/Textures/thinTexture";
2727
import type { InternalTextureCreationOptions, TextureSize } from "../Materials/Textures/textureCreationOptions";
2828
import type { EffectFallbacks } from "../Materials/effectFallbacks";
2929
import type { IMaterialContext } from "./IMaterialContext";
30-
import type { IStencilState } from "../States/IStencilState";
30+
import type { IStencilStateProperties, IStencilState } from "../States/IStencilState";
3131
import type { DrawWrapper } from "../Materials/drawWrapper";
3232
import type { IDrawContext } from "./IDrawContext";
3333
import type { VertexBuffer } from "../Meshes/buffer";
@@ -1363,7 +1363,7 @@ export abstract class AbstractEngine {
13631363
force?: boolean,
13641364
reverseSide?: boolean,
13651365
cullBackFaces?: boolean,
1366-
stencil?: IStencilState,
1366+
stencil?: IStencilState | IStencilStateProperties,
13671367
zOffsetUnits?: number
13681368
): void;
13691369

packages/dev/core/src/Engines/webgpuEngine.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
31363136
// We use the MSAA texture format (if available) to determine if it has a stencil aspect or not because, for MSAA depth textures,
31373137
// the format of the "resolve" texture (gpuDepthStencilWrapper.format) is a single red channel format, not a depth-stencil format.
31383138
const depthTextureHasStencil = gpuDepthStencilWrapper ? WebGPUTextureHelper.HasStencilAspect(gpuDepthStencilMSAATexture?.format ?? gpuDepthStencilWrapper.format) : false;
3139+
const depthTextureHasDepth = gpuDepthStencilWrapper ? WebGPUTextureHelper.HasDepthAspect(gpuDepthStencilMSAATexture?.format ?? gpuDepthStencilWrapper.format) : false;
31393140

31403141
const colorAttachments: (GPURenderPassColorAttachment | null)[] = [];
31413142

@@ -3240,18 +3241,17 @@ export class WebGPUEngine extends ThinWebGPUEngine {
32403241
? {
32413242
view: depthMSAATextureView ? depthMSAATextureView : depthTextureView!,
32423243
depthClearValue: mustClearDepth ? (this.useReverseDepthBuffer ? this._clearReverseDepthValue : this._clearDepthValue) : undefined,
3243-
depthLoadOp: rtWrapper.depthReadOnly ? undefined : mustClearDepth ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
3244-
depthStoreOp: rtWrapper.depthReadOnly ? undefined : WebGPUConstants.StoreOp.Store,
3244+
depthLoadOp: rtWrapper.depthReadOnly || !depthTextureHasDepth ? undefined : mustClearDepth ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
3245+
depthStoreOp: rtWrapper.depthReadOnly || !depthTextureHasDepth ? undefined : WebGPUConstants.StoreOp.Store,
32453246
depthReadOnly: rtWrapper.depthReadOnly,
32463247
stencilClearValue: rtWrapper._depthStencilTextureWithStencil && mustClearStencil ? this._clearStencilValue : undefined,
3247-
stencilLoadOp: rtWrapper.stencilReadOnly
3248-
? undefined
3249-
: !depthTextureHasStencil
3250-
? undefined
3251-
: rtWrapper._depthStencilTextureWithStencil && mustClearStencil
3252-
? WebGPUConstants.LoadOp.Clear
3253-
: WebGPUConstants.LoadOp.Load,
3254-
stencilStoreOp: rtWrapper.stencilReadOnly ? undefined : !depthTextureHasStencil ? undefined : WebGPUConstants.StoreOp.Store,
3248+
stencilLoadOp:
3249+
rtWrapper.stencilReadOnly || !depthTextureHasStencil
3250+
? undefined
3251+
: rtWrapper._depthStencilTextureWithStencil && mustClearStencil
3252+
? WebGPUConstants.LoadOp.Clear
3253+
: WebGPUConstants.LoadOp.Load,
3254+
stencilStoreOp: rtWrapper.stencilReadOnly || !depthTextureHasStencil ? undefined : WebGPUConstants.StoreOp.Store,
32553255
stencilReadOnly: rtWrapper.stencilReadOnly,
32563256
}
32573257
: undefined,
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import type {
2+
Camera,
3+
DirectionalLight,
4+
FrameGraph,
5+
FrameGraphObjectList,
6+
FrameGraphTextureHandle,
7+
NodeRenderGraphBuildState,
8+
NodeRenderGraphConnectionPoint,
9+
Scene,
10+
} from "core/index";
11+
import { RegisterClass } from "../../../../Misc/typeStore";
12+
import { editableInPropertyPage, PropertyTypeForEdition } from "../../../../Decorators/nodeDecorator";
13+
import { NodeRenderGraphBlock } from "../../nodeRenderGraphBlock";
14+
import { FrameGraphVolumetricLightingTask } from "core/FrameGraph/Tasks/PostProcesses/volumetricLightingTask";
15+
import { NodeRenderGraphBlockConnectionPointTypes } from "../../Types/nodeRenderGraphTypes";
16+
import { Vector3 } from "core/Maths/math.vector";
17+
import { Color3 } from "core/Maths/math.color";
18+
19+
/**
20+
* Block that implements the volumetric lighting post process
21+
*/
22+
export class NodeRenderGraphVolumetricLightingBlock extends NodeRenderGraphBlock {
23+
protected override _frameGraphTask: FrameGraphVolumetricLightingTask;
24+
25+
public override _additionalConstructionParameters: [boolean];
26+
27+
/**
28+
* Gets the frame graph task associated with this block
29+
*/
30+
public override get task() {
31+
return this._frameGraphTask;
32+
}
33+
34+
/**
35+
* Create a new NodeRenderGraphVolumetricLightingBlock
36+
* @param name defines the block name
37+
* @param frameGraph defines the hosting frame graph
38+
* @param scene defines the hosting scene
39+
* @param enableExtinction defines whether to enable extinction coefficients
40+
*/
41+
public constructor(name: string, frameGraph: FrameGraph, scene: Scene, enableExtinction = false) {
42+
super(name, frameGraph, scene);
43+
44+
this._additionalConstructionParameters = [enableExtinction];
45+
46+
this.registerInput("target", NodeRenderGraphBlockConnectionPointTypes.AutoDetect);
47+
this.registerInput("depth", NodeRenderGraphBlockConnectionPointTypes.AutoDetect);
48+
this.registerInput("camera", NodeRenderGraphBlockConnectionPointTypes.Camera);
49+
this.registerInput("lightingVolumeMesh", NodeRenderGraphBlockConnectionPointTypes.ObjectList);
50+
this.registerInput("light", NodeRenderGraphBlockConnectionPointTypes.ShadowLight);
51+
this.registerInput("lightingVolumeTexture", NodeRenderGraphBlockConnectionPointTypes.AutoDetect, true);
52+
53+
this.target.addExcludedConnectionPointFromAllowedTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer);
54+
this.lightingVolumeTexture.addExcludedConnectionPointFromAllowedTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer);
55+
56+
this.depth.addExcludedConnectionPointFromAllowedTypes(
57+
NodeRenderGraphBlockConnectionPointTypes.TextureDepthStencilAttachment | NodeRenderGraphBlockConnectionPointTypes.TextureScreenDepth
58+
);
59+
60+
this._addDependenciesInput();
61+
62+
this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.BasedOnInput);
63+
64+
this.output._typeConnectionSource = () => {
65+
return this.target;
66+
};
67+
68+
this._frameGraphTask = new FrameGraphVolumetricLightingTask(name, frameGraph, enableExtinction);
69+
}
70+
71+
private _createTask(enableExtinction: boolean) {
72+
const sourceSamplingMode = this._frameGraphTask.sourceSamplingMode;
73+
const phaseG = this._frameGraphTask.phaseG;
74+
const extinction = this._frameGraphTask.extinction;
75+
const lightPower = this._frameGraphTask.lightPower;
76+
77+
this._frameGraphTask.dispose();
78+
79+
this._frameGraphTask = new FrameGraphVolumetricLightingTask(this.name, this._frameGraph, enableExtinction);
80+
this._frameGraphTask.sourceSamplingMode = sourceSamplingMode;
81+
this._frameGraphTask.phaseG = phaseG;
82+
this._frameGraphTask.extinction = extinction;
83+
this._frameGraphTask.lightPower = lightPower;
84+
85+
this._additionalConstructionParameters = [enableExtinction];
86+
}
87+
88+
/** Gets or sets the phaseG parameter */
89+
@editableInPropertyPage("PhaseG", PropertyTypeForEdition.Float, "PROPERTIES", { min: -0.9, max: 0.9 })
90+
public get phaseG(): number {
91+
return this._frameGraphTask.phaseG;
92+
}
93+
94+
public set phaseG(value: number) {
95+
this._frameGraphTask.phaseG = value;
96+
}
97+
98+
/** If extinction coefficients should be used */
99+
@editableInPropertyPage("Enable extinction", PropertyTypeForEdition.Boolean, "PROPERTIES")
100+
public get enableExtinction(): boolean {
101+
return this._frameGraphTask.enableExtinction;
102+
}
103+
104+
public set enableExtinction(value: boolean) {
105+
this._createTask(value);
106+
}
107+
108+
/** Gets or sets the extinction color */
109+
@editableInPropertyPage("Extinction", PropertyTypeForEdition.Vector3, "PROPERTIES")
110+
public get extinction(): Vector3 {
111+
return this._frameGraphTask.extinction;
112+
}
113+
114+
public set extinction(value: Vector3) {
115+
this._frameGraphTask.extinction = value;
116+
}
117+
118+
/** Gets or sets the light power */
119+
@editableInPropertyPage("Light power", PropertyTypeForEdition.Color3, "PROPERTIES")
120+
public get lightPower(): Color3 {
121+
return this._frameGraphTask.lightPower;
122+
}
123+
124+
public set lightPower(value: Color3) {
125+
this._frameGraphTask.lightPower = value;
126+
}
127+
128+
/**
129+
* Gets the current class name
130+
* @returns the class name
131+
*/
132+
public override getClassName() {
133+
return "NodeRenderGraphVolumetricLightingBlock";
134+
}
135+
136+
/**
137+
* Gets the target input component
138+
*/
139+
public get target(): NodeRenderGraphConnectionPoint {
140+
return this._inputs[0];
141+
}
142+
143+
/**
144+
* Gets the depth texture input component
145+
*/
146+
public get depth(): NodeRenderGraphConnectionPoint {
147+
return this._inputs[1];
148+
}
149+
150+
/**
151+
* Gets the camera input component
152+
*/
153+
public get camera(): NodeRenderGraphConnectionPoint {
154+
return this._inputs[2];
155+
}
156+
157+
/**
158+
* Gets the lighting volume mesh input component
159+
*/
160+
public get lightingVolumeMesh(): NodeRenderGraphConnectionPoint {
161+
return this._inputs[3];
162+
}
163+
164+
/**
165+
* Gets the light input component
166+
*/
167+
public get light(): NodeRenderGraphConnectionPoint {
168+
return this._inputs[4];
169+
}
170+
171+
/**
172+
* Gets the lighting volume texture input component
173+
*/
174+
public get lightingVolumeTexture(): NodeRenderGraphConnectionPoint {
175+
return this._inputs[5];
176+
}
177+
178+
/**
179+
* Gets the output component
180+
*/
181+
public get output(): NodeRenderGraphConnectionPoint {
182+
return this._outputs[0];
183+
}
184+
185+
protected override _buildBlock(state: NodeRenderGraphBuildState) {
186+
super._buildBlock(state);
187+
188+
this.output.value = this._frameGraphTask.outputTexture;
189+
190+
this._frameGraphTask.targetTexture = this.target.connectedPoint?.value as FrameGraphTextureHandle;
191+
this._frameGraphTask.depthTexture = this.depth.connectedPoint?.value as FrameGraphTextureHandle;
192+
this._frameGraphTask.camera = this.camera.connectedPoint?.value as Camera;
193+
this._frameGraphTask.lightingVolumeMesh = this.lightingVolumeMesh.connectedPoint?.value as FrameGraphObjectList;
194+
this._frameGraphTask.light = this.light.connectedPoint?.value as DirectionalLight;
195+
this._frameGraphTask.lightingVolumeTexture = this.lightingVolumeTexture.connectedPoint?.value as FrameGraphTextureHandle;
196+
}
197+
198+
protected override _dumpPropertiesCode() {
199+
const codes: string[] = [];
200+
codes.push(`${this._codeVariableName}.phaseG = ${this.phaseG};`);
201+
codes.push(`${this._codeVariableName}.extinction = new BABYLON.Vector3(${this.extinction.x}, ${this.extinction.y}, ${this.extinction.z});`);
202+
codes.push(`${this._codeVariableName}.lightPower = new Color3(${this.lightPower.r}, ${this.lightPower.g}, ${this.lightPower.b});`);
203+
return super._dumpPropertiesCode() + codes.join("\n");
204+
}
205+
206+
public override serialize(): any {
207+
const serializationObject = super.serialize();
208+
serializationObject.phaseG = this.phaseG;
209+
serializationObject.extinction = this.extinction.asArray();
210+
serializationObject.lightPower = this.lightPower.asArray();
211+
return serializationObject;
212+
}
213+
214+
public override _deserialize(serializationObject: any) {
215+
super._deserialize(serializationObject);
216+
this.phaseG = serializationObject.phaseG;
217+
this.extinction = Vector3.FromArray(serializationObject.extinction);
218+
this.lightPower = Color3.FromArray(serializationObject.lightPower);
219+
}
220+
}
221+
222+
RegisterClass("BABYLON.NodeRenderGraphVolumetricLightingBlock", NodeRenderGraphVolumetricLightingBlock);

packages/dev/core/src/FrameGraph/Node/Blocks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from "./cullObjectsBlock";
33
export * from "./elbowBlock";
44
export * from "./executeBlock";
55
export * from "./inputBlock";
6+
export * from "./lightingVolumeBlock";
67
export * from "./outputBlock";
78
export * from "./resourceContainerBlock";
89

@@ -33,6 +34,7 @@ export * from "./PostProcesses/ssao2PostProcessBlock";
3334
export * from "./PostProcesses/ssrPostProcessBlock";
3435
export * from "./PostProcesses/taaPostProcessBlock";
3536
export * from "./PostProcesses/tonemapPostProcessBlock";
37+
export * from "./PostProcesses/volumetricLightingBlock";
3638

3739
export * from "./Rendering/csmShadowGeneratorBlock";
3840
export * from "./Rendering/geometryRendererBlock";

0 commit comments

Comments
 (0)