UNPKG

@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
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