diff --git a/packages/dev/addons/src/atmosphere/Shaders/ShadersInclude/atmosphereFunctions.fx b/packages/dev/addons/src/atmosphere/Shaders/ShadersInclude/atmosphereFunctions.fx index 1119bcaff24..a9ed6fb74f7 100644 --- a/packages/dev/addons/src/atmosphere/Shaders/ShadersInclude/atmosphereFunctions.fx +++ b/packages/dev/addons/src/atmosphere/Shaders/ShadersInclude/atmosphereFunctions.fx @@ -332,7 +332,7 @@ const float uniformPhase = RECIPROCAL_PI4; // Utilizes the transmittance LUT and multiple scattering LUT to compute the radiance and transmittance for a given ray. #define inline -void integrateScatteredRadiance( +vec3 integrateScatteredRadiance( bool isAerialPerspectiveLut, float lightIntensity, sampler2D transmittanceLut, @@ -346,14 +346,13 @@ void integrateScatteredRadiance( float tMaxMax, float sampleCount, float distanceToSurface, - out vec3 radiance, out vec3 transmittance #if COMPUTE_MULTI_SCATTERING , out vec3 multiScattering #endif ) { - radiance = vec3(0.); + vec3 radiance = vec3(0.); transmittance = vec3(1.); #if COMPUTE_MULTI_SCATTERING multiScattering = vec3(0.); @@ -366,7 +365,7 @@ void integrateScatteredRadiance( if (tBottom < 0.) { if (tTop < 0.) { // No intersection with the atmosphere or the planet, so early out. - return; + return radiance; } else { // Didn't intersect the planet, but did intersect the atmosphere. tMax = tTop; @@ -456,6 +455,8 @@ void integrateScatteredRadiance( radiance *= lightIntensity; + return radiance; + } #endif @@ -632,10 +633,9 @@ vec4 renderMultiScattering(vec2 uv, sampler2D transmittanceLut) { float sinInclination; vec3 rayDirection = getSphereSample(azimuth, inclination, sinInclination); - vec3 radiance; vec3 transmittance; vec3 multiScattering; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( false, // isAerialPerspectiveLut 1., // No light intensity; it will be applied in downstream LUTs (AerialPerspective, SkyView, and DiffuseSkyIrradiance). transmittanceLut, @@ -645,7 +645,6 @@ vec4 renderMultiScattering(vec2 uv, sampler2D transmittanceLut) { 100000000., MultiScatteringLutSampleCount, -1., // No planet hit. - radiance, transmittance, multiScattering); @@ -737,8 +736,7 @@ vec4 renderSkyView(vec2 uv, sampler2D transmittanceLut, sampler2D multiScatterin } vec3 transmittance; - vec3 radiance; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( false, // isAerialPerspectiveLut atmosphereExposure * lightIntensity, transmittanceLut, @@ -750,7 +748,6 @@ vec4 renderSkyView(vec2 uv, sampler2D transmittanceLut, sampler2D multiScatterin 100000000., SkyViewLutSampleCount, -1., // No planet hit. - radiance, transmittance); float transparency = 1. - avg(transmittance); @@ -804,8 +801,7 @@ vec4 renderCameraVolume( float sampleCount = min(SkyViewLutSampleCount, 2. * layer + 2.); vec3 transmittance; - vec3 radiance; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( true, // isAerialPerspectiveLut lightIntensity, transmittanceLut, @@ -817,7 +813,6 @@ vec4 renderCameraVolume( tMaxMax, sampleCount, -1., // No planet hit. - radiance, transmittance); float transparency = 1. - avg(transmittance); diff --git a/packages/dev/addons/src/atmosphere/Shaders/compositeAerialPerspective.fragment.fx b/packages/dev/addons/src/atmosphere/Shaders/compositeAerialPerspective.fragment.fx index afb8b0baa8d..6d50edbb0b8 100644 --- a/packages/dev/addons/src/atmosphere/Shaders/compositeAerialPerspective.fragment.fx +++ b/packages/dev/addons/src/atmosphere/Shaders/compositeAerialPerspective.fragment.fx @@ -79,10 +79,9 @@ void main() { return; } - vec3 transmittance; - vec3 radiance; bool isAerialPerspectiveLut = clampedCameraRadius < atmosphereRadius; - integrateScatteredRadiance( + vec3 transmittance; + vec3 radiance = integrateScatteredRadiance( isAerialPerspectiveLut, // isAerialPerspectiveLut atmosphereExposure * lightIntensity, transmittanceLut, @@ -94,7 +93,6 @@ void main() { 100000000., SkyViewLutSampleCount, distanceToSurface, - radiance, transmittance); float transparency = 1. - avg(transmittance); diff --git a/packages/dev/addons/src/atmosphere/Shaders/compositeGlobeAtmosphere.fragment.fx b/packages/dev/addons/src/atmosphere/Shaders/compositeGlobeAtmosphere.fragment.fx index f48c3c55936..e31f56af289 100644 --- a/packages/dev/addons/src/atmosphere/Shaders/compositeGlobeAtmosphere.fragment.fx +++ b/packages/dev/addons/src/atmosphere/Shaders/compositeGlobeAtmosphere.fragment.fx @@ -44,16 +44,15 @@ void main() { float cosAngleBetweenViewAndZenith; bool isRayIntersectingGround; - vec4 skyColor = - sampleSkyViewLut( - skyViewLut, - clampedCameraRadius, - cameraGeocentricNormal, - rayDirection, - directionToLight, - cosCameraHorizonAngleFromZenith, - cosAngleBetweenViewAndZenith, - isRayIntersectingGround); + vec4 skyColor = sampleSkyViewLut( + skyViewLut, + clampedCameraRadius, + cameraGeocentricNormal, + rayDirection, + directionToLight, + cosCameraHorizonAngleFromZenith, + cosAngleBetweenViewAndZenith, + isRayIntersectingGround); gl_FragColor = skyColor; @@ -102,8 +101,7 @@ void main() { #endif vec3 transmittance; - vec3 radiance; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( false, // isAerialPerspectiveLut atmosphereExposure * lightIntensity, transmittanceLut, @@ -115,7 +113,6 @@ void main() { 100000000., SkyViewLutSampleCount, distanceToSurface, - radiance, transmittance); float transparency = 1. - avg(transmittance); diff --git a/packages/dev/addons/src/atmosphere/Shaders/compositeSky.fragment.fx b/packages/dev/addons/src/atmosphere/Shaders/compositeSky.fragment.fx index 6c9555b7b0d..4e4e7f7a725 100644 --- a/packages/dev/addons/src/atmosphere/Shaders/compositeSky.fragment.fx +++ b/packages/dev/addons/src/atmosphere/Shaders/compositeSky.fragment.fx @@ -37,16 +37,15 @@ void main() { float cosAngleBetweenViewAndZenith; bool isRayIntersectingGround; - vec4 skyColor = - sampleSkyViewLut( - skyViewLut, - clampedCameraRadius, - cameraGeocentricNormal, - rayDirection, - directionToLight, - cosCameraHorizonAngleFromZenith, - cosAngleBetweenViewAndZenith, - isRayIntersectingGround); + vec4 skyColor = sampleSkyViewLut( + skyViewLut, + clampedCameraRadius, + cameraGeocentricNormal, + rayDirection, + directionToLight, + cosCameraHorizonAngleFromZenith, + cosAngleBetweenViewAndZenith, + isRayIntersectingGround); #ifndef APPLY_TRANSMITTANCE_BLENDING skyColor.a = 0.; @@ -75,8 +74,7 @@ void main() { } vec3 transmittance; - vec3 radiance; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( false, // isAerialPerspectiveLut atmosphereExposure * lightIntensity, transmittanceLut, @@ -88,7 +86,6 @@ void main() { 100000000., SkyViewLutSampleCount, -1., // No planet hit. - radiance, transmittance); #if APPLY_TRANSMITTANCE_BLENDING diff --git a/packages/dev/addons/src/atmosphere/Shaders/diffuseSkyIrradiance.fragment.fx b/packages/dev/addons/src/atmosphere/Shaders/diffuseSkyIrradiance.fragment.fx index a4e9e2b9b0c..565828dcc36 100644 --- a/packages/dev/addons/src/atmosphere/Shaders/diffuseSkyIrradiance.fragment.fx +++ b/packages/dev/addons/src/atmosphere/Shaders/diffuseSkyIrradiance.fragment.fx @@ -15,9 +15,8 @@ uniform sampler2D multiScatteringLut; #include vec3 integrateForIrradiance(vec3 directionToLight, vec3 rayDirection, vec3 rayOrigin) { - vec3 radiance; vec3 transmittance; - integrateScatteredRadiance( + vec3 radiance = integrateScatteredRadiance( false, // isAerialPerspectiveLut 1., transmittanceLut, @@ -30,7 +29,6 @@ vec3 integrateForIrradiance(vec3 directionToLight, vec3 rayDirection, vec3 rayOr 100000000., DiffuseSkyIrradianceLutSampleCount, -1., // No planet hit. - radiance, transmittance); return radiance; } diff --git a/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereFunctions.fx b/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereFunctions.fx new file mode 100644 index 00000000000..82b3c331a48 --- /dev/null +++ b/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereFunctions.fx @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Portions from https://github.com/sebh/UnrealEngineSkyAtmosphere +// MIT License +// +// Copyright (c) 2020 Epic Games, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Common helpers for atmosphere rendering. + +// TODO: Port remaining GLSL atmosphereFunctions.fx. Currently this only includes functions for AtmospherePBRMaterialPlugin. +// TODO: Move LUT sizes and ray march related parameters to UBO. + +const NumAerialPerspectiveLutLayers: f32 = 32.0; +const AerialPerspectiveLutSize: vec3f = vec3f(16.0, 64.0, NumAerialPerspectiveLutLayers); + +const AerialPerspectiveLutKMPerSlice: f32 = 4.0; +const AerialPerspectiveLutRangeKM: f32 = AerialPerspectiveLutKMPerSlice * NumAerialPerspectiveLutLayers; + +const TransmittanceLutSize: vec2f = vec2f(256.0, 64.0); +const TransmittanceLutDomainInUVSpace: vec2f = (TransmittanceLutSize - vec2f(1.0, 1.0)) / TransmittanceLutSize; +const TransmittanceLutHalfTexelSize: vec2f = vec2f(0.5, 0.5) / TransmittanceLutSize; + +// Constants to fade out transmittance as the light goes below the horizon. +const TransmittanceHorizonRange: f32 = 2.0 * TransmittanceLutHalfTexelSize.x; +const TransmittanceMaxUnoccludedU: f32 = 1.0 - 0.5 * TransmittanceHorizonRange; +const TransmittanceMinOccludedU: f32 = 1.0 + 0.5 * TransmittanceHorizonRange; + +#if defined(SAMPLE_TRANSMITTANCE_LUT) || !defined(EXCLUDE_RAY_MARCHING_FUNCTIONS) + +fn getTransmittanceUV(radius: f32, cosAngleLightToZenith: f32, distanceToHorizon: ptr) -> vec2f { + let radiusSquared = radius * radius; + let horizonDistance = sqrtClamped(radiusSquared - atmosphere.planetRadiusSquared); + *distanceToHorizon = horizonDistance; + + let cosAngleLightToZenithSquared = cosAngleLightToZenith * cosAngleLightToZenith; + let discriminant = radiusSquared * (cosAngleLightToZenithSquared - 1.0) + atmosphere.atmosphereRadiusSquared; + let distanceToAtmosphereEdge = max(0.0, -radius * cosAngleLightToZenith + sqrtClamped(discriminant)); + + let minDistanceToAtmosphereEdge = max(0.0, atmosphere.atmosphereRadius - radius); + let maxDistanceToAtmosphereEdge = horizonDistance + atmosphere.horizonDistanceToAtmosphereEdge; + let cosAngleLightToZenithCoordinate = (distanceToAtmosphereEdge - minDistanceToAtmosphereEdge) / max(0.000001, maxDistanceToAtmosphereEdge - minDistanceToAtmosphereEdge); + let distanceToHorizonCoordinate = horizonDistance / max(0.000001, atmosphere.horizonDistanceToAtmosphereEdge); + let unit = vec2f(cosAngleLightToZenithCoordinate, distanceToHorizonCoordinate); + + return unit * TransmittanceLutDomainInUVSpace + TransmittanceLutHalfTexelSize; +} + +// Gets the transmittance of an external light through the atmosphere to a point described by its radius (from the center of the planet) and the angle of incoming light. +fn sampleTransmittanceLut(transmittanceLut: texture_2d, positionRadius: f32, cosAngleLightToZenith: f32) -> vec4f { + var distanceToHorizon = 0.0; + let uv = getTransmittanceUV(positionRadius, cosAngleLightToZenith, &distanceToHorizon); + + // Fade transmittance out as the light goes below the horizon. + let weight = smoothstep(TransmittanceMinOccludedU, TransmittanceMaxUnoccludedU, uv.x); + return weight * textureSampleLevel(transmittanceLut, transmittanceLutSampler, uv, 0.0); +} + +#endif + +fn layerIdxToAerialPerspectiveLayer(layerIdx: f32) -> f32 { + var layer = (layerIdx + 1.0) / NumAerialPerspectiveLutLayers; + layer *= layer; // squared distribution + layer *= NumAerialPerspectiveLutLayers; + return layer; +} + +fn toAerialPerspectiveDepth(layer: f32) -> f32 { + return layer * AerialPerspectiveLutKMPerSlice; +} + +fn toAerialPerspectiveLayer(distance: f32, aerialPerspectiveLutDistancePerSlice: f32) -> f32 { + return distance / aerialPerspectiveLutDistancePerSlice; +} + +fn applyAerialPerspectiveSaturation(aerialPerspective: vec4f) -> vec4f { + let previousRadiance = getLuminance(aerialPerspective.rgb); + let mixed = mix(vec3f(previousRadiance), aerialPerspective.rgb, atmosphere.aerialPerspectiveSaturation); + return vec4f(mixed, aerialPerspective.a); +} + +fn applyAerialPerspectiveIntensity(aerialPerspective: vec4f) -> vec4f { + var result = aerialPerspective; +#if APPLY_AERIAL_PERSPECTIVE_INTENSITY + if (atmosphere.aerialPerspectiveIntensity == 0.0) { + result = vec4f(0.0); + } else { + let previousAlpha = result.a; + result = result / max(0.00001, previousAlpha); + result = result * pow(previousAlpha, 1.0 / atmosphere.aerialPerspectiveIntensity); + } +#endif + return result; +} + +fn applyAerialPerspectiveRadianceBias(aerialPerspective: vec4f) -> vec4f { + var result = aerialPerspective; +#if APPLY_AERIAL_PERSPECTIVE_RADIANCE_BIAS + let originalRadiance = dot(result.rgb, LuminanceEncodeApprox); + let targetRadiance = originalRadiance + atmosphere.aerialPerspectiveRadianceBias; + + if (originalRadiance > 0.0) { + result = result * max(0.0, targetRadiance / originalRadiance); + } else { + result = max(vec4f(0.0), vec4f(atmosphere.aerialPerspectiveRadianceBias)); + } + + result.a = min(result.a, 1.0); +#endif + return result; +} + +// Samples the aerial perspective LUT at the specified distance from the camera. +// Assumes aerialPerspectiveLut has been declared. +fn sampleAerialPerspectiveLut( + screenUV: vec2f, + clampToLutRange: bool, + distanceFromCamera: f32, + numAerialPerspectiveLutLayers: f32, + aerialPerspectiveLutKMPerSlice: f32, + aerialPerspectiveLutRangeKM: f32, + aerialPerspective: ptr +) -> bool { + + *aerialPerspective = vec4f(0.0); + +#if USE_AERIAL_PERSPECTIVE_LUT + if (distanceFromCamera > 0.0 && + (clampToLutRange || distanceFromCamera < aerialPerspectiveLutRangeKM) && + atmosphere.clampedCameraRadius <= atmosphere.atmosphereRadius) { + + var layer = toAerialPerspectiveLayer(distanceFromCamera, aerialPerspectiveLutKMPerSlice); + let normalizedLayer = sqrt(layer / numAerialPerspectiveLutLayers); // squared distribution + layer = min(normalizedLayer * numAerialPerspectiveLutLayers, numAerialPerspectiveLutLayers); + + // Fade out nearest layer to 0. + let weight = min(layer, 1.0); + + // Interpolate aerial perspective from the two adjacent texture layers. + let layerIdx = max(0.0, layer - 1.0); + let floorLayerIdx = floor(layerIdx); + let aerialPerspectiveLayer0 = textureSampleLevel(aerialPerspectiveLut, aerialPerspectiveLutSampler, screenUV, i32(floorLayerIdx), 0.0); + let aerialPerspectiveLayer1 = textureSampleLevel(aerialPerspectiveLut, aerialPerspectiveLutSampler, screenUV, i32(floorLayerIdx + 1.0), 0.0); + var interpolated = mix(aerialPerspectiveLayer0, aerialPerspectiveLayer1, layerIdx - floorLayerIdx); + + interpolated = vec4f(interpolated.rgb * atmosphere.atmosphereExposure, interpolated.a); + interpolated = applyAerialPerspectiveSaturation(interpolated); + interpolated = weight * applyAerialPerspectiveIntensity(interpolated); + interpolated = applyAerialPerspectiveRadianceBias(interpolated); + + *aerialPerspective = interpolated; + return true; + } +#endif + + return false; +} diff --git a/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereUboDeclaration.fx b/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereUboDeclaration.fx new file mode 100644 index 00000000000..97019d5731d --- /dev/null +++ b/packages/dev/addons/src/atmosphere/ShadersWGSL/ShadersInclude/atmosphereUboDeclaration.fx @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +struct Atmosphere { + peakRayleighScattering: vec3, + planetRadius: f32, + peakMieScattering: vec3, + atmosphereThickness: f32, + peakMieAbsorption: vec3, + planetRadiusSquared: f32, + peakMieExtinction: vec3, + atmosphereRadius: f32, + peakOzoneAbsorption: vec3, + atmosphereRadiusSquared: f32, + horizonDistanceToAtmosphereEdge: f32, + horizonDistanceToAtmosphereEdgeSquared: f32, + planetRadiusWithOffset: f32, + planetRadiusOffset: f32, + atmosphereExposure: f32, + aerialPerspectiveRadianceBias: f32, + inverseAtmosphereThickness: f32, + aerialPerspectiveTransmittanceScale: f32, + inverseViewProjectionWithoutTranslation: mat4x4, + directionToLight: vec3, + multiScatteringIntensity: f32, + directionToLightRelativeToCameraGeocentricNormal: vec3, + cameraRadius: f32, + lightRadianceAtCamera: vec3, + diffuseSkyIrradianceDesaturationFactor: f32, + groundAlbedo: vec3, + aerialPerspectiveSaturation: f32, + minMultiScattering: vec3, + diffuseSkyIrradianceIntensity: f32, + cameraPositionGlobal: vec3, + lightIntensity: f32, + clampedCameraPositionGlobal: vec3, + aerialPerspectiveIntensity: f32, + cameraGeocentricNormal: vec3, + clampedCameraRadius: f32, + cameraForward: vec3, + clampedCameraHeight: f32, + cameraPosition: vec3, + cosCameraHorizonAngleFromZenith: f32, + viewport: vec4, + additionalDiffuseSkyIrradiance: vec3, + cameraHeight: f32, + cameraNearPlane: f32, + originHeight: f32, + sinCameraAtmosphereHorizonAngleFromNadir: f32 +}; + +var atmosphere : Atmosphere; diff --git a/packages/dev/addons/src/atmosphere/atmosphere.ts b/packages/dev/addons/src/atmosphere/atmosphere.ts index 76599169efb..38c31aca844 100644 --- a/packages/dev/addons/src/atmosphere/atmosphere.ts +++ b/packages/dev/addons/src/atmosphere/atmosphere.ts @@ -19,6 +19,7 @@ import type { IColor3Like, IVector3Like } from "core/Maths/math.like"; import type { IDisposable, Scene } from "core/scene"; import { Observable, type Observer } from "core/Misc/observable"; import { RegisterMaterialPlugin, UnregisterMaterialPlugin } from "core/Materials/materialPluginManager"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; import type { RenderingGroupInfo } from "core/Rendering/renderingManager"; import { RenderTargetTexture, type RenderTargetTextureOptions } from "core/Materials/Textures/renderTargetTexture"; import type { RenderTargetWrapper } from "core/Engines/renderTargetWrapper"; @@ -116,7 +117,7 @@ export class Atmosphere implements IDisposable { * @returns True if the atmosphere is supported, false otherwise. */ public static IsSupported(engine: AbstractEngine): boolean { - return !engine._badOS && !engine.isWebGPU && engine.version >= 2; + return !engine._badOS && (engine.isWebGPU || engine.version >= 2); } /** @@ -662,10 +663,7 @@ export class Atmosphere implements IDisposable { ) { const engine = (this._engine = scene.getEngine()); - if (engine.isWebGPU) { - throw new Error("Atmosphere is not supported on WebGPU."); - } - if (engine.version < 2) { + if (!engine.isWebGPU && engine.version < 2) { throw new Error(`Atmosphere is not supported on WebGL ${engine.version}.`); } @@ -1288,8 +1286,9 @@ export class Atmosphere implements IDisposable { */ public bindUniformBufferToEffect(effect: Effect): void { const uniformBuffer = this.uniformBuffer; - const name = uniformBuffer.name; - uniformBuffer.bindToEffect(effect, name); + const isWGSL = effect.shaderLanguage === ShaderLanguage.WGSL; + const blockName = isWGSL ? "atmosphere" : uniformBuffer.name; + uniformBuffer.bindToEffect(effect, blockName); if (uniformBuffer.useUbo) { uniformBuffer.bindUniformBuffer(); } else { @@ -1523,6 +1522,9 @@ const DrawEffect = ( effectRenderer.setViewport(); effectRenderer.applyEffectWrapper(effectWrapper, depthTest); // Note, stencil is false by default. + const currentCull = engine.depthCullingState.cull; + engine.depthCullingState.cull = false; + const effect = effectWrapper.effect; effect.setFloat("depth", depth); @@ -1531,6 +1533,7 @@ const DrawEffect = ( drawCallback(effectRenderer, renderTarget?.renderTarget!, effect, engine); // Restore state (order matters!) + engine.depthCullingState.cull = currentCull; engine.setAlphaMode(currentAlphaMode); if (currentDepthWrite !== undefined) { engine.setDepthWrite(currentDepthWrite); diff --git a/packages/dev/addons/src/atmosphere/atmospherePBRMaterialPlugin.ts b/packages/dev/addons/src/atmosphere/atmospherePBRMaterialPlugin.ts index b838a94ab7a..abb72901fb1 100644 --- a/packages/dev/addons/src/atmosphere/atmospherePBRMaterialPlugin.ts +++ b/packages/dev/addons/src/atmosphere/atmospherePBRMaterialPlugin.ts @@ -9,6 +9,9 @@ import { MaterialPluginBase } from "core/Materials/materialPluginBase"; import type { Nullable } from "core/types"; import type { UniformBuffer } from "core/Materials/uniformBuffer"; import { Vector3FromFloatsToRef, Vector3ScaleToRef } from "core/Maths/math.vector.functions"; +import { ShaderLanguage } from "core/Materials/shaderLanguage"; +import "./ShadersWGSL/ShadersInclude/atmosphereFunctions"; +import "./ShadersWGSL/ShadersInclude/atmosphereUboDeclaration"; class AtmospherePBRMaterialDefines extends MaterialDefines { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -96,13 +99,21 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { this._pluginManager._addPlugin(this); } + /** + * @override + */ + public override isCompatible(): boolean { + return true; + } + /** * @override */ public override getUniformBuffersNames(_ubos: string[]): void { const uniformBuffer = this._atmosphere.uniformBuffer; if (uniformBuffer.useUbo) { - _ubos.push(uniformBuffer.name); + const uboName = this._material.shaderLanguage === ShaderLanguage.WGSL ? "atmosphere" : uniformBuffer.name; + _ubos.push(uboName); } } @@ -214,7 +225,7 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { /** * @override */ - public override getCustomCode(shaderType: string): Nullable> { + public override getCustomCode(shaderType: string, shaderLanguage: ShaderLanguage): Nullable> { // Assumed inputs are light0, vPositionW, normalW. // Only works for directional lights. if (shaderType !== "fragment") { @@ -224,15 +235,16 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { const useUbo = this._atmosphere.scene.getEngine().supportsUniformBuffers; const directionToLightSnippet = useUbo ? "-light0.vLightData.xyz" : "-vLightData0.xyz"; - const useAtmosphereUbo = this._atmosphere.uniformBuffer.useUbo; + const useAtmosphereUbo = shaderLanguage === ShaderLanguage.WGSL || this._atmosphere.uniformBuffer.useUbo; const atmosphereImportSnippet = useAtmosphereUbo ? "#include" : "#include"; - return { - CUSTOM_FRAGMENT_DEFINITIONS: - this._isAerialPerspectiveEnabled && this._atmosphere.isAerialPerspectiveLutEnabled - ? `uniform sampler2D transmittanceLut;\r\nprecision highp sampler2DArray;\r\nuniform sampler2DArray aerialPerspectiveLut;\r\n${atmosphereImportSnippet}\r\n#include` - : `uniform sampler2D transmittanceLut;\r\n${atmosphereImportSnippet}\r\n#include`, - CUSTOM_LIGHT0_COLOR: ` + if (shaderLanguage === ShaderLanguage.GLSL) { + return { + CUSTOM_FRAGMENT_DEFINITIONS: + this._isAerialPerspectiveEnabled && this._atmosphere.isAerialPerspectiveLutEnabled + ? `uniform sampler2D transmittanceLut;\r\nprecision highp sampler2DArray;\r\nuniform sampler2DArray aerialPerspectiveLut;\r\n${atmosphereImportSnippet}\r\n#include` + : `uniform sampler2D transmittanceLut;\r\n${atmosphereImportSnippet}\r\n#include`, + CUSTOM_LIGHT0_COLOR: ` { vec3 positionGlobal = 0.001 * vPositionW + ${OriginOffsetUniformName}; float positionRadius = length(positionGlobal); @@ -242,7 +254,7 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { diffuse0 = lightIntensity * sampleTransmittanceLut(transmittanceLut, positionRadius, cosAngleLightToZenith); } `, - CUSTOM_REFLECTION: ` + CUSTOM_REFLECTION: ` { vec3 positionGlobal = 0.001 * vPositionW + ${OriginOffsetUniformName}; float positionRadius = length(positionGlobal); @@ -271,8 +283,8 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { reflectionOut.environmentRadiance.rgb = reflectionOut.environmentIrradiance; } `, - // TODO: Support full ray marching if USE_AERIAL_PERSPECTIVE_LUT is disabled. - CUSTOM_FRAGMENT_BEFORE_FOG: ` + // TODO: Support full ray marching if USE_AERIAL_PERSPECTIVE_LUT is disabled. + CUSTOM_FRAGMENT_BEFORE_FOG: ` #if USE_AERIAL_PERSPECTIVE_LUT { float distanceFromCameraKm = 0.001 * distance(vEyePosition.xyz, vPositionW); @@ -290,6 +302,73 @@ export class AtmospherePBRMaterialPlugin extends MaterialPluginBase { } #endif `, - }; + }; + } else { + // WGSL + return { + CUSTOM_FRAGMENT_DEFINITIONS: + this._isAerialPerspectiveEnabled && this._atmosphere.isAerialPerspectiveLutEnabled + ? `var transmittanceLutSampler: sampler;\r\nvar transmittanceLut: texture_2d;\r\nvar aerialPerspectiveLutSampler: sampler;\r\nvar aerialPerspectiveLut: texture_2d_array;\r\n${atmosphereImportSnippet}\r\n#include` + : `var transmittanceLutSampler: sampler;\r\nvar transmittanceLut: texture_2d;\r\n${atmosphereImportSnippet}\r\n#include`, + CUSTOM_LIGHT0_COLOR: ` + { + var positionGlobal = 0.001 * fragmentInputs.vPositionW + uniforms.${OriginOffsetUniformName}; + var positionRadius = length(positionGlobal); + var geocentricNormal = positionGlobal / positionRadius; + var directionToLight = ${directionToLightSnippet}; + var cosAngleLightToZenith = dot(directionToLight, geocentricNormal); + diffuse0 = atmosphere.lightIntensity * sampleTransmittanceLut(transmittanceLut, positionRadius, cosAngleLightToZenith); + } +`, + CUSTOM_REFLECTION: ` + { + var positionGlobal = 0.001 * fragmentInputs.vPositionW + uniforms.${OriginOffsetUniformName}; + var positionRadius = length(positionGlobal); + var geocentricNormal = positionGlobal / positionRadius; + + var directionToLight = ${directionToLightSnippet}; + var cosAngleLightToZenith = dot(directionToLight, geocentricNormal); + + var uv = vec2f(0.5 + 0.5 * cosAngleLightToZenith, (positionRadius - atmosphere.planetRadius) / atmosphere.atmosphereThickness); + var irradianceScaleT = 0.5 * dot(normalW, geocentricNormal) + 0.5; + var irradianceScale = ((-0.6652 * irradianceScaleT) + 1.5927) * irradianceScaleT + 0.1023; + var environmentIrradiance = atmosphere.lightIntensity * textureSample(irradianceSampler, irradianceSamplerSampler, uv).rgb; + + // Add a contribution here to estimate indirect lighting. + const r = 0.2; + var indirect = getLuminance(environmentIrradiance) / max(0.00001, 1.0 - r); + environmentIrradiance *= irradianceScale; + environmentIrradiance += indirect; + + environmentIrradiance += atmosphere.additionalDiffuseSkyIrradiance; + + const diffuseBrdf = 1.0 / PI; + environmentIrradiance *= diffuseBrdf * atmosphere.diffuseSkyIrradianceIntensity; + + reflectionOut.environmentIrradiance = environmentIrradiance; + reflectionOut.environmentRadiance = vec4f(reflectionOut.environmentIrradiance, reflectionOut.environmentRadiance.a); + } +`, + // TODO: Support full ray marching if USE_AERIAL_PERSPECTIVE_LUT is disabled. + CUSTOM_FRAGMENT_BEFORE_FOG: ` + #if USE_AERIAL_PERSPECTIVE_LUT + { + var distanceFromCameraKm = 0.001 * distance(scene.vEyePosition.xyz, fragmentInputs.vPositionW); + var aerialPerspective = vec4f(0.); + if (sampleAerialPerspectiveLut( + fragmentInputs.position.xy * uniforms.${InverseViewportSizeUniformName}, + true, + distanceFromCameraKm, + NumAerialPerspectiveLutLayers, + AerialPerspectiveLutKMPerSlice, + AerialPerspectiveLutRangeKM, + &aerialPerspective)) { + finalColor = aerialPerspective + (1. - aerialPerspective.a) * finalColor; + } + } + #endif +`, + }; + } } } diff --git a/packages/dev/core/src/Materials/materialPluginManager.ts b/packages/dev/core/src/Materials/materialPluginManager.ts index 735378ef6e1..1bda121bddc 100644 --- a/packages/dev/core/src/Materials/materialPluginManager.ts +++ b/packages/dev/core/src/Materials/materialPluginManager.ts @@ -387,13 +387,13 @@ export class MaterialPluginManager { for (let pointName in points) { let injectedCode = ""; for (const plugin of this._activePlugins) { - let customCode = plugin.getCustomCode(shaderType, this._material.shaderLanguage)?.[pointName]; + const shaderLanguage = this._material.shaderLanguage; + let customCode = plugin.getCustomCode(shaderType, shaderLanguage)?.[pointName]; if (!customCode) { continue; } if (plugin.resolveIncludes) { if (processorOptions === null) { - const shaderLanguage = ShaderLanguage.GLSL; processorOptions = { defines: [], // not used by _ProcessIncludes indexParameters: eventData.indexParameters, diff --git a/packages/tools/tests/test/visualization/config.json b/packages/tools/tests/test/visualization/config.json index 3b77300b866..4023905e4b3 100644 --- a/packages/tools/tests/test/visualization/config.json +++ b/packages/tools/tests/test/visualization/config.json @@ -2916,49 +2916,49 @@ "title": "Atmosphere Day", "playgroundId": "#VO1Z0C#14", "referenceImage": "atmosphere-day.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Day (Planet Origin)", "playgroundId": "#VO1Z0C#30", "referenceImage": "atmosphere-day.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Sunset", "playgroundId": "#VO1Z0C#15", "referenceImage": "atmosphere-sunset.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Sunset (Planet Origin)", "playgroundId": "#VO1Z0C#31", "referenceImage": "atmosphere-sunset.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Night", "playgroundId": "#VO1Z0C#13", "referenceImage": "atmosphere-night.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Night (Planet Origin)", "playgroundId": "#VO1Z0C#32", "referenceImage": "atmosphere-night.png", - "excludedEngines": ["webgl1", "webgpu"], + "excludedEngines": ["webgl1"], "renderCount": 2 }, { "title": "Atmosphere Space View", "playgroundId": "#VO1Z0C#6", "referenceImage": "atmosphere-space-view.png", - "excludedEngines": ["webgl1", "webgpu"] + "excludedEngines": ["webgl1"] }, { "title": "OpenPBR IBL Reflectance with IOR",