@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
212 lines (211 loc) • 12.3 kB
JavaScript
import { FrameGraphVolumetricLightingBlendVolumeTask } from "./volumetricLightingBlendVolumeTask.js";
import { Matrix, TmpVectors, Vector2, Vector3, Vector4 } from "../../../Maths/math.vector.js";
import { Color3, Color4 } from "../../../Maths/math.color.js";
import { FrameGraphTask } from "../../frameGraphTask.js";
import { FrameGraphClearTextureTask } from "../Texture/clearTextureTask.js";
import { FrameGraphObjectRendererTask } from "../Rendering/objectRendererTask.js";
import { ShaderMaterial } from "../../../Materials/shaderMaterial.js";
const InvViewProjectionMatrix = new Matrix();
/**
* A frame graph task that performs volumetric lighting.
*/
export class FrameGraphVolumetricLightingTask extends FrameGraphTask {
/**
* Returns whether volumetric lighting is supported by the engine.
* @param engine The engine to check for volumetric lighting support.
* @param enableExtinction Whether the extinction/dual-source blending path will be used.
* @returns True if volumetric lighting is supported, false otherwise.
*/
static IsSupported(engine, enableExtinction = false) {
return !enableExtinction || engine.getCaps().dualSourceBlending;
}
/**
* The camera used for volumetric lighting calculations.
*/
get camera() {
return this._camera;
}
set camera(value) {
if (this._camera === value) {
return;
}
this._camera = value;
this._renderLightingVolumeTask.camera = value;
this._blendLightingVolumeTask.camera = value;
}
/**
* The phase G parameter for the volumetric lighting effect (default: 0).
* This parameter controls the anisotropy of the scattering.
* A value of 0 means isotropic scattering, while a value of 1 means forward scattering and -1 means backward scattering.
*/
get phaseG() {
return this._extinctionPhaseG.w;
}
set phaseG(value) {
this._extinctionPhaseG.w = value;
this._renderLightingVolumeMaterial.setVector4("extinctionPhaseG", this._extinctionPhaseG);
}
/**
* The extinction coefficient for the volumetric lighting effect (default: (0, 0, 0) - no extinction).
* This parameter controls how much light is absorbed and scattered as it travels through the medium.
* Will only have an effect if enableExtinction is set to true in the constructor!
*/
get extinction() {
return this._blendLightingVolumeTask.postProcess.extinction;
}
set extinction(value) {
this._extinctionPhaseG.x = Math.max(value.x, 1e-6);
this._extinctionPhaseG.y = Math.max(value.y, 1e-6);
this._extinctionPhaseG.z = Math.max(value.z, 1e-6);
this._renderLightingVolumeMaterial.setVector4("extinctionPhaseG", this._extinctionPhaseG);
this._blendLightingVolumeTask.postProcess.extinction.copyFromFloats(this._extinctionPhaseG.x, this._extinctionPhaseG.y, this._extinctionPhaseG.z);
}
/**
* The light power/color for the volumetric lighting effect (default: (1, 1, 1)).
* This parameter controls the intensity and color of the light used for volumetric lighting.
*/
get lightPower() {
return this._lightPower;
}
set lightPower(value) {
this._lightPower.copyFrom(value);
this._renderLightingVolumeMaterial.setColor3("lightPower", this._lightPower);
}
get name() {
return this._name;
}
set name(name) {
this._name = name;
if (this._renderLightingVolumeMaterial) {
this._renderLightingVolumeMaterial.name = `${name} - render lighting volume`;
}
if (this._clearLightingVolumeTextureTask) {
this._clearLightingVolumeTextureTask.name = `${name} - clear lighting volume texture`;
}
if (this._renderLightingVolumeTask) {
this._renderLightingVolumeTask.name = `${name} - render lighting volume`;
}
if (this._blendLightingVolumeTask) {
this._blendLightingVolumeTask.name = `${name} - blend lighting volume`;
}
}
/**
* Creates a new FrameGraphVolumetricLightingTask.
* @param name The name of the task.
* @param frameGraph The frame graph to which the task belongs.
* @param enableExtinction Whether to enable extinction in the volumetric lighting effect (default: false). If you don't plan to set extinction to something different than (0, 0, 0), you can disable this to save some performance.
*/
constructor(name, frameGraph, enableExtinction = false) {
super(name, frameGraph);
/**
* The sampling mode to use when blending the volumetric lighting texture with targetTexture.
*/
this.sourceSamplingMode = 2;
this._extinctionPhaseG = new Vector4(0, 0, 0, 0);
this._lightPower = new Color3(1, 1, 1);
if (!FrameGraphVolumetricLightingTask.IsSupported(frameGraph.engine, enableExtinction)) {
throw new Error(`FrameGraphVolumetricLightingTask "${name}": the current configuration is not supported. Use FrameGraphVolumetricLightingTask.IsSupported(engine, enableExtinction) to check before creating this task.`);
}
this.enableExtinction = enableExtinction;
const isWebGPU = this._frameGraph.engine.isWebGPU;
this._renderLightingVolumeMaterial = new ShaderMaterial(`${name} - render lighting volume`, this._frameGraph.scene, "volumetricLightingRenderVolume", {
attributes: ["position"],
uniformBuffers: ["Scene", "Mesh"],
uniforms: ["world", "viewProjection", "vEyePosition", "lightDir", "invViewProjection", "outputTextureSize", "extinctionPhaseG", "lightPower", "textureRatio"],
samplers: ["depthTexture"],
defines: enableExtinction ? ["USE_EXTINCTION"] : [],
shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
needAlphaBlending: true,
});
this._renderLightingVolumeMaterial.backFaceCulling = false;
this._renderLightingVolumeMaterial.alphaMode = 1;
this._renderLightingVolumeMaterial.onBindObservable.add(() => {
this._renderLightingVolumeMaterial.bindEyePosition(this._renderLightingVolumeMaterial.getEffect());
});
this._clearLightingVolumeTextureTask = new FrameGraphClearTextureTask(`clear lighting volume texture`, frameGraph);
this._renderLightingVolumeTask = new FrameGraphObjectRendererTask(`render lighting volume`, frameGraph, frameGraph.scene);
this._blendLightingVolumeTask = new FrameGraphVolumetricLightingBlendVolumeTask(`blend lighting volume texture`, frameGraph, enableExtinction);
this.onTexturesAllocatedObservable.add(() => {
this._renderLightingVolumeMaterial.setInternalTexture("depthTexture", frameGraph.textureManager.getTextureFromHandle(this.depthTexture));
});
this.outputTexture = this._frameGraph.textureManager.createDanglingHandle();
// Triggers the setters to set the uniforms
this.phaseG = this._extinctionPhaseG.w;
this.extinction = new Vector3(this.extinction.x, this.extinction.y, this.extinction.z);
this.lightPower = this._lightPower;
}
// eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
initAsync() {
if (this._frameGraph.engine.isWebGPU) {
return Promise.all([import("../../../ShadersWGSL/volumetricLightingRenderVolume.vertex.js"), import("../../../ShadersWGSL/volumetricLightingRenderVolume.fragment.js")]);
}
return Promise.all([import("../../../Shaders/volumetricLightingRenderVolume.vertex.js"), import("../../../Shaders/volumetricLightingRenderVolume.fragment.js")]);
}
isReady() {
return (this._renderLightingVolumeMaterial.isReady() &&
this._clearLightingVolumeTextureTask.isReady() &&
this._renderLightingVolumeMaterial.isReady() &&
this._blendLightingVolumeTask.isReady());
}
getClassName() {
return "FrameGraphVolumetricLightingTask";
}
record(skipCreationOfDisabledPasses = false) {
if (this.targetTexture === undefined || this.depthTexture === undefined || this.camera === undefined || this.lightingVolumeMesh === undefined || this.light === undefined) {
throw new Error(`FrameGraphVolumetricLightingTask "${this.name}": targetTexture, depthTexture, camera, lightingVolumeMesh and light are required`);
}
if (!this.lightingVolumeMesh.meshes || this.lightingVolumeMesh.meshes.length === 0) {
throw new Error(`FrameGraphVolumetricLightingTask "${this.name}": lightingVolumeMesh is empty`);
}
this._frameGraph.textureManager.resolveDanglingHandle(this.outputTexture, this.targetTexture);
const textureManager = this._frameGraph.textureManager;
let lightingVolumeTexture = this.lightingVolumeTexture;
if (!lightingVolumeTexture) {
const targetTextureCreationOptions = textureManager.getTextureCreationOptions(this.targetTexture);
targetTextureCreationOptions.options.labels = ["InScattering"];
targetTextureCreationOptions.options.samples = 1;
lightingVolumeTexture = textureManager.createRenderTargetTexture(`${this.name} - lighting volume texture`, targetTextureCreationOptions);
}
this.lightingVolumeMesh.meshes[0].material = this._renderLightingVolumeMaterial;
const targetTextureSize = textureManager.getTextureAbsoluteDimensions(this.targetTexture);
const volumeTextureSize = textureManager.getTextureAbsoluteDimensions(lightingVolumeTexture);
this._renderLightingVolumeMaterial.setVector2("textureRatio", new Vector2(targetTextureSize.width / volumeTextureSize.width, targetTextureSize.height / volumeTextureSize.height));
this._renderLightingVolumeMaterial.setVector2("outputTextureSize", new Vector2(volumeTextureSize.width, volumeTextureSize.height));
const passUpdateMaterial = this._frameGraph.addPass(this.name);
passUpdateMaterial.setExecuteFunc(() => {
this.camera.getTransformationMatrix().invertToRef(InvViewProjectionMatrix);
this._renderLightingVolumeMaterial.setMatrix("invViewProjection", InvViewProjectionMatrix);
this._renderLightingVolumeMaterial.setVector3("lightDir", this.light.direction.normalizeToRef(TmpVectors.Vector3[0]));
});
this._clearLightingVolumeTextureTask.clearColor = true;
this._clearLightingVolumeTextureTask.clearStencil = this.enableExtinction;
this._clearLightingVolumeTextureTask.color = new Color4(0, 0, 0, 1);
this._clearLightingVolumeTextureTask.stencilValue = 0;
this._clearLightingVolumeTextureTask.targetTexture = lightingVolumeTexture;
this._clearLightingVolumeTextureTask.record(true);
this._renderLightingVolumeTask.targetTexture = this._clearLightingVolumeTextureTask.outputTexture;
this._renderLightingVolumeTask.objectList = this.lightingVolumeMesh;
this._renderLightingVolumeTask.camera = this.camera;
this._renderLightingVolumeTask.disableImageProcessing = true;
this._renderLightingVolumeTask.depthTest = false;
this._renderLightingVolumeTask.record(true);
this._blendLightingVolumeTask.sourceTexture = this._renderLightingVolumeTask.outputTexture;
this._blendLightingVolumeTask.sourceSamplingMode = this.sourceSamplingMode;
this._blendLightingVolumeTask.targetTexture = this.targetTexture;
this._blendLightingVolumeTask.depthTexture = this.depthTexture;
this._blendLightingVolumeTask.camera = this.camera;
this._blendLightingVolumeTask.record(true);
if (!skipCreationOfDisabledPasses) {
const disabledPass = this._frameGraph.addPass(this.name + "_disabled", true);
disabledPass.setExecuteFunc(() => { });
}
}
dispose() {
this._renderLightingVolumeMaterial?.dispose();
this._clearLightingVolumeTextureTask.dispose();
this._renderLightingVolumeTask.dispose();
this._blendLightingVolumeTask.dispose();
super.dispose();
}
}
//# sourceMappingURL=volumetricLightingTask.js.map