// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= SkyAtmosphereRendering.cpp =============================================================================*/ #include "SkyAtmosphereRendering.h" #include "CanvasTypes.h" #include "Components/SkyAtmosphereComponent.h" #include "DeferredShadingRenderer.h" #include "LightSceneInfo.h" #include "PixelShaderUtils.h" #include "Rendering/SkyAtmosphereCommonData.h" #include "ScenePrivate.h" #include "SceneProxies/SkyAtmosphereSceneProxy.h" #include "SceneProxies/SkyLightSceneProxy.h" #include "SceneRenderTargetParameters.h" #include "VolumeLighting.h" #include "VolumetricCloudRendering.h" #include "VirtualShadowMaps/VirtualShadowMapArray.h" #include "RendererUtils.h" #include "ScreenPass.h" #include "UnrealEngine.h" #include "PostProcess/PostProcessing.h" // IsPostProcessingWithAlphaChannelSupported //PRAGMA_DISABLE_OPTIMIZATION // The runtime ON/OFF toggle static TAutoConsoleVariable CVarSkyAtmosphere( TEXT("r.SkyAtmosphere"), 1, TEXT("SkyAtmosphere components are rendered when this is not 0, otherwise ignored.\n"), ECVF_RenderThreadSafe); // The project setting (disable runtime and shader code) static TAutoConsoleVariable CVarSupportSkyAtmosphere( TEXT("r.SupportSkyAtmosphere"), 1, TEXT("Enables SkyAtmosphere rendering and shader code."), ECVF_ReadOnly | ECVF_RenderThreadSafe); // The project setting for the sky atmosphere component to affect the height fog (disable runtime and shader code) static TAutoConsoleVariable CVarSupportSkyAtmosphereAffectsHeightFog( TEXT("r.SupportSkyAtmosphereAffectsHeightFog"), 1, TEXT("Enables SkyAtmosphere affecting height fog. It requires r.SupportSkyAtmosphere to be true."), ECVF_ReadOnly | ECVF_RenderThreadSafe); ////////////////////////////////////////////////////////////////////////// Regular sky static TAutoConsoleVariable CVarSkyAtmosphereSampleCountMin( TEXT("r.SkyAtmosphere.SampleCountMin"), 2.0f, TEXT("The minimum sample count used to compute sky/atmosphere scattering and transmittance.\n") TEXT("The minimal value will be clamped to 1.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereSampleCountMax( TEXT("r.SkyAtmosphere.SampleCountMax"), 32.0f, TEXT("The maximum sample count used to compute sky/atmosphere scattering and transmittance The effective sample count is usually lower and depends on distance and SampleCountScale on the component, as well as .ini files.\n") TEXT("The minimal value will be clamped to r.SkyAtmosphere.SampleCountMin + 1.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereDistanceToSampleCountMax( TEXT("r.SkyAtmosphere.DistanceToSampleCountMax"), 150.0f, TEXT("The distance in kilometer after which SampleCountMax samples will be used to ray march the atmosphere."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereSampleLightShadowmap( TEXT("r.SkyAtmosphere.SampleLightShadowmap"), 1, TEXT("Enable the sampling of atmospheric lights shadow map in order to produce volumetric shadows."), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Fast sky static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUT( TEXT("r.SkyAtmosphere.FastSkyLUT"), 1, TEXT("When enabled, a look up texture is used to render the sky.\n") TEXT("It is faster but can result in visual artefacts if there are some high frequency details\n") TEXT("in the sky such as earth shadow or scattering lob."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUTSampleCountMin( TEXT("r.SkyAtmosphere.FastSkyLUT.SampleCountMin"), 4.0f, TEXT("Fast sky minimum sample count used to compute sky/atmosphere scattering and transmittance.\n") TEXT("The minimal value will be clamped to 1.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUTSampleCountMax( TEXT("r.SkyAtmosphere.FastSkyLUT.SampleCountMax"), 32.0f, TEXT("Fast sky maximum sample count used to compute sky/atmosphere scattering and transmittance.\n") TEXT("The maximum sample count used to compute FastSkyLUT scattering. The effective sample count is usually lower and depends on distance and SampleCountScale on the component, as well as .ini files.\n") TEXT("The minimal value will be clamped to r.SkyAtmosphere.FastSkyLUT.SampleCountMin + 1.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUTDistanceToSampleCountMax( TEXT("r.SkyAtmosphere.FastSkyLUT.DistanceToSampleCountMax"), 150.0f, TEXT("Fast sky distance in kilometer after which at which SampleCountMax samples will be used to ray march the atmosphere."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUTWidth( TEXT("r.SkyAtmosphere.FastSkyLUT.Width"), 192, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereFastSkyLUTHeight( TEXT("r.SkyAtmosphere.FastSkyLUT.Height"), 104, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Aerial perspective static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveDepthTest( TEXT("r.SkyAtmosphere.AerialPerspective.DepthTest"), 1, TEXT("When enabled, a depth test will be used to not write pixel closer to the camera than StartDepth, effectively improving performance."), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Aerial perspective LUT static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveLUTDepthResolution( TEXT("r.SkyAtmosphere.AerialPerspectiveLUT.DepthResolution"), 16.0f, TEXT("The number of depth slice to use for the aerial perspective volume texture."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveLUTDepth( TEXT("r.SkyAtmosphere.AerialPerspectiveLUT.Depth"), 96.0f, TEXT("The length of the LUT in kilometers (default = 96km to get nice cloud/atmosphere interactions in the distance for default sky). Further than this distance, the last slice is used."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveLUTSampleCountMaxPerSlice( TEXT("r.SkyAtmosphere.AerialPerspectiveLUT.SampleCountMaxPerSlice"), 2.0f, TEXT("The sample count used per slice to evaluate aerial perspective. The effective sample count is usually lower and depends on SampleCountScale on the component as well as .ini files.\n") TEXT("scattering and transmittance in camera frustum space froxel.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveLUTWidth( TEXT("r.SkyAtmosphere.AerialPerspectiveLUT.Width"), 32, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereAerialPerspectiveApplyOnOpaque( TEXT("r.SkyAtmosphere.AerialPerspectiveLUT.FastApplyOnOpaque"), 1, TEXT("When enabled, the low resolution camera frustum/froxel volume containing atmospheric fog\n") TEXT(", usually used for fog on translucent surface, is used to render fog on opaque.\n") TEXT("It is faster but can result in visual artefacts if there are some high frequency details\n") TEXT("such as earth shadow or scattering lob."), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Transmittance LUT static TAutoConsoleVariable CVarSkyAtmosphereTransmittanceLUT( TEXT("r.SkyAtmosphere.TransmittanceLUT"), 1, TEXT("Enable the generation of the sky transmittance.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereTransmittanceLUTSampleCount( TEXT("r.SkyAtmosphere.TransmittanceLUT.SampleCount"), 10.0f, TEXT("The sample count used to evaluate transmittance."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereTransmittanceLUTUseSmallFormat( TEXT("r.SkyAtmosphere.TransmittanceLUT.UseSmallFormat"), 0, TEXT("If true, the transmittance LUT will use a small R8BG8B8A8 format to store data at lower quality."), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereTransmittanceLUTWidth( TEXT("r.SkyAtmosphere.TransmittanceLUT.Width"), 256, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereTransmittanceLUTHeight( TEXT("r.SkyAtmosphere.TransmittanceLUT.Height"), 64, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Multi-scattering LUT static TAutoConsoleVariable CVarSkyAtmosphereMultiScatteringLUTSampleCount( TEXT("r.SkyAtmosphere.MultiScatteringLUT.SampleCount"), 15.0f, TEXT("The sample count used to evaluate multi-scattering.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereMultiScatteringLUTHighQuality( TEXT("r.SkyAtmosphere.MultiScatteringLUT.HighQuality"), 0.0f, TEXT("The when enabled, 64 samples are used instead of 2, resulting in a more accurate multi scattering approximation (but also more expenssive).\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereMultiScatteringLUTWidth( TEXT("r.SkyAtmosphere.MultiScatteringLUT.Width"), 32, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereMultiScatteringLUTHeight( TEXT("r.SkyAtmosphere.MultiScatteringLUT.Height"), 32, TEXT(""), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Distant Sky Light LUT static TAutoConsoleVariable CVarSkyAtmosphereDistantSkyLightLUT( TEXT("r.SkyAtmosphere.DistantSkyLightLUT"), 1, TEXT("Enable the generation the sky ambient lighting value.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereDistantSkyLightLUTAltitude( TEXT("r.SkyAtmosphere.DistantSkyLightLUT.Altitude"), 6.0f, TEXT("The altitude at which the sky samples are taken to integrate the sky lighting. Default to 6km, typicaly cirrus clouds altitude.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); ////////////////////////////////////////////////////////////////////////// Debug / Visualization static TAutoConsoleVariable CVarSkyAtmosphereLUT32( TEXT("r.SkyAtmosphere.LUT32"), 0, TEXT("Use full 32bit per-channel precision for all sky LUTs.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereEditorNotifications( TEXT("r.SkyAtmosphere.EditorNotifications"), 1, TEXT("Enable the rendering of in editor notification to warn the user about missing sky dome pixels on screen. It is better to keep it enabled and will be removed when shipping.\n"), ECVF_RenderThreadSafe | ECVF_Scalability); static TAutoConsoleVariable CVarSkyAtmosphereAsyncCompute( TEXT("r.SkyAtmosphereAsyncCompute"), 0, TEXT("SkyAtmosphere on async compute (default: false). When running on the async pipe, SkyAtmosphere lut generation will overlap with the occlusion pass.\n"), ECVF_RenderThreadSafe); DECLARE_GPU_STAT(SkyAtmosphereLUTs); DECLARE_GPU_STAT(SkyAtmosphere); DECLARE_GPU_STAT(SkyAtmosphereEditor); DECLARE_GPU_STAT(SkyAtmosphereDebugVisualize); // Extra internal constants shared between all passes. It is used to render the sky itself (not shared with material) BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FSkyAtmosphereInternalCommonParameters, ) SHADER_PARAMETER(float, SampleCountMin) SHADER_PARAMETER(float, SampleCountMax) SHADER_PARAMETER(float, DistanceToSampleCountMaxInv) SHADER_PARAMETER(float, FastSkySampleCountMin) SHADER_PARAMETER(float, FastSkySampleCountMax) SHADER_PARAMETER(float, FastSkyDistanceToSampleCountMaxInv) SHADER_PARAMETER(FVector4f, CameraAerialPerspectiveVolumeSizeAndInvSize) SHADER_PARAMETER(float, CameraAerialPerspectiveVolumeDepthResolution) // Also on View UB SHADER_PARAMETER(float, CameraAerialPerspectiveVolumeDepthResolutionInv) // Also on View UB SHADER_PARAMETER(float, CameraAerialPerspectiveVolumeDepthSliceLengthKm) // Also on View UB SHADER_PARAMETER(float, CameraAerialPerspectiveVolumeDepthSliceLengthKmInv) // Also on View UB SHADER_PARAMETER(float, CameraAerialPerspectiveSampleCountPerSlice) SHADER_PARAMETER(FVector4f, TransmittanceLutSizeAndInvSize) SHADER_PARAMETER(FVector4f, MultiScatteredLuminanceLutSizeAndInvSize) SHADER_PARAMETER(FVector4f, SkyViewLutSizeAndInvSize) // Also on View UB SHADER_PARAMETER(float, TransmittanceSampleCount) SHADER_PARAMETER(float, MultiScatteringSampleCount) SHADER_PARAMETER(float, AerialPespectiveViewDistanceScale) SHADER_PARAMETER(float, FogShowFlagFactor) SHADER_PARAMETER(FVector3f, SkyLuminanceFactor) SHADER_PARAMETER(FVector3f, SkyAndAerialPerspectiveLuminanceFactor) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FAtmosphereUniformShaderParameters, "Atmosphere"); IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FSkyAtmosphereInternalCommonParameters, "SkyAtmosphere"); BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FSkyAtmosphereRealTimeReflectionLUTParameters, ) SHADER_PARAMETER(FMatrix44f, SkyViewLutReferential) SHADER_PARAMETER(FVector4f, SkyPlanetTranslatedWorldCenterAndViewHeight) SHADER_PARAMETER(FVector3f, SkyCameraTranslatedWorldOrigin) SHADER_PARAMETER(float, RealTimeReflectionCapture) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FSkyAtmosphereRealTimeReflectionLUTParameters, "SkyAtmosphereRealTimeReflectionLUTParameters"); auto GetDefaultSkyAtmosphereRealTimeReflectionLUTUniformBuffer() { FSkyAtmosphereRealTimeReflectionLUTParameters Parameters; Parameters.SkyViewLutReferential = FMatrix44f::Identity; Parameters.SkyPlanetTranslatedWorldCenterAndViewHeight = FVector4f::Zero(); Parameters.SkyCameraTranslatedWorldOrigin = FVector3f::Zero(); Parameters.RealTimeReflectionCapture = 0.0f; return TUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleDraw); } #define GET_VALID_DATA_FROM_CVAR \ auto ValidateLUTResolution = [](int32 Value) \ { \ return Value < 4 ? 4 : Value; \ }; \ int32 TransmittanceLutWidth = ValidateLUTResolution(CVarSkyAtmosphereTransmittanceLUTWidth.GetValueOnRenderThread()); \ int32 TransmittanceLutHeight = ValidateLUTResolution(CVarSkyAtmosphereTransmittanceLUTHeight.GetValueOnRenderThread()); \ int32 MultiScatteredLuminanceLutWidth = ValidateLUTResolution(CVarSkyAtmosphereMultiScatteringLUTWidth.GetValueOnRenderThread()); \ int32 MultiScatteredLuminanceLutHeight = ValidateLUTResolution(CVarSkyAtmosphereMultiScatteringLUTHeight.GetValueOnRenderThread()); \ int32 SkyViewLutWidth = ValidateLUTResolution(CVarSkyAtmosphereFastSkyLUTWidth.GetValueOnRenderThread()); \ int32 SkyViewLutHeight = ValidateLUTResolution(CVarSkyAtmosphereFastSkyLUTHeight.GetValueOnRenderThread()); \ int32 CameraAerialPerspectiveVolumeScreenResolution = ValidateLUTResolution(CVarSkyAtmosphereAerialPerspectiveLUTWidth.GetValueOnRenderThread()); \ int32 CameraAerialPerspectiveVolumeDepthResolution = ValidateLUTResolution(CVarSkyAtmosphereAerialPerspectiveLUTDepthResolution.GetValueOnRenderThread()); \ float CameraAerialPerspectiveVolumeDepthKm = CVarSkyAtmosphereAerialPerspectiveLUTDepth.GetValueOnRenderThread(); \ CameraAerialPerspectiveVolumeDepthKm = CameraAerialPerspectiveVolumeDepthKm < 1.0f ? 1.0f : CameraAerialPerspectiveVolumeDepthKm; /* 1 kilometer minimum */ \ float CameraAerialPerspectiveVolumeDepthSliceLengthKm = CameraAerialPerspectiveVolumeDepthKm / CameraAerialPerspectiveVolumeDepthResolution; #define KM_TO_CM 100000.0f #define CM_TO_KM (1.0f / KM_TO_CM) ESkyAtmospherePassLocation GetSkyAtmospherePassLocation() { if (CVarSkyAtmosphereAsyncCompute.GetValueOnAnyThread() != 0) { return ESkyAtmospherePassLocation::BeforePrePass; } // When SkyAtmLUT is running on graphics pipe, kick before BasePass to have a better overlap when DistanceFieldShadows is running async return ESkyAtmospherePassLocation::BeforeBasePass; } float GetValidAerialPerspectiveStartDepthInCm(const FViewInfo& View, const FSkyAtmosphereSceneProxy& SkyAtmosphereProxy) { float AerialPerspectiveStartDepthKm = SkyAtmosphereProxy.GetAerialPerspectiveStartDepthKm(); AerialPerspectiveStartDepthKm = AerialPerspectiveStartDepthKm < 0.0f ? 0.0f : AerialPerspectiveStartDepthKm; // For sky reflection capture, the start depth can be super large. So we max it to make sure the triangle is never in front the NearClippingDistance. const float StartDepthInCm = FMath::Max(AerialPerspectiveStartDepthKm * KM_TO_CM, View.NearClippingDistance); return StartDepthInCm; } static bool VirtualShadowMapSamplingSupported(EShaderPlatform ShaderPlatform) { return GetMaxSupportedFeatureLevel(ShaderPlatform) >= ERHIFeatureLevel::SM5; } bool ShouldSkySampleAtmosphereLightsOpaqueShadow(const FScene& Scene, const TArray& VisibleLightInfos, SkyAtmosphereLightShadowData& LightShadowData) { LightShadowData.LightVolumetricShadowSceneinfo0 = Scene.AtmosphereLights[0]; LightShadowData.LightVolumetricShadowSceneinfo1 = Scene.AtmosphereLights[1]; if (LightShadowData.LightVolumetricShadowSceneinfo0 && LightShadowData.LightVolumetricShadowSceneinfo0->Proxy && LightShadowData.LightVolumetricShadowSceneinfo0->Proxy->GetCastShadowsOnAtmosphere()) { const FVisibleLightInfo& Light0 = VisibleLightInfos[LightShadowData.LightVolumetricShadowSceneinfo0->Id]; LightShadowData.ProjectedShadowInfo0 = GetFirstWholeSceneShadowMap(Light0); // NOTE: This will be an arbitrary clipmap if multiple exist, but this is similar to the CSM select above, // which also does not take multiple views into account. LightShadowData.VirtualShadowMapId0 = Light0.VirtualShadowMapId; } if (LightShadowData.LightVolumetricShadowSceneinfo1 && LightShadowData.LightVolumetricShadowSceneinfo1->Proxy && LightShadowData.LightVolumetricShadowSceneinfo1->Proxy->GetCastShadowsOnAtmosphere()) { const FVisibleLightInfo& Light1 = VisibleLightInfos[LightShadowData.LightVolumetricShadowSceneinfo1->Id]; LightShadowData.ProjectedShadowInfo1 = GetFirstWholeSceneShadowMap(Light1); LightShadowData.VirtualShadowMapId1 = Light1.VirtualShadowMapId; } return CVarSkyAtmosphereSampleLightShadowmap.GetValueOnRenderThread() > 0 && (LightShadowData.ProjectedShadowInfo0 || LightShadowData.ProjectedShadowInfo1 || LightShadowData.VirtualShadowMapId0 != INDEX_NONE || LightShadowData.VirtualShadowMapId1 != INDEX_NONE); } void GetSkyAtmosphereLightsUniformBuffers( FRDGBuilder& GraphBuilder, TRDGUniformBufferRef& OutLightShadowShaderParams0UniformBuffer, TRDGUniformBufferRef& OutLightShadowShaderParams1UniformBuffer, const SkyAtmosphereLightShadowData& LightShadowData, const FViewInfo& ViewInfo, const bool bShouldSampleOpaqueShadow, const EUniformBufferUsage UniformBufferUsage ) { FVolumeShadowingShaderParametersGlobal0& LightShadowShaderParams0 = *GraphBuilder.AllocParameters(); FVolumeShadowingShaderParametersGlobal1& LightShadowShaderParams1 = *GraphBuilder.AllocParameters(); if (bShouldSampleOpaqueShadow && LightShadowData.LightVolumetricShadowSceneinfo0) { SetVolumeShadowingShaderParameters(GraphBuilder, LightShadowShaderParams0, ViewInfo, LightShadowData.LightVolumetricShadowSceneinfo0, LightShadowData.ProjectedShadowInfo0); } else { SetVolumeShadowingDefaultShaderParameters(GraphBuilder, LightShadowShaderParams0); } if (bShouldSampleOpaqueShadow && LightShadowData.LightVolumetricShadowSceneinfo1) { SetVolumeShadowingShaderParameters(GraphBuilder, LightShadowShaderParams1, ViewInfo, LightShadowData.LightVolumetricShadowSceneinfo1, LightShadowData.ProjectedShadowInfo1); } else { SetVolumeShadowingDefaultShaderParameters(GraphBuilder, LightShadowShaderParams1); } OutLightShadowShaderParams0UniformBuffer = GraphBuilder.CreateUniformBuffer(&LightShadowShaderParams0); OutLightShadowShaderParams1UniformBuffer = GraphBuilder.CreateUniformBuffer(&LightShadowShaderParams1); } bool ShouldRenderSkyAtmosphere(const FScene* Scene, const FEngineShowFlags& EngineShowFlags) { if (Scene && Scene->HasSkyAtmosphere() && EngineShowFlags.Atmosphere) { EShaderPlatform ShaderPlatform = Scene->GetShaderPlatform(); const FSkyAtmosphereRenderSceneInfo* SkyAtmosphere = Scene->GetSkyAtmosphereSceneInfo(); check(SkyAtmosphere); return FReadOnlyCVARCache::SupportSkyAtmosphere() && CVarSkyAtmosphere.GetValueOnRenderThread() > 0; } return false; } static auto GetSizeAndInvSize = [](int32 Width, int32 Height) { float FWidth = float(Width); float FHeight = float(Height); return FVector4f(FWidth, FHeight, 1.0f / FWidth, 1.0f / FHeight); }; bool ShouldApplyAerialPerspective(EShaderPlatform ShaderPlatform) { static auto* MobilePixelFogQuality = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.PixelFogQuality")); return !IsMobilePlatform(ShaderPlatform) || (MobilePixelFogQuality && (MobilePixelFogQuality->GetValueOnAnyThread() == 1)); } void SetupSkyAtmosphereViewSharedUniformShaderParameters(const FViewInfo& View, const FSkyAtmosphereSceneProxy& SkyAtmosphereProxy, FSkyAtmosphereViewSharedUniformShaderParameters& OutParameters) { GET_VALID_DATA_FROM_CVAR; FRHITexture* SkyAtmosphereCameraAerialPerspectiveVolume = nullptr; if (View.SkyAtmosphereCameraAerialPerspectiveVolume) { SkyAtmosphereCameraAerialPerspectiveVolume = View.SkyAtmosphereCameraAerialPerspectiveVolume->GetRHI(); } OutParameters.CameraAerialPerspectiveVolumeSizeAndInvSize = GetSizeAndInvSize(CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeScreenResolution); OutParameters.ApplyCameraAerialPerspectiveVolume = (View.SkyAtmosphereCameraAerialPerspectiveVolume == nullptr) || !ShouldApplyAerialPerspective(View.GetShaderPlatform()) ? 0.0f : 1.0f; OutParameters.CameraAerialPerspectiveVolumeDepthResolution = float(CameraAerialPerspectiveVolumeDepthResolution); OutParameters.CameraAerialPerspectiveVolumeDepthResolutionInv = 1.0f / OutParameters.CameraAerialPerspectiveVolumeDepthResolution; OutParameters.CameraAerialPerspectiveVolumeDepthSliceLengthKm = CameraAerialPerspectiveVolumeDepthSliceLengthKm; OutParameters.CameraAerialPerspectiveVolumeDepthSliceLengthKmInv = 1.0f / OutParameters.CameraAerialPerspectiveVolumeDepthSliceLengthKm; OutParameters.AerialPerspectiveStartDepthKm = GetValidAerialPerspectiveStartDepthInCm(View, SkyAtmosphereProxy) * CM_TO_KM; SetBlackAlpha13DIfNull(SkyAtmosphereCameraAerialPerspectiveVolume); // Needs to be after we set ApplyCameraAerialPerspectiveVolume } static void CopyAtmosphereSetupToUniformShaderParameters(FAtmosphereUniformShaderParameters& out, const FAtmosphereSetup& Atmosphere) { #define COPYMACRO(MemberName) out.MemberName = Atmosphere.MemberName COPYMACRO(MultiScatteringFactor); COPYMACRO(BottomRadiusKm); COPYMACRO(TopRadiusKm); COPYMACRO(RayleighDensityExpScale); COPYMACRO(RayleighScattering); COPYMACRO(MieScattering); COPYMACRO(MieDensityExpScale); COPYMACRO(MieExtinction); COPYMACRO(MiePhaseG); COPYMACRO(MieAbsorption); COPYMACRO(AbsorptionDensity0LayerWidth); COPYMACRO(AbsorptionDensity0ConstantTerm); COPYMACRO(AbsorptionDensity0LinearTerm); COPYMACRO(AbsorptionDensity1ConstantTerm); COPYMACRO(AbsorptionDensity1LinearTerm); COPYMACRO(AbsorptionExtinction); COPYMACRO(GroundAlbedo); #undef COPYMACRO } static FLinearColor GetLightDiskLuminance(FLightSceneInfo& Light) { const float SunSolidAngle = 2.0f * PI * (1.0f - FMath::Cos(Light.Proxy->GetSunLightHalfApexAngleRadian())); // Solid angle from aperture https://en.wikipedia.org/wiki/Solid_angle return Light.Proxy->GetAtmosphereSunDiskColorScale() * Light.Proxy->GetOuterSpaceIlluminance() / SunSolidAngle; // approximation } void PrepareSunLightProxy(const FSkyAtmosphereRenderSceneInfo& SkyAtmosphere, uint32 AtmosphereLightIndex, FLightSceneInfo& AtmosphereLight) { // See explanation in "Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite" page 26 const FSkyAtmosphereSceneProxy& SkyAtmosphereProxy = SkyAtmosphere.GetSkyAtmosphereSceneProxy(); const FVector AtmosphereLightDirection = SkyAtmosphereProxy.GetAtmosphereLightDirection(AtmosphereLightIndex, -AtmosphereLight.Proxy->GetDirection()); const FLinearColor TransmittanceTowardSun = SkyAtmosphereProxy.GetAtmosphereSetup().GetTransmittanceAtGroundLevel(AtmosphereLightDirection); const FLinearColor SunDiskOuterSpaceLuminance = GetLightDiskLuminance(AtmosphereLight); AtmosphereLight.Proxy->SetAtmosphereRelatedProperties(TransmittanceTowardSun, SunDiskOuterSpaceLuminance); } bool IsLightAtmospherePerPixelTransmittanceEnabled(const FScene* Scene, const FViewInfo& View, const FLightSceneInfo* const LightSceneInfo) { if (Scene && LightSceneInfo && LightSceneInfo->Proxy->GetLightType() == LightType_Directional && ShouldRenderSkyAtmosphere(LightSceneInfo->Scene, View.Family->EngineShowFlags)) { FLightSceneProxy* AtmosphereLight0Proxy = Scene->AtmosphereLights[0] ? Scene->AtmosphereLights[0]->Proxy : nullptr; FLightSceneProxy* AtmosphereLight1Proxy = Scene->AtmosphereLights[1] ? Scene->AtmosphereLights[1]->Proxy : nullptr; // The light must be one of the atmospheric directional lights and has per pixel atmosphere transmittance enabled. return (AtmosphereLight0Proxy == LightSceneInfo->Proxy && AtmosphereLight0Proxy && AtmosphereLight0Proxy->GetUsePerPixelAtmosphereTransmittance()) || (AtmosphereLight1Proxy == LightSceneInfo->Proxy && AtmosphereLight1Proxy && AtmosphereLight1Proxy->GetUsePerPixelAtmosphereTransmittance()); } return false; } /*============================================================================= FSkyAtmosphereRenderSceneInfo implementation. =============================================================================*/ FSkyAtmosphereRenderSceneInfo::FSkyAtmosphereRenderSceneInfo(FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxyIn) :SkyAtmosphereSceneProxy(SkyAtmosphereSceneProxyIn) { // Create a multiframe uniform buffer. A render command is used because FSkyAtmosphereRenderSceneInfo ctor is called on the Game thread. TUniformBufferRef* AtmosphereUniformBufferPtr = &AtmosphereUniformBuffer; FAtmosphereUniformShaderParameters* AtmosphereUniformShaderParametersPtr = &AtmosphereUniformShaderParameters; CopyAtmosphereSetupToUniformShaderParameters(AtmosphereUniformShaderParameters, SkyAtmosphereSceneProxy.GetAtmosphereSetup()); ENQUEUE_RENDER_COMMAND(FCreateUniformBuffer)( [AtmosphereUniformBufferPtr, AtmosphereUniformShaderParametersPtr](FRHICommandListImmediate& RHICmdList) { *AtmosphereUniformBufferPtr = TUniformBufferRef::CreateUniformBufferImmediate(*AtmosphereUniformShaderParametersPtr, UniformBuffer_MultiFrame); }); } FSkyAtmosphereRenderSceneInfo::~FSkyAtmosphereRenderSceneInfo() { } void FSkyAtmosphereRenderSceneInfo::CreateDistantSkyLightLutBufferAndSRV(FRDGBuilder& GraphBuilder) { DistantSkyLightLutBuffer = AllocatePooledBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FVector4f), 1), TEXT("SkyAtmosphere.DistantSkyLightLutBuffer")); DistantSkyLightLutBufferSRV = DistantSkyLightLutBuffer->GetOrCreateSRV(GraphBuilder.RHICmdList, FRHIBufferSRVCreateInfo(PF_A32B32G32R32F)); MobileDistantSkyLightLutBuffer = AllocatePooledBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(FVector4f), 1), TEXT("SkyAtmosphere.MobileDistantSkyLightLutBuffer")); MobileDistantSkyLightLutBufferSRV = MobileDistantSkyLightLutBuffer->GetOrCreateSRV(GraphBuilder.RHICmdList, FRHIBufferSRVCreateInfo(PF_A32B32G32R32F)); } TRefCountPtr& FSkyAtmosphereRenderSceneInfo::GetDistantSkyLightLutBuffer() { check(CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0); return DistantSkyLightLutBuffer; } TRefCountPtr& FSkyAtmosphereRenderSceneInfo::GetMobileDistantSkyLightLutBuffer() { check(CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0); return MobileDistantSkyLightLutBuffer; } FRHIShaderResourceView* FSkyAtmosphereRenderSceneInfo::GetDistantSkyLightLutBufferSRV() { if (CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { return DistantSkyLightLutBufferSRV; } return GBlackFloat4StructuredBufferWithSRV->ShaderResourceViewRHI; } FRHIShaderResourceView* FSkyAtmosphereRenderSceneInfo::GetMobileDistantSkyLightLutBufferSRV() { if (CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { return MobileDistantSkyLightLutBufferSRV; } return GBlackFloat4VertexBufferWithSRV->ShaderResourceViewRHI; } /*============================================================================= FScene functions =============================================================================*/ void FScene::AddSkyAtmosphere(FSkyAtmosphereSceneProxy* SkyAtmosphereSceneProxy, bool bStaticLightingBuilt) { check(SkyAtmosphereSceneProxy); FScene* Scene = this; ENQUEUE_RENDER_COMMAND(FAddSkyAtmosphereCommand)( [Scene, SkyAtmosphereSceneProxy, bStaticLightingBuilt](FRHICommandListImmediate& RHICmdList) { check(!Scene->SkyAtmosphereStack.Contains(SkyAtmosphereSceneProxy)); Scene->SkyAtmosphereStack.Push(SkyAtmosphereSceneProxy); SkyAtmosphereSceneProxy->RenderSceneInfo = new FSkyAtmosphereRenderSceneInfo(*SkyAtmosphereSceneProxy); // Use the most recently enabled SkyAtmosphere Scene->SkyAtmosphere = SkyAtmosphereSceneProxy->RenderSceneInfo; SkyAtmosphereSceneProxy->bStaticLightingBuilt = bStaticLightingBuilt; if (!SkyAtmosphereSceneProxy->bStaticLightingBuilt) { FPlatformAtomics::InterlockedIncrement(&Scene->NumUncachedStaticLightingInteractions); } Scene->InvalidatePathTracedOutput(); } ); } void FScene::RemoveSkyAtmosphere(FSkyAtmosphereSceneProxy* SkyAtmosphereSceneProxy) { check(SkyAtmosphereSceneProxy); FScene* Scene = this; ENQUEUE_RENDER_COMMAND(FRemoveSkyAtmosphereCommand)( [Scene, SkyAtmosphereSceneProxy](FRHICommandListImmediate& RHICmdList) { if (!SkyAtmosphereSceneProxy->bStaticLightingBuilt) { FPlatformAtomics::InterlockedDecrement(&Scene->NumUncachedStaticLightingInteractions); } delete SkyAtmosphereSceneProxy->RenderSceneInfo; Scene->SkyAtmosphereStack.RemoveSingle(SkyAtmosphereSceneProxy); if (Scene->SkyAtmosphereStack.Num() > 0) { // Use the most recently enabled SkyAtmosphere Scene->SkyAtmosphere = Scene->SkyAtmosphereStack.Last()->RenderSceneInfo; } else { Scene->SkyAtmosphere = nullptr; } Scene->InvalidatePathTracedOutput(); } ); } void FScene::ResetAtmosphereLightsProperties() { // Also rest the current atmospheric light to default atmosphere for (int32 LightIndex = 0; LightIndex < NUM_ATMOSPHERE_LIGHTS; ++LightIndex) { FLightSceneInfo* Light = AtmosphereLights[LightIndex]; if (Light) { const FLinearColor TransmittanceTowardSun = FLinearColor::White; Light->Proxy->SetAtmosphereRelatedProperties(TransmittanceTowardSun, GetLightDiskLuminance(*Light)); } } } /*============================================================================= Sky/Atmosphere rendering functions =============================================================================*/ namespace { class FHighQualityMultiScatteringApprox : SHADER_PERMUTATION_BOOL("HIGHQUALITY_MULTISCATTERING_APPROX_ENABLED"); class FFastSky : SHADER_PERMUTATION_BOOL("FASTSKY_ENABLED"); class FFastAerialPespective : SHADER_PERMUTATION_BOOL("FASTAERIALPERSPECTIVE_ENABLED"); class FSecondAtmosphereLight : SHADER_PERMUTATION_BOOL("SECOND_ATMOSPHERE_LIGHT_ENABLED"); class FRenderSky : SHADER_PERMUTATION_BOOL("RENDERSKY_ENABLED"); class FSampleOpaqueShadow : SHADER_PERMUTATION_BOOL("SAMPLE_OPAQUE_SHADOW"); class FSampleCloudShadow : SHADER_PERMUTATION_BOOL("SAMPLE_CLOUD_SHADOW"); class FSampleCloudSkyAO : SHADER_PERMUTATION_BOOL("SAMPLE_CLOUD_SKYAO"); class FAtmosphereOnClouds : SHADER_PERMUTATION_BOOL("SAMPLE_ATMOSPHERE_ON_CLOUDS"); class FMSAASampleCount : SHADER_PERMUTATION_SPARSE_INT("MSAA_SAMPLE_COUNT", 1, 2, 4, 8); class FSeparateMieAndRayleighScattering : SHADER_PERMUTATION_BOOL("SEPARATE_MIE_RAYLEIGH_SCATTERING"); class FRealTimeReflectionLUT : SHADER_PERMUTATION_BOOL("PERMUTATION_REALTIME_REFLECTION_LUT"); } ////////////////////////////////////////////////////////////////////////// class FRenderSkyAtmosphereVS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderSkyAtmosphereVS); SHADER_USE_PARAMETER_STRUCT(FRenderSkyAtmosphereVS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(float, StartDepthZ) END_SHADER_PARAMETER_STRUCT() static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { // Mobile must use a skydome mesh with a sky material to achieve good GPU performance return !IsMobilePlatform(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); } }; IMPLEMENT_GLOBAL_SHADER(FRenderSkyAtmosphereVS, "/Engine/Private/SkyAtmosphere.usf", "SkyAtmosphereVS", SF_Vertex); class FRenderSkyAtmospherePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderSkyAtmospherePS); SHADER_USE_PARAMETER_STRUCT(FRenderSkyAtmospherePS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) RENDER_TARGET_BINDING_SLOTS() SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, MultiScatteredLuminanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SkyViewLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture3D, CameraAerialPerspectiveVolumeTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture0) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture1) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudSkyAOTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D , VolumetricCloudDepthTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputCloudLuminanceTransmittanceTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2DMS, MSAADepthTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, MultiScatteredLuminanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, SkyViewLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, CameraAerialPerspectiveVolumeTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture0Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture1Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudSkyAOTextureSampler) SHADER_PARAMETER(float, AerialPerspectiveStartDepthKm) SHADER_PARAMETER(uint32, bPropagateAlphaNonReflection) SHADER_PARAMETER(uint32, SourceDiskEnabled) SHADER_PARAMETER(uint32, DepthReadDisabled) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal0, Light0Shadow) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal1, Light1Shadow) SHADER_PARAMETER(float, VolumetricCloudShadowStrength0) SHADER_PARAMETER(float, VolumetricCloudShadowStrength1) SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMap) SHADER_PARAMETER(int32, VirtualShadowMapId0) SHADER_PARAMETER(int32, VirtualShadowMapId1) SHADER_PARAMETER_STRUCT_REF(FVolumetricCloudCommonGlobalShaderParameters, VolumetricCloudCommonGlobalParams) END_SHADER_PARAMETER_STRUCT() static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { // If not rendering the sky, ignore the fastsky and sundisk permutations if (PermutationVector.Get() == false) { PermutationVector.Set(false); } if (PermutationVector.Get() == true) { PermutationVector.Set(false); PermutationVector.Set(false); PermutationVector.Set(false); } return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { if (IsMobilePlatform(Parameters.Platform)) { // Mobile must use a skydome mesh with a sky material to achieve good GPU performance return false; } FPermutationDomain PermutationVector(Parameters.PermutationId); // If not rendering the sky, ignore the FFastSky permutation if (PermutationVector.Get() == false && PermutationVector.Get()) { return false; } if (PermutationVector.Get() == true) { // FSampleCloudSkyAO, FFastSky, FFastAerialPespective, FSecondAtmosphereLight, FRenderSky, FSampleOpaqueShadow, FSampleCloudShadow // When tracing atmosphere on clouds, this is because we want crisp light shaft on them. if (PermutationVector.Get() || PermutationVector.Get() || PermutationVector.Get()) { return false; } } if ((!IsForwardShadingEnabled(Parameters.Platform) || !RHISupportsMSAA(Parameters.Platform)) && PermutationVector.Get() > 1) { // We only compile the MSAA support when Forward shading is enabled because MSAA can only be used in this case. return false; } return true; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FPermutationDomain PermutationVector(Parameters.PermutationId); FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("PER_PIXEL_NOISE"), 1); OutEnvironment.SetDefine(TEXT("MULTISCATTERING_APPROX_SAMPLING_ENABLED"), 1); OutEnvironment.SetDefine(TEXT("SOURCE_DISK_ENABLED"), 1); if (PermutationVector.Get() && VirtualShadowMapSamplingSupported(Parameters.Platform)) { OutEnvironment.SetDefine(TEXT("VIRTUAL_SHADOW_MAP"), 1); FVirtualShadowMapArray::SetShaderDefines(OutEnvironment); } } }; IMPLEMENT_GLOBAL_SHADER(FRenderSkyAtmospherePS, "/Engine/Private/SkyAtmosphere.usf", "RenderSkyAtmosphereRayMarchingPS", SF_Pixel); ////////////////////////////////////////////////////////////////////////// class FRenderTransmittanceLutCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderTransmittanceLutCS); SHADER_USE_PARAMETER_STRUCT(FRenderTransmittanceLutCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; public: const static uint32 GroupSize = 8; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, TransmittanceLutUAV) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GroupSize); OutEnvironment.SetDefine(TEXT("WHITE_TRANSMITTANCE"),1); // Workaround for some compiler not culling enough unused code (e.g. when computing TransmittanceLUT, Transmittance texture is still requested but we are computing it) OutEnvironment.SetDefine(TEXT("TRANSMITTANCE_PASS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FRenderTransmittanceLutCS, "/Engine/Private/SkyAtmosphere.usf", "RenderTransmittanceLutCS", SF_Compute); ////////////////////////////////////////////////////////////////////////// class FRenderMultiScatteredLuminanceLutCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderMultiScatteredLuminanceLutCS); SHADER_USE_PARAMETER_STRUCT(FRenderMultiScatteredLuminanceLutCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; public: const static uint32 GroupSize = 8; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, MultiScatteredLuminanceLutUAV) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_SRV(Buffer, UniformSphereSamplesBuffer) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER(uint32, UniformSphereSamplesBufferSampleCount) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GroupSize); OutEnvironment.SetDefine(TEXT("MULTISCATT_PASS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FRenderMultiScatteredLuminanceLutCS, "/Engine/Private/SkyAtmosphere.usf", "RenderMultiScatteredLuminanceLutCS", SF_Compute); ////////////////////////////////////////////////////////////////////////// class FRenderDistantSkyLightLutCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderDistantSkyLightLutCS); SHADER_USE_PARAMETER_STRUCT(FRenderDistantSkyLightLutCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; public: const static uint32 GroupSize = 8; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, DistantSkyLightLutBufferUAV) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, MobileDistantSkyLightLutBufferUAV) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, MultiScatteredLuminanceLutTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, MultiScatteredLuminanceLutTextureSampler) SHADER_PARAMETER_SRV(Buffer, UniformSphereSamplesBuffer) SHADER_PARAMETER(FVector4f, AtmosphereLightDirection0) SHADER_PARAMETER(FVector4f, AtmosphereLightDirection1) SHADER_PARAMETER(FLinearColor, AtmosphereLightIlluminanceOuterSpace0) SHADER_PARAMETER(FLinearColor, AtmosphereLightIlluminanceOuterSpace1) SHADER_PARAMETER(float, DistantSkyLightSampleAltitude) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GroupSize); OutEnvironment.SetDefine(TEXT("SKYLIGHT_PASS"), 1); OutEnvironment.SetDefine(TEXT("MULTISCATTERING_APPROX_SAMPLING_ENABLED"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FRenderDistantSkyLightLutCS, "/Engine/Private/SkyAtmosphere.usf", "RenderDistantSkyLightLutCS", SF_Compute); ////////////////////////////////////////////////////////////////////////// class FRenderSkyViewLutCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderSkyViewLutCS); SHADER_USE_PARAMETER_STRUCT(FRenderSkyViewLutCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; public: const static uint32 GroupSize = 8; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereRealTimeReflectionLUTParameters, SkyAtmosphereRealTimeReflectionLUTUniformBuffer) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SkyViewLutUAV) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, MultiScatteredLuminanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture0) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture1) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudSkyAOTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, MultiScatteredLuminanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture0Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture1Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudSkyAOTextureSampler) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal0, Light0Shadow) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal1, Light1Shadow) SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMap) SHADER_PARAMETER(int32, VirtualShadowMapId0) SHADER_PARAMETER(int32, VirtualShadowMapId1) SHADER_PARAMETER(float, VolumetricCloudShadowStrength0) SHADER_PARAMETER(float, VolumetricCloudShadowStrength1) SHADER_PARAMETER_STRUCT_REF(FVolumetricCloudCommonGlobalShaderParameters, VolumetricCloudCommonGlobalParams) SHADER_PARAMETER(uint32, SourceDiskEnabled) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FPermutationDomain PermutationVector(Parameters.PermutationId); FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GroupSize); OutEnvironment.SetDefine(TEXT("SKYVIEWLUT_PASS"), 1); OutEnvironment.SetDefine(TEXT("MULTISCATTERING_APPROX_SAMPLING_ENABLED"), 1); OutEnvironment.SetDefine(TEXT("SOURCE_DISK_ENABLED"), 1); if (PermutationVector.Get() && VirtualShadowMapSamplingSupported(Parameters.Platform)) { OutEnvironment.SetDefine(TEXT("VIRTUAL_SHADOW_MAP"), 1); FVirtualShadowMapArray::SetShaderDefines(OutEnvironment); } } }; IMPLEMENT_GLOBAL_SHADER(FRenderSkyViewLutCS, "/Engine/Private/SkyAtmosphere.usf", "RenderSkyViewLutCS", SF_Compute); ////////////////////////////////////////////////////////////////////////// class FRenderCameraAerialPerspectiveVolumeCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderCameraAerialPerspectiveVolumeCS); SHADER_USE_PARAMETER_STRUCT(FRenderCameraAerialPerspectiveVolumeCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; public: const static uint32 GroupSize = 4; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereRealTimeReflectionLUTParameters, SkyAtmosphereRealTimeReflectionLUTUniformBuffer) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, CameraAerialPerspectiveVolumeUAV) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, CameraAerialPerspectiveVolumeMieOnlyUAV) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, CameraAerialPerspectiveVolumeRayOnlyUAV) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, MultiScatteredLuminanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture0) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudShadowMapTexture1) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VolumetricCloudSkyAOTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, MultiScatteredLuminanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture0Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudShadowMapTexture1Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, VolumetricCloudSkyAOTextureSampler) SHADER_PARAMETER(float, AerialPerspectiveStartDepthKm) SHADER_PARAMETER(float, RealTimeReflection360Mode) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal0, Light0Shadow) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVolumeShadowingShaderParametersGlobal1, Light1Shadow) SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, VirtualShadowMap) SHADER_PARAMETER(int32, VirtualShadowMapId0) SHADER_PARAMETER(int32, VirtualShadowMapId1) SHADER_PARAMETER(float, VolumetricCloudShadowStrength0) SHADER_PARAMETER(float, VolumetricCloudShadowStrength1) SHADER_PARAMETER_STRUCT_REF(FVolumetricCloudCommonGlobalShaderParameters, VolumetricCloudCommonGlobalParams) END_SHADER_PARAMETER_STRUCT() static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FPermutationDomain PermutationVector(Parameters.PermutationId); FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GroupSize); OutEnvironment.SetDefine(TEXT("MULTISCATTERING_APPROX_SAMPLING_ENABLED"), 1); if (PermutationVector.Get() && VirtualShadowMapSamplingSupported(Parameters.Platform)) { OutEnvironment.SetDefine(TEXT("VIRTUAL_SHADOW_MAP"), 1); FVirtualShadowMapArray::SetShaderDefines(OutEnvironment); } } }; IMPLEMENT_GLOBAL_SHADER(FRenderCameraAerialPerspectiveVolumeCS, "/Engine/Private/SkyAtmosphere.usf", "RenderCameraAerialPerspectiveVolumeCS", SF_Compute); ////////////////////////////////////////////////////////////////////////// class FRenderDebugSkyAtmospherePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRenderDebugSkyAtmospherePS); SHADER_USE_PARAMETER_STRUCT(FRenderDebugSkyAtmospherePS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FAtmosphereUniformShaderParameters, Atmosphere) SHADER_PARAMETER_STRUCT_REF(FSkyAtmosphereInternalCommonParameters, SkyAtmosphere) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) RENDER_TARGET_BINDING_SLOTS() SHADER_PARAMETER_RDG_TEXTURE(Texture2D, TransmittanceLutTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, MultiScatteredLuminanceLutTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TransmittanceLutTextureSampler) SHADER_PARAMETER_SAMPLER(SamplerState, MultiScatteredLuminanceLutTextureSampler) SHADER_PARAMETER(float, ViewPortWidth) SHADER_PARAMETER(float, ViewPortHeight) END_SHADER_PARAMETER_STRUCT() static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { // TODO: Exclude when shipping. return true; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("MULTISCATTERING_APPROX_SAMPLING_ENABLED"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FRenderDebugSkyAtmospherePS, "/Engine/Private/SkyAtmosphere.usf", "RenderSkyAtmosphereDebugPS", SF_Pixel); ////////////////////////////////////////////////////////////////////////// class RenderSkyAtmosphereEditorHudPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(RenderSkyAtmosphereEditorHudPS); SHADER_USE_PARAMETER_STRUCT(RenderSkyAtmosphereEditorHudPS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_TEXTURE(Texture2D, MiniFontTexture) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { // TODO: Exclude when shipping. return true; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_EDITOR_HUD"), 1); } }; IMPLEMENT_GLOBAL_SHADER(RenderSkyAtmosphereEditorHudPS, "/Engine/Private/SkyAtmosphere.usf", "RenderSkyAtmosphereEditorHudPS", SF_Pixel); /*============================================================================= FUniformSphereSamplesBuffer =============================================================================*/ class FUniformSphereSamplesBuffer : public FRenderResource { public: FReadBuffer UniformSphereSamplesBuffer; uint32 GetSampletCount() { return FRenderDistantSkyLightLutCS::GroupSize; } virtual void InitRHI(FRHICommandListBase& RHICmdList) override { const uint32 GroupSize = GetSampletCount(); const float GroupSizeInv = 1.0f / float(GroupSize); UniformSphereSamplesBuffer.InitializeWithData(RHICmdList, TEXT("UniformSphereSamplesBuffer"), sizeof(FVector4f), GroupSize * GroupSize, PF_A32B32G32R32F, EBufferUsageFlags::Static, [GroupSize, GroupSizeInv](FRHIBufferInitializer& Initializer) { TArrayView Dest = Initializer.GetWriteView(); FRandomStream RandomStream(0xDE4DC0DE); for (uint32 i = 0; i < GroupSize; ++i) { for (uint32 j = 0; j < GroupSize; ++j) { const float u0 = (float(i) + RandomStream.GetFraction()) * GroupSizeInv; const float u1 = (float(j) + RandomStream.GetFraction()) * GroupSizeInv; const float a = 1.0f - 2.0f * u0; const float b = FMath::Sqrt(1.0f - a * a); const float phi = 2 * PI * u1; uint32 idx = j * GroupSize + i; Dest[idx].X = b * FMath::Cos(phi); Dest[idx].Y = b * FMath::Sin(phi); Dest[idx].Z = a; Dest[idx].W = 0.0f; } } }); } virtual void ReleaseRHI() { UniformSphereSamplesBuffer.Release(); } }; TGlobalResource GUniformSphereSamplesBuffer; /*============================================================================= FSceneRenderer functions =============================================================================*/ void FSceneRenderer::InitSkyAtmosphereForViews(FRHICommandListImmediate& RHICmdList, FRDGBuilder& GraphBuilder) { InitSkyAtmosphereForScene(RHICmdList, GraphBuilder, Scene); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; InitSkyAtmosphereForView(RHICmdList, Scene, View); } } static EPixelFormat GetSkyLutTextureFormat(ERHIFeatureLevel::Type FeatureLevel) { EPixelFormat TextureLUTFormat = PF_FloatRGB; const bool bSupportsAlpha = IsPostProcessingWithAlphaChannelSupported(); if (FeatureLevel <= ERHIFeatureLevel::ES3_1 || bSupportsAlpha) { // OpenGL ES3.1 does not support storing into 3-component images // TODO: check if need this for Metal, Vulkan TextureLUTFormat = PF_FloatRGBA; } return TextureLUTFormat; } static EPixelFormat GetSkyLutSmallTextureFormat() { return PF_R8G8B8A8; } void InitSkyAtmosphereForScene(FRHICommandListImmediate& RHICmdList, FRDGBuilder& GraphBuilder, FScene* Scene) { if (Scene) { GET_VALID_DATA_FROM_CVAR; FPooledRenderTargetDesc Desc; check(Scene->GetSkyAtmosphereSceneInfo()); FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); EPixelFormat TextureLUTFormat = GetSkyLutTextureFormat(Scene->GetFeatureLevel()); EPixelFormat TextureLUTSmallFormat = GetSkyLutSmallTextureFormat(); // // Initialise per scene/atmosphere resources // if (CVarSkyAtmosphereTransmittanceLUT.GetValueOnAnyThread() > 0) { const bool TranstmittanceLUTUseSmallFormat = CVarSkyAtmosphereTransmittanceLUTUseSmallFormat.GetValueOnRenderThread() > 0; TRefCountPtr& TransmittanceLutTexture = SkyInfo.GetTransmittanceLutTexture(); Desc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(TransmittanceLutWidth, TransmittanceLutHeight), TranstmittanceLUTUseSmallFormat ? TextureLUTSmallFormat : TextureLUTFormat, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV, false); GRenderTargetPool.FindFreeElement(RHICmdList, Desc, TransmittanceLutTexture, TEXT("SkyAtmosphere.TransmittanceLut")); } else { SkyInfo.GetTransmittanceLutTexture() = GSystemTextures.WhiteDummy; } TRefCountPtr& MultiScatteredLuminanceLutTexture = SkyInfo.GetMultiScatteredLuminanceLutTexture(); Desc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(MultiScatteredLuminanceLutWidth, MultiScatteredLuminanceLutHeight), TextureLUTFormat, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV, false); GRenderTargetPool.FindFreeElement(RHICmdList, Desc, MultiScatteredLuminanceLutTexture, TEXT("SkyAtmosphere.MultiScatteredLuminanceLut")); if (CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { SkyInfo.CreateDistantSkyLightLutBufferAndSRV(GraphBuilder); } } } void InitSkyAtmosphereForView(FRHICommandListImmediate& RHICmdList, const FScene* Scene, FViewInfo& View) { if (Scene) { GET_VALID_DATA_FROM_CVAR; check(ShouldRenderSkyAtmosphere(Scene, View.Family->EngineShowFlags)); // This should not be called if we should not render SkyAtmosphere FPooledRenderTargetDesc Desc; check(Scene->GetSkyAtmosphereSceneInfo()); const FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); EPixelFormat TextureLUTFormat = GetSkyLutTextureFormat(Scene->GetFeatureLevel()); EPixelFormat TextureLUTSmallFormat = GetSkyLutSmallTextureFormat(); EPixelFormat TextureAerialLUTFormat = (CVarSkyAtmosphereLUT32.GetValueOnAnyThread() != 0) ? PF_A32B32G32R32F : PF_FloatRGBA; // // Initialise view resources. // FPooledRenderTargetDesc SkyAtmosphereViewLutTextureDesc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(SkyViewLutWidth, SkyViewLutHeight), TextureLUTFormat, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV, false); FPooledRenderTargetDesc SkyAtmosphereCameraAerialPerspectiveVolumeDesc = FPooledRenderTargetDesc::CreateVolumeDesc( CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeDepthResolution, TextureAerialLUTFormat, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_UAV, false); // Set textures and data that will be needed later on the view. View.SkyAtmosphereUniformShaderParameters = SkyInfo.GetAtmosphereShaderParameters(); GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereViewLutTextureDesc, View.SkyAtmosphereViewLutTexture, TEXT("SkyAtmosphere.SkyViewLut")); const bool bSeparatedAtmosphereMieRayLeigh = VolumetricCloudWantsSeparatedAtmosphereMieRayLeigh(Scene); if (View.ViewState) { // Per view non transient double buffered resources when needed. View.ViewState->PersistentSkyAtmosphereData.InitialiseOrNextFrame(View.FeatureLevel, SkyAtmosphereCameraAerialPerspectiveVolumeDesc, RHICmdList, bSeparatedAtmosphereMieRayLeigh); View.SkyAtmosphereCameraAerialPerspectiveVolume = View.ViewState->PersistentSkyAtmosphereData.GetCurrentCameraAerialPerspectiveVolume(); if (bSeparatedAtmosphereMieRayLeigh) { View.SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly = View.ViewState->PersistentSkyAtmosphereData.GetCurrentCameraAerialPerspectiveVolumeMieOnly(); View.SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly = View.ViewState->PersistentSkyAtmosphereData.GetCurrentCameraAerialPerspectiveVolumeRayOnly(); } } else { // Per frame transient resource for reflection views GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereCameraAerialPerspectiveVolumeDesc, View.SkyAtmosphereCameraAerialPerspectiveVolume, TEXT("SkyAtmosphere.CameraAPVolume")); if (bSeparatedAtmosphereMieRayLeigh) { GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereCameraAerialPerspectiveVolumeDesc, View.SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly, TEXT("SkyAtmosphere.CameraAPVolumeMieOnly")); GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereCameraAerialPerspectiveVolumeDesc, View.SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly, TEXT("SkyAtmosphere.CameraAPVolumeRayOnly")); } } } } static void SetupSkyAtmosphereInternalCommonParameters( FSkyAtmosphereInternalCommonParameters& InternalCommonParameters, const FScene& Scene, const FSceneViewFamily& ViewFamily, const FSkyAtmosphereRenderSceneInfo& SkyInfo) { GET_VALID_DATA_FROM_CVAR; InternalCommonParameters.TransmittanceLutSizeAndInvSize = GetSizeAndInvSize(TransmittanceLutWidth, TransmittanceLutHeight); InternalCommonParameters.MultiScatteredLuminanceLutSizeAndInvSize = GetSizeAndInvSize(MultiScatteredLuminanceLutWidth, MultiScatteredLuminanceLutHeight); InternalCommonParameters.SkyViewLutSizeAndInvSize = GetSizeAndInvSize(SkyViewLutWidth, SkyViewLutHeight); const float SkyAtmosphereBaseSampleCount = 32.0f; const float AerialPerspectiveBaseSampleCountPerSlice = 1.0f; InternalCommonParameters.SampleCountMin = CVarSkyAtmosphereSampleCountMin.GetValueOnRenderThread(); InternalCommonParameters.SampleCountMax = FMath::Min(SkyAtmosphereBaseSampleCount * SkyInfo.GetSkyAtmosphereSceneProxy().GetTraceSampleCountScale(), float(CVarSkyAtmosphereSampleCountMax.GetValueOnRenderThread())); float DistanceToSampleCountMaxInv = CVarSkyAtmosphereDistanceToSampleCountMax.GetValueOnRenderThread(); InternalCommonParameters.FastSkySampleCountMin = CVarSkyAtmosphereFastSkyLUTSampleCountMin.GetValueOnRenderThread(); InternalCommonParameters.FastSkySampleCountMax = FMath::Min(SkyAtmosphereBaseSampleCount * SkyInfo.GetSkyAtmosphereSceneProxy().GetTraceSampleCountScale(), float(CVarSkyAtmosphereFastSkyLUTSampleCountMax.GetValueOnRenderThread())); float FastSkyDistanceToSampleCountMaxInv = CVarSkyAtmosphereFastSkyLUTDistanceToSampleCountMax.GetValueOnRenderThread(); InternalCommonParameters.CameraAerialPerspectiveVolumeDepthResolution = float(CameraAerialPerspectiveVolumeDepthResolution); InternalCommonParameters.CameraAerialPerspectiveVolumeDepthResolutionInv = 1.0f / InternalCommonParameters.CameraAerialPerspectiveVolumeDepthResolution; InternalCommonParameters.CameraAerialPerspectiveVolumeDepthSliceLengthKm = CameraAerialPerspectiveVolumeDepthSliceLengthKm; InternalCommonParameters.CameraAerialPerspectiveVolumeDepthSliceLengthKmInv = 1.0f / CameraAerialPerspectiveVolumeDepthSliceLengthKm; InternalCommonParameters.CameraAerialPerspectiveSampleCountPerSlice = FMath::Max(AerialPerspectiveBaseSampleCountPerSlice, FMath::Min(2.0f * SkyInfo.GetSkyAtmosphereSceneProxy().GetTraceSampleCountScale(), float(CVarSkyAtmosphereAerialPerspectiveLUTSampleCountMaxPerSlice.GetValueOnRenderThread()))); InternalCommonParameters.TransmittanceSampleCount = CVarSkyAtmosphereTransmittanceLUTSampleCount.GetValueOnRenderThread(); InternalCommonParameters.MultiScatteringSampleCount = CVarSkyAtmosphereMultiScatteringLUTSampleCount.GetValueOnRenderThread(); const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy(); InternalCommonParameters.SkyLuminanceFactor = FVector3f(SkyAtmosphereSceneProxy.GetSkyLuminanceFactor()); InternalCommonParameters.SkyAndAerialPerspectiveLuminanceFactor = FVector3f(SkyAtmosphereSceneProxy.GetSkyAndAerialPerspectiveLuminanceFactor()); InternalCommonParameters.AerialPespectiveViewDistanceScale = SkyAtmosphereSceneProxy.GetAerialPespectiveViewDistanceScale(); InternalCommonParameters.FogShowFlagFactor = ViewFamily.EngineShowFlags.Fog ? 1.0f : 0.0f; auto ValidateDistanceValue = [](float& Value) { Value = Value < KINDA_SMALL_NUMBER ? KINDA_SMALL_NUMBER : Value; }; auto ValidateSampleCountValue = [](float& Value) { Value = Value < 1.0f ? 1.0f : Value; }; auto ValidateMaxSampleCountValue = [](float& Value, float& MinValue) { Value = Value < MinValue ? MinValue : Value; }; ValidateSampleCountValue(InternalCommonParameters.SampleCountMin); ValidateMaxSampleCountValue(InternalCommonParameters.SampleCountMax, InternalCommonParameters.SampleCountMin); ValidateSampleCountValue(InternalCommonParameters.FastSkySampleCountMin); ValidateMaxSampleCountValue(InternalCommonParameters.FastSkySampleCountMax, InternalCommonParameters.FastSkySampleCountMin); ValidateSampleCountValue(InternalCommonParameters.CameraAerialPerspectiveSampleCountPerSlice); ValidateSampleCountValue(InternalCommonParameters.TransmittanceSampleCount); ValidateSampleCountValue(InternalCommonParameters.MultiScatteringSampleCount); ValidateDistanceValue(DistanceToSampleCountMaxInv); ValidateDistanceValue(FastSkyDistanceToSampleCountMaxInv); // Derived values post validation InternalCommonParameters.DistanceToSampleCountMaxInv = 1.0f / DistanceToSampleCountMaxInv; InternalCommonParameters.FastSkyDistanceToSampleCountMaxInv = 1.0f / FastSkyDistanceToSampleCountMaxInv; InternalCommonParameters.CameraAerialPerspectiveVolumeSizeAndInvSize = GetSizeAndInvSize(CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeScreenResolution); } void FSceneRenderer::RenderSkyAtmosphereLookUpTables(FRDGBuilder& GraphBuilder, class FSkyAtmospherePendingRDGResources& PendingRDGResources) { check(ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)); // This should not be called if we should not render SkyAtmosphere RDG_EVENT_SCOPE_STAT(GraphBuilder, SkyAtmosphereLUTs, "SkyAtmosphereLUTs"); RDG_GPU_STAT_SCOPE(GraphBuilder, SkyAtmosphereLUTs); RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SkyAtmosphere); SCOPED_NAMED_EVENT(RenderSkyAtmosphereLookUpTables, FColor::Emerald); FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy(); PendingRDGResources.SceneRenderer = this; PendingRDGResources.ViewResources.SetNum(Views.Num()); const bool bHighQualityMultiScattering = CVarSkyAtmosphereMultiScatteringLUTHighQuality.GetValueOnRenderThread() > 0; const bool bSecondAtmosphereLightEnabled = Scene->IsSecondAtmosphereLightEnabled(); const bool bSeparatedAtmosphereMieRayLeigh = VolumetricCloudWantsSeparatedAtmosphereMieRayLeigh(Scene); FRHISamplerState* SamplerLinearClamp = TStaticSamplerState::GetRHI(); // Initialise common internal parameters on the sky info for this frame FSkyAtmosphereInternalCommonParameters InternalCommonParameters; SetupSkyAtmosphereInternalCommonParameters(InternalCommonParameters, *Scene, ViewFamily, SkyInfo); SkyInfo.GetInternalCommonParametersUniformBuffer() = TUniformBufferRef::CreateUniformBufferImmediate(InternalCommonParameters, UniformBuffer_SingleFrame); FRDGTextureRef TransmittanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetTransmittanceLutTexture()); FRDGTextureRef MultiScatteredLuminanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetMultiScatteredLuminanceLutTexture()); FRDGTextureUAVRef MultiScatteredLuminanceLutUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(MultiScatteredLuminanceLut, 0)); ERDGPassFlags PassFlag = CVarSkyAtmosphereAsyncCompute.GetValueOnAnyThread() ? ERDGPassFlags::AsyncCompute : ERDGPassFlags::Compute; // Transmittance LUT FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel); if (CVarSkyAtmosphereTransmittanceLUT.GetValueOnRenderThread() > 0) { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::TransmittanceLut"); TShaderMapRef ComputeShader(GlobalShaderMap); FRenderTransmittanceLutCS::FParameters * PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->TransmittanceLutUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(TransmittanceLut, 0)); FIntVector TextureSize = TransmittanceLut->Desc.GetSize(); TextureSize.Z = 1; const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderTransmittanceLutCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("TransmittanceLut"), PassFlag, ComputeShader, PassParameters, NumGroups); } // Multi-Scattering LUT { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::MultiScatteringLut"); FRenderMultiScatteredLuminanceLutCS::FPermutationDomain PermutationVector; PermutationVector.Set(bHighQualityMultiScattering); TShaderMapRef ComputeShader(GlobalShaderMap, PermutationVector); FRenderMultiScatteredLuminanceLutCS::FParameters * PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->UniformSphereSamplesBuffer = GUniformSphereSamplesBuffer.UniformSphereSamplesBuffer.SRV; PassParameters->UniformSphereSamplesBufferSampleCount = GUniformSphereSamplesBuffer.GetSampletCount(); PassParameters->MultiScatteredLuminanceLutUAV = MultiScatteredLuminanceLutUAV; FIntVector TextureSize = MultiScatteredLuminanceLut->Desc.GetSize(); TextureSize.Z = 1; const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderMultiScatteredLuminanceLutCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("MultiScatteringLut"), PassFlag, ComputeShader, PassParameters, NumGroups); } // Distant Sky Light LUT if(CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::DistantSkyLightLut"); FRDGBufferRef DistantSkyLightLutBuffer = GraphBuilder.RegisterExternalBuffer(SkyInfo.GetDistantSkyLightLutBuffer()); FRDGBufferUAVRef DistantSkyLightLutBufferUAV = GraphBuilder.CreateUAV(FRDGBufferUAVDesc(DistantSkyLightLutBuffer, PF_A32B32G32R32F)); FRDGBufferRef MobileDistantSkyLightLutBuffer = GraphBuilder.RegisterExternalBuffer(SkyInfo.GetMobileDistantSkyLightLutBuffer()); FRDGBufferUAVRef MobileDistantSkyLightLutBufferUAV = GraphBuilder.CreateUAV(FRDGBufferUAVDesc(MobileDistantSkyLightLutBuffer, PF_A32B32G32R32F)); FRenderDistantSkyLightLutCS::FPermutationDomain PermutationVector; PermutationVector.Set(bSecondAtmosphereLightEnabled); TShaderMapRef ComputeShader(GlobalShaderMap, PermutationVector); FRenderDistantSkyLightLutCS::FParameters * PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->UniformSphereSamplesBuffer = GUniformSphereSamplesBuffer.UniformSphereSamplesBuffer.SRV; PassParameters->DistantSkyLightLutBufferUAV = DistantSkyLightLutBufferUAV; PassParameters->MobileDistantSkyLightLutBufferUAV = MobileDistantSkyLightLutBufferUAV; FLightSceneInfo* Light0 = Scene->AtmosphereLights[0]; FLightSceneInfo* Light1 = Scene->AtmosphereLights[1]; if (Light0) { PassParameters->AtmosphereLightDirection0 = FVector3f(SkyAtmosphereSceneProxy.GetAtmosphereLightDirection(0, -Light0->Proxy->GetDirection())); PassParameters->AtmosphereLightIlluminanceOuterSpace0 = Light0->Proxy->GetOuterSpaceIlluminance(); } else { PassParameters->AtmosphereLightDirection0 = FVector4f(0.0f, 0.0f, 1.0f, 1.0f); PassParameters->AtmosphereLightIlluminanceOuterSpace0 = FLinearColor::Black; } if (Light1) { PassParameters->AtmosphereLightDirection1 = FVector3f(SkyAtmosphereSceneProxy.GetAtmosphereLightDirection(1, -Light1->Proxy->GetDirection())); PassParameters->AtmosphereLightIlluminanceOuterSpace1 = Light1->Proxy->GetOuterSpaceIlluminance(); } else { PassParameters->AtmosphereLightDirection1 = FVector4f(0.0f, 0.0f, 1.0f, 1.0f); PassParameters->AtmosphereLightIlluminanceOuterSpace1 = FLinearColor::Black; } PassParameters->DistantSkyLightSampleAltitude = CVarSkyAtmosphereDistantSkyLightLUTAltitude.GetValueOnAnyThread(); FIntVector TextureSize = FIntVector(1, 1, 1); const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderDistantSkyLightLutCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("DistantSkyLightLut"), PassFlag, ComputeShader, PassParameters, NumGroups); PendingRDGResources.DistantSkyLightLutBuffer = DistantSkyLightLutBuffer; PendingRDGResources.MobileDistantSkyLightLutBuffer = MobileDistantSkyLightLutBuffer; } SkyAtmosphereLightShadowData LightShadowData; const bool bShouldSampleOpaqueShadow = ShouldSkySampleAtmosphereLightsOpaqueShadow(*Scene, VisibleLightInfos, LightShadowData); FLightSceneProxy* AtmosphereLight0Proxy = Scene->AtmosphereLights[0] ? Scene->AtmosphereLights[0]->Proxy : nullptr; FLightSceneProxy* AtmosphereLight1Proxy = Scene->AtmosphereLights[1] ? Scene->AtmosphereLights[1]->Proxy : nullptr; const float CloudShadowOnAtmosphereStrength0 = AtmosphereLight0Proxy ? AtmosphereLight0Proxy->GetCloudShadowOnAtmosphereStrength() : 0.0f; const float CloudShadowOnAtmosphereStrength1 = AtmosphereLight1Proxy ? AtmosphereLight1Proxy->GetCloudShadowOnAtmosphereStrength() : 0.0f; // SkyViewLUT texture is required if there are any sky dome material that could potentially sample it. // This texture is sampled on skydome mesh with a sky material when rendered into a cubemap real time capture. const bool bRealTimeReflectionCaptureSkyAtmosphereViewLutTexture = Views.Num() > 0 && Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled; // CameraAP volume is required if there is a skydome or a volumetric cloud component rendered in a cubemap real time capture. const bool bRealTimeReflectionCapture360APLutTexture = Views.Num() > 0 && Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled && (Views[0].bSceneHasSkyMaterial || Scene->HasVolumetricCloud()); if (bRealTimeReflectionCaptureSkyAtmosphereViewLutTexture || bRealTimeReflectionCapture360APLutTexture) { FViewInfo& ViewForReflection = Views[0]; const float AerialPerspectiveStartDepthInCm = GetValidAerialPerspectiveStartDepthInCm(ViewForReflection, SkyAtmosphereSceneProxy); // Setup a constant referential for each of the faces of the dynamic reflection capture. const FAtmosphereSetup& AtmosphereSetup = SkyAtmosphereSceneProxy.GetAtmosphereSetup(); const FVector3f SkyViewLutReferentialForward = FVector3f(1.0f, 0.0f, 0.0f); const FVector3f SkyViewLutReferentialRight = FVector3f(0.0f, 1.0f, 0.0f); FVector3f SkyCameraTranslatedWorldOrigin; FMatrix44f SkyViewLutReferential; FVector4f TempSkyPlanetData; AtmosphereSetup.ComputeViewData( Scene->SkyLight->CapturePosition, ViewForReflection.ViewMatrices.GetPreViewTranslation(), SkyViewLutReferentialForward, SkyViewLutReferentialRight, SkyCameraTranslatedWorldOrigin, TempSkyPlanetData, SkyViewLutReferential); // Here, it would be convenient to create a new ViewUB using Scene->SkyLight->CapturePosition, // while setting up correct view parameters such as SkyPlanetTranslatedWorldCenterAndViewHeight, SkyViewLutReferential, etc. // But this is expenssive and can increase the cost of that function by 3 on low end platform. // As an optimisation to avoid creating a new View UB, shaders will fetch from constants what they need. FSkyAtmosphereRealTimeReflectionLUTParameters Parameters; Parameters.SkyViewLutReferential = SkyViewLutReferential; Parameters.SkyPlanetTranslatedWorldCenterAndViewHeight = TempSkyPlanetData; Parameters.SkyCameraTranslatedWorldOrigin = SkyCameraTranslatedWorldOrigin; Parameters.RealTimeReflectionCapture = 0.0f; TUniformBufferRef SkyAtmosphereRealTimeReflectionLUTUB = TUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo(); FCloudShadowAOData CloudShadowAOData; GetCloudShadowAOData(CloudInfo, ViewForReflection, GraphBuilder, CloudShadowAOData); const bool bShouldSampleCloudShadow = CloudShadowAOData.bShouldSampleCloudShadow && (CloudShadowOnAtmosphereStrength0 > 0.0f || CloudShadowOnAtmosphereStrength1 > 0.0f); if (bRealTimeReflectionCaptureSkyAtmosphereViewLutTexture) { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::RealTimeCaptureSkyViewLut"); FIntVector SkyViewLutSize = ViewForReflection.SkyAtmosphereViewLutTexture->GetDesc().GetSize(); FRDGTextureRef RealTimeReflectionCaptureSkyAtmosphereViewLutTexture = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2D(FIntPoint(SkyViewLutSize.X, SkyViewLutSize.Y), GetSkyLutTextureFormat(Scene->GetFeatureLevel()), FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV), TEXT("SkyAtmosphere.RealTimeSkyCapViewLut")); FRDGTextureUAVRef RealTimeReflectionCaptureSkyAtmosphereViewLutTextureUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RealTimeReflectionCaptureSkyAtmosphereViewLutTexture, 0)); FRenderSkyViewLutCS::FPermutationDomain PermutationVector; PermutationVector.Set(CloudShadowAOData.bShouldSampleCloudSkyAO); PermutationVector.Set(bSecondAtmosphereLightEnabled); PermutationVector.Set(false); // bShouldSampleOpaqueShadow); Off for now to not have to generate Light0Shadow and Light1Shadow PermutationVector.Set(bShouldSampleCloudShadow); PermutationVector.Set(true); TShaderMapRef ComputeShader(ViewForReflection.ShaderMap, PermutationVector); FRenderSkyViewLutCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->ViewUniformBuffer = ViewForReflection.ViewUniformBuffer; PassParameters->SkyAtmosphereRealTimeReflectionLUTUniformBuffer = SkyAtmosphereRealTimeReflectionLUTUB; PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture0Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture1Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudSkyAOTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->VolumetricCloudShadowMapTexture0 = CloudShadowAOData.VolumetricCloudShadowMap[0]; PassParameters->VolumetricCloudShadowMapTexture1 = CloudShadowAOData.VolumetricCloudShadowMap[1]; PassParameters->VolumetricCloudSkyAOTexture = CloudShadowAOData.VolumetricCloudSkyAO; PassParameters->VolumetricCloudShadowStrength0 = CloudShadowOnAtmosphereStrength0; PassParameters->VolumetricCloudShadowStrength1 = CloudShadowOnAtmosphereStrength1; PassParameters->SkyViewLutUAV = RealTimeReflectionCaptureSkyAtmosphereViewLutTextureUAV; if (bShouldSampleCloudShadow || CloudShadowAOData.bShouldSampleCloudSkyAO) { PassParameters->VolumetricCloudCommonGlobalParams = CloudInfo->GetVolumetricCloudCommonShaderParametersUB(); } PassParameters->SourceDiskEnabled = 0; FIntVector TextureSize = RealTimeReflectionCaptureSkyAtmosphereViewLutTexture->Desc.GetSize(); TextureSize.Z = 1; const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderSkyViewLutCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("RealTimeCaptureSkyViewLut"), PassFlag, ComputeShader, PassParameters, NumGroups); PendingRDGResources.RealTimeReflectionCaptureSkyAtmosphereViewLutTexture = RealTimeReflectionCaptureSkyAtmosphereViewLutTexture; } if (bRealTimeReflectionCapture360APLutTexture) { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::RealTimeCaptureCamera360VolumeLut"); FIntVector CameraAPLutSize = ViewForReflection.SkyAtmosphereCameraAerialPerspectiveVolume->GetDesc().GetSize(); EPixelFormat TextureAerialLUTFormat = (CVarSkyAtmosphereLUT32.GetValueOnAnyThread() != 0) ? PF_A32B32G32R32F : PF_FloatRGBA; FRDGTextureRef RealTimeReflectionCaptureCamera360APLutTexture = GraphBuilder.CreateTexture( FRDGTextureDesc::Create3D(CameraAPLutSize, TextureAerialLUTFormat, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV), TEXT("SkyAtmosphere.RealTimeSkyCapCamera360APLut")); FRDGTextureUAVRef RealTimeReflectionCaptureSkyAtmosphereViewLutTextureUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RealTimeReflectionCaptureCamera360APLutTexture, 0)); FRenderCameraAerialPerspectiveVolumeCS::FPermutationDomain PermutationVector; PermutationVector.Set(CloudShadowAOData.bShouldSampleCloudSkyAO); PermutationVector.Set(bSecondAtmosphereLightEnabled); PermutationVector.Set(false); // bShouldSampleOpaqueShadow); Off for now to not have to generate Light0Shadow and Light1Shadow PermutationVector.Set(bShouldSampleCloudShadow); PermutationVector.Set(false); // Not for reflections for now PermutationVector.Set(true); TShaderMapRef ComputeShader(ViewForReflection.ShaderMap, PermutationVector); FRenderCameraAerialPerspectiveVolumeCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->ViewUniformBuffer = ViewForReflection.ViewUniformBuffer; PassParameters->SkyAtmosphereRealTimeReflectionLUTUniformBuffer = SkyAtmosphereRealTimeReflectionLUTUB; PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture0Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture1Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudSkyAOTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->VolumetricCloudShadowMapTexture0 = CloudShadowAOData.VolumetricCloudShadowMap[0]; PassParameters->VolumetricCloudShadowMapTexture1 = CloudShadowAOData.VolumetricCloudShadowMap[1]; PassParameters->VolumetricCloudSkyAOTexture = CloudShadowAOData.VolumetricCloudSkyAO; PassParameters->VolumetricCloudShadowStrength0 = CloudShadowOnAtmosphereStrength0; PassParameters->VolumetricCloudShadowStrength1 = CloudShadowOnAtmosphereStrength1; PassParameters->CameraAerialPerspectiveVolumeUAV = RealTimeReflectionCaptureSkyAtmosphereViewLutTextureUAV; PassParameters->AerialPerspectiveStartDepthKm = AerialPerspectiveStartDepthInCm * CM_TO_KM; PassParameters->RealTimeReflection360Mode = 1.0f; //PassParameters->Light0Shadow = nullptr; //PassParameters->Light1Shadow = nullptr; if (bShouldSampleCloudShadow || CloudShadowAOData.bShouldSampleCloudSkyAO) { PassParameters->VolumetricCloudCommonGlobalParams = CloudInfo->GetVolumetricCloudCommonShaderParametersUB(); } FIntVector TextureSize = RealTimeReflectionCaptureCamera360APLutTexture->Desc.GetSize(); const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderCameraAerialPerspectiveVolumeCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("RealTimeCaptureCamera360VolumeLut"), PassFlag, ComputeShader, PassParameters, NumGroups); PendingRDGResources.RealTimeReflectionCaptureCamera360APLutTexture = RealTimeReflectionCaptureCamera360APLutTexture; } } for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; const float AerialPerspectiveStartDepthInCm = GetValidAerialPerspectiveStartDepthInCm(View, SkyAtmosphereSceneProxy); const bool bLightDiskEnabled = !View.bIsReflectionCapture; FRDGTextureRef SkyAtmosphereViewLutTexture = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereViewLutTexture); FRDGTextureUAVRef SkyAtmosphereViewLutTextureUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyAtmosphereViewLutTexture, 0)); FRDGTextureRef SkyAtmosphereCameraAerialPerspectiveVolume = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereCameraAerialPerspectiveVolume); FRDGTextureUAVRef SkyAtmosphereCameraAerialPerspectiveVolumeUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyAtmosphereCameraAerialPerspectiveVolume, 0)); FRDGTextureRef SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly = nullptr; FRDGTextureUAVRef SkyAtmosphereCameraAerialPerspectiveVolumeMieOnlyUAV = nullptr; FRDGTextureRef SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly = nullptr; FRDGTextureUAVRef SkyAtmosphereCameraAerialPerspectiveVolumeRayOnlyUAV = nullptr; if (bSeparatedAtmosphereMieRayLeigh) { SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly); SkyAtmosphereCameraAerialPerspectiveVolumeMieOnlyUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyAtmosphereCameraAerialPerspectiveVolumeMieOnly, 0)); SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly); SkyAtmosphereCameraAerialPerspectiveVolumeRayOnlyUAV = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyAtmosphereCameraAerialPerspectiveVolumeRayOnly, 0)); } TRDGUniformBufferRef LightShadowShaderParams0UniformBuffer{}; TRDGUniformBufferRef LightShadowShaderParams1UniformBuffer{}; GetSkyAtmosphereLightsUniformBuffers(GraphBuilder, LightShadowShaderParams0UniformBuffer, LightShadowShaderParams1UniformBuffer, LightShadowData, View, bShouldSampleOpaqueShadow, UniformBuffer_SingleFrame); FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo(); FCloudShadowAOData CloudShadowAOData; GetCloudShadowAOData(CloudInfo, View, GraphBuilder, CloudShadowAOData); const bool bShouldSampleCloudShadow = CloudShadowAOData.bShouldSampleCloudShadow && (CloudShadowOnAtmosphereStrength0 > 0.0f || CloudShadowOnAtmosphereStrength1 > 0.0f); // Sky View LUT { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::SkyViewLut"); FRenderSkyViewLutCS::FPermutationDomain PermutationVector; PermutationVector.Set(CloudShadowAOData.bShouldSampleCloudSkyAO); PermutationVector.Set(bSecondAtmosphereLightEnabled); PermutationVector.Set(bShouldSampleOpaqueShadow); PermutationVector.Set(bShouldSampleCloudShadow); PermutationVector.Set(false); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FRenderSkyViewLutCS::FParameters * PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->SkyAtmosphereRealTimeReflectionLUTUniformBuffer = GetDefaultSkyAtmosphereRealTimeReflectionLUTUniformBuffer(); PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture0Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture1Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudSkyAOTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->VolumetricCloudShadowMapTexture0 = CloudShadowAOData.VolumetricCloudShadowMap[0]; PassParameters->VolumetricCloudShadowMapTexture1 = CloudShadowAOData.VolumetricCloudShadowMap[1]; PassParameters->VolumetricCloudSkyAOTexture = CloudShadowAOData.VolumetricCloudSkyAO; PassParameters->VolumetricCloudShadowStrength0 = CloudShadowOnAtmosphereStrength0; PassParameters->VolumetricCloudShadowStrength1 = CloudShadowOnAtmosphereStrength1; PassParameters->SkyViewLutUAV = SkyAtmosphereViewLutTextureUAV; PassParameters->Light0Shadow = LightShadowShaderParams0UniformBuffer; PassParameters->Light1Shadow = LightShadowShaderParams1UniformBuffer; PassParameters->VirtualShadowMap = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex); PassParameters->VirtualShadowMapId0 = LightShadowData.VirtualShadowMapId0; PassParameters->VirtualShadowMapId1 = LightShadowData.VirtualShadowMapId1; if (bShouldSampleCloudShadow || CloudShadowAOData.bShouldSampleCloudSkyAO) { PassParameters->VolumetricCloudCommonGlobalParams = CloudInfo->GetVolumetricCloudCommonShaderParametersUB(); } PassParameters->SourceDiskEnabled = bLightDiskEnabled ? 1 : 0; FIntVector TextureSize = SkyAtmosphereViewLutTexture->Desc.GetSize(); TextureSize.Z = 1; const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderSkyViewLutCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("SkyViewLut"), PassFlag, ComputeShader, PassParameters, NumGroups); } // Camera Atmosphere Volume { RDG_EVENT_SCOPE(GraphBuilder, "SkyAtmosphere::CameraVolumeLut"); FRenderCameraAerialPerspectiveVolumeCS::FPermutationDomain PermutationVector; PermutationVector.Set(CloudShadowAOData.bShouldSampleCloudSkyAO); PermutationVector.Set(bSecondAtmosphereLightEnabled); PermutationVector.Set(bShouldSampleOpaqueShadow); PermutationVector.Set(bShouldSampleCloudShadow); PermutationVector.Set(bSeparatedAtmosphereMieRayLeigh); PermutationVector.Set(false); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FRenderCameraAerialPerspectiveVolumeCS::FParameters * PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->SkyAtmosphereRealTimeReflectionLUTUniformBuffer = GetDefaultSkyAtmosphereRealTimeReflectionLUTUniformBuffer(); PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture0Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudShadowMapTexture1Sampler = SamplerLinearClamp; PassParameters->VolumetricCloudSkyAOTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->VolumetricCloudShadowMapTexture0 = CloudShadowAOData.VolumetricCloudShadowMap[0]; PassParameters->VolumetricCloudShadowMapTexture1 = CloudShadowAOData.VolumetricCloudShadowMap[1]; PassParameters->VolumetricCloudSkyAOTexture = CloudShadowAOData.VolumetricCloudSkyAO; PassParameters->VolumetricCloudShadowStrength0 = CloudShadowOnAtmosphereStrength0; PassParameters->VolumetricCloudShadowStrength1 = CloudShadowOnAtmosphereStrength1; PassParameters->CameraAerialPerspectiveVolumeUAV = SkyAtmosphereCameraAerialPerspectiveVolumeUAV; PassParameters->CameraAerialPerspectiveVolumeMieOnlyUAV = SkyAtmosphereCameraAerialPerspectiveVolumeMieOnlyUAV; PassParameters->CameraAerialPerspectiveVolumeRayOnlyUAV = SkyAtmosphereCameraAerialPerspectiveVolumeRayOnlyUAV; PassParameters->AerialPerspectiveStartDepthKm = AerialPerspectiveStartDepthInCm * CM_TO_KM; PassParameters->RealTimeReflection360Mode = 0.0f; PassParameters->Light0Shadow = LightShadowShaderParams0UniformBuffer; PassParameters->Light1Shadow = LightShadowShaderParams1UniformBuffer; PassParameters->VirtualShadowMap = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, ViewIndex); PassParameters->VirtualShadowMapId0 = LightShadowData.VirtualShadowMapId0; PassParameters->VirtualShadowMapId1 = LightShadowData.VirtualShadowMapId1; if (bShouldSampleCloudShadow || CloudShadowAOData.bShouldSampleCloudSkyAO) { PassParameters->VolumetricCloudCommonGlobalParams = CloudInfo->GetVolumetricCloudCommonShaderParametersUB(); } FIntVector TextureSize = SkyAtmosphereCameraAerialPerspectiveVolume->Desc.GetSize(); const FIntVector NumGroups = FIntVector::DivideAndRoundUp(TextureSize, FRenderCameraAerialPerspectiveVolumeCS::GroupSize); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("CameraVolumeLut"), PassFlag, ComputeShader, PassParameters, NumGroups); } PendingRDGResources.ViewResources[ViewIndex].SkyAtmosphereViewLutTexture = SkyAtmosphereViewLutTexture; PendingRDGResources.ViewResources[ViewIndex].SkyAtmosphereCameraAerialPerspectiveVolume = SkyAtmosphereCameraAerialPerspectiveVolume; } PendingRDGResources.TransmittanceLut = TransmittanceLut; } void FSkyAtmospherePendingRDGResources::CommitToSceneAndViewUniformBuffers(FRDGBuilder& GraphBuilder, FRDGExternalAccessQueue& ExternalAccessQueue) const { check(SceneRenderer); FScene* Scene = SceneRenderer->Scene; FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); if (DistantSkyLightLutBuffer && CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { SkyInfo.GetDistantSkyLightLutBuffer() = ConvertToExternalAccessBuffer(GraphBuilder, ExternalAccessQueue, DistantSkyLightLutBuffer, ERHIAccess::SRVMask, ERHIPipeline::All); } if (MobileDistantSkyLightLutBuffer && CVarSkyAtmosphereDistantSkyLightLUT.GetValueOnRenderThread() > 0) { SkyInfo.GetMobileDistantSkyLightLutBuffer() = ConvertToExternalAccessBuffer(GraphBuilder, ExternalAccessQueue, MobileDistantSkyLightLutBuffer, ERHIAccess::SRVMask, ERHIPipeline::All); } if (RealTimeReflectionCaptureSkyAtmosphereViewLutTexture) { Scene->RealTimeReflectionCaptureSkyAtmosphereViewLutTexture = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, RealTimeReflectionCaptureSkyAtmosphereViewLutTexture, ERHIAccess::SRVMask, ERHIPipeline::All); } else { Scene->RealTimeReflectionCaptureSkyAtmosphereViewLutTexture = nullptr; } if (RealTimeReflectionCaptureCamera360APLutTexture) { Scene->RealTimeReflectionCaptureCamera360APLutTexture = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, RealTimeReflectionCaptureCamera360APLutTexture, ERHIAccess::SRVMask, ERHIPipeline::All); } else { Scene->RealTimeReflectionCaptureCamera360APLutTexture = nullptr; } for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ViewIndex++) { FViewInfo& View = SceneRenderer->Views[ViewIndex]; View.SkyAtmosphereViewLutTexture = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, ViewResources[ViewIndex].SkyAtmosphereViewLutTexture, ERHIAccess::SRVMask, ERHIPipeline::All); View.SkyAtmosphereCameraAerialPerspectiveVolume = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, ViewResources[ViewIndex].SkyAtmosphereCameraAerialPerspectiveVolume, ERHIAccess::SRVMask, ERHIPipeline::All); } check(TransmittanceLut); SkyInfo.GetTransmittanceLutTexture() = ConvertToExternalAccessTexture(GraphBuilder, ExternalAccessQueue, TransmittanceLut, ERHIAccess::SRVMask, ERHIPipeline::All); } FSkyAtmosphereRenderContext::FSkyAtmosphereRenderContext() { bAPOnCloudMode = false; VolumetricCloudDepthTexture = nullptr; InputCloudLuminanceTransmittanceTexture = nullptr; MSAASampleCount = 1; MSAADepthTexture = nullptr; } void FSceneRenderer::RenderSkyAtmosphereInternal( FRDGBuilder& GraphBuilder, const FSceneTextureShaderParameters& SceneTextures, FSkyAtmosphereRenderContext& SkyRC) { check(Scene->HasSkyAtmosphere()); FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy(); const FAtmosphereSetup& Atmosphere = SkyAtmosphereSceneProxy.GetAtmosphereSetup(); const FViewMatrices& ViewMatrices = *SkyRC.ViewMatrices; FRHISamplerState* SamplerLinearClamp = TStaticSamplerState::GetRHI(); const float AerialPerspectiveStartDepthInCm = SkyRC.AerialPerspectiveStartDepthInCm; const FVector3f ViewOrigin = (FVector3f)ViewMatrices.GetViewOrigin(); const FVector3f PlanetCenter = (FVector3f)Atmosphere.PlanetCenterKm * KM_TO_CM; // LWC_TODO: Precision Loss const float TopOfAtmosphere = Atmosphere.TopRadiusKm * KM_TO_CM; const float PLANET_RADIUS_RATIO_SAFE_EDGE = 1.00000155763f; // must match PLANET_RADIUS_SAFE_TRACE_EDGE const bool ForceRayMarching = SkyRC.bForceRayMarching || (FVector3f::Distance(ViewOrigin, PlanetCenter) >= (TopOfAtmosphere * PLANET_RADIUS_RATIO_SAFE_EDGE)); const bool bDisableBlending = SkyRC.bDisableBlending; // We only support MSAA up to 8 sample and in forward check(SkyRC.MSAASampleCount <= 8); // We only support MSAA in forward, not in deferred. const bool bForwardShading = IsForwardShadingEnabled(Scene->GetShaderPlatform()); check(bForwardShading || (!bForwardShading && SkyRC.MSAASampleCount == 1)); // Render the sky, and optionally the atmosphere aerial perspective, on the scene luminance buffer { FLightSceneProxy* AtmosphereLight0Proxy = Scene->AtmosphereLights[0] ? Scene->AtmosphereLights[0]->Proxy : nullptr; FLightSceneProxy* AtmosphereLight1Proxy = Scene->AtmosphereLights[1] ? Scene->AtmosphereLights[1]->Proxy : nullptr; const float CloudShadowOnAtmosphereStrength0 = AtmosphereLight0Proxy ? AtmosphereLight0Proxy->GetCloudShadowOnAtmosphereStrength() : 0.0f; const float CloudShadowOnAtmosphereStrength1 = AtmosphereLight1Proxy ? AtmosphereLight1Proxy->GetCloudShadowOnAtmosphereStrength() : 0.0f; const bool bShouldSampleCloudShadow = SkyRC.bShouldSampleCloudShadow && (CloudShadowOnAtmosphereStrength0 > 0.0f || CloudShadowOnAtmosphereStrength1 > 0.0f); const bool bFastAerialPerspectiveDepthTest = SkyRC.bFastAerialPerspectiveDepthTest; const bool SkyAtmosphereOutputsAlpha = IsPostProcessingWithAlphaChannelSupported(); const bool SkyAtmosphereAlphaHoldOut = SkyAtmosphereOutputsAlpha && SkyAtmosphereSceneProxy.IsHoldout(); const bool bRenderSkyPixel = SkyRC.bRenderSkyPixel || (SkyAtmosphereOutputsAlpha && !SkyRC.bSceneHasSkyMaterial); // In this case we need to write alpha holdout values in the sky pixels. If there is no IsSky dmoe meshes. FRenderSkyAtmospherePS::FPermutationDomain PsPermutationVector; PsPermutationVector.Set(SkyRC.bShouldSampleCloudSkyAO); PsPermutationVector.Set(SkyRC.bFastSky && !ForceRayMarching); PsPermutationVector.Set(SkyRC.bFastAerialPerspective && !ForceRayMarching); PsPermutationVector.Set(SkyRC.bSecondAtmosphereLightEnabled); PsPermutationVector.Set(bRenderSkyPixel); PsPermutationVector.Set(SkyRC.bShouldSampleOpaqueShadow); PsPermutationVector.Set(bShouldSampleCloudShadow); PsPermutationVector.Set(SkyRC.bAPOnCloudMode); PsPermutationVector.Set(SkyRC.MSAASampleCount); PsPermutationVector = FRenderSkyAtmospherePS::RemapPermutation(PsPermutationVector); TShaderMapRef PixelShader(GetGlobalShaderMap(SkyRC.FeatureLevel), PsPermutationVector); FRenderSkyAtmosphereVS::FPermutationDomain VsPermutationVector; TShaderMapRef VertexShader(GetGlobalShaderMap(SkyRC.FeatureLevel), VsPermutationVector); FRenderSkyAtmospherePS::FParameters* PsPassParameters = GraphBuilder.AllocParameters(); PsPassParameters->Atmosphere = Scene->GetSkyAtmosphereSceneInfo()->GetAtmosphereUniformBuffer(); PsPassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PsPassParameters->ViewUniformBuffer = SkyRC.ViewUniformBuffer; PsPassParameters->Scene = SkyRC.SceneUniformBuffer; PsPassParameters->RenderTargets = SkyRC.RenderTargets; PsPassParameters->SceneTextures = SceneTextures; PsPassParameters->MSAADepthTexture = SkyRC.MSAADepthTexture; PsPassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PsPassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PsPassParameters->SkyViewLutTextureSampler = SamplerLinearClamp; PsPassParameters->CameraAerialPerspectiveVolumeTextureSampler = SamplerLinearClamp; PsPassParameters->VolumetricCloudShadowMapTexture0Sampler = SamplerLinearClamp; PsPassParameters->VolumetricCloudShadowMapTexture1Sampler = SamplerLinearClamp; PsPassParameters->VolumetricCloudSkyAOTextureSampler = SamplerLinearClamp; PsPassParameters->TransmittanceLutTexture = SkyRC.TransmittanceLut; PsPassParameters->MultiScatteredLuminanceLutTexture = SkyRC.MultiScatteredLuminanceLut; PsPassParameters->SkyViewLutTexture = SkyRC.SkyAtmosphereViewLutTexture; PsPassParameters->CameraAerialPerspectiveVolumeTexture = SkyRC.SkyAtmosphereCameraAerialPerspectiveVolume; PsPassParameters->VolumetricCloudShadowMapTexture0 = SkyRC.VolumetricCloudShadowMap[0]; PsPassParameters->VolumetricCloudShadowMapTexture1 = SkyRC.VolumetricCloudShadowMap[1]; PsPassParameters->VolumetricCloudSkyAOTexture = SkyRC.VolumetricCloudSkyAO; PsPassParameters->VolumetricCloudShadowStrength0 = CloudShadowOnAtmosphereStrength0; PsPassParameters->VolumetricCloudShadowStrength1 = CloudShadowOnAtmosphereStrength1; PsPassParameters->VolumetricCloudDepthTexture = SkyRC.VolumetricCloudDepthTexture; PsPassParameters->InputCloudLuminanceTransmittanceTexture = SkyRC.InputCloudLuminanceTransmittanceTexture; PsPassParameters->AerialPerspectiveStartDepthKm = AerialPerspectiveStartDepthInCm * CM_TO_KM; PsPassParameters->bPropagateAlphaNonReflection = (IsPostProcessingWithAlphaChannelSupported() && !SkyRC.bIsReflectionCapture) ? 1 : 0; PsPassParameters->SourceDiskEnabled = SkyRC.bIsReflectionCapture ? 0 : 1; PsPassParameters->DepthReadDisabled = SkyRC.bDepthReadDisabled ? 1 : 0; if (bShouldSampleCloudShadow || SkyRC.bShouldSampleCloudSkyAO) { PsPassParameters->VolumetricCloudCommonGlobalParams = Scene->GetVolumetricCloudSceneInfo()->GetVolumetricCloudCommonShaderParametersUB(); } PsPassParameters->Light0Shadow = SkyRC.LightShadowShaderParams0UniformBuffer; PsPassParameters->Light1Shadow = SkyRC.LightShadowShaderParams1UniformBuffer; PsPassParameters->VirtualShadowMap = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder, 0); PsPassParameters->VirtualShadowMapId0 = SkyRC.VirtualShadowMapId0; PsPassParameters->VirtualShadowMapId1 = SkyRC.VirtualShadowMapId1; ClearUnusedGraphResources(PixelShader, PsPassParameters); float StartDepthZ = 0.1f; if (SkyRC.bFastAerialPerspectiveDepthTest) { const FMatrix ProjectionMatrix = ViewMatrices.GetProjectionMatrix(); float StartDepthViewCm; if (ViewMatrices.IsPerspectiveProjection()) { float HalfHorizontalFOV = FMath::Atan(1.0f / ProjectionMatrix.M[0][0]); float HalfVerticalFOV = FMath::Atan(1.0f / ProjectionMatrix.M[1][1]); StartDepthViewCm = FMath::Cos(FMath::Max(HalfHorizontalFOV, HalfVerticalFOV)) * AerialPerspectiveStartDepthInCm; } else { StartDepthViewCm = AerialPerspectiveStartDepthInCm; } StartDepthViewCm = FMath::Max(StartDepthViewCm, SkyRC.NearClippingDistance); // In any case, we need to limit the distance to frustum near plane to not be clipped away. const FVector4 Projected = ProjectionMatrix.TransformFVector4(FVector4(0.0f, 0.0f, StartDepthViewCm, 1.0f)); StartDepthZ = float(Projected.Z / Projected.W); // LWC_TODO: precision loss } FIntRect Viewport = SkyRC.Viewport; GraphBuilder.AddPass( {}, PsPassParameters, ERDGPassFlags::Raster, [PsPassParameters, VertexShader, PixelShader, Viewport, bFastAerialPerspectiveDepthTest, bRenderSkyPixel, bDisableBlending, StartDepthZ, SkyAtmosphereOutputsAlpha, SkyAtmosphereAlphaHoldOut](FRDGAsyncTask, FRHICommandList& RHICmdListLambda) { RHICmdListLambda.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdListLambda.ApplyCachedRenderTargets(GraphicsPSOInit); if (bDisableBlending) { GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); } else { if (SkyAtmosphereOutputsAlpha) { if (SkyAtmosphereAlphaHoldOut) { // We might need to write alpha holdout data. Since this is the sky writing to GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } else { // Maintain alpha untouched GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } else { // Disable alpha writes GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); } } if (bFastAerialPerspectiveDepthTest) { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } else { GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); } GraphicsPSOInit.RasterizerState = TStaticRasterizerState::GetRHI(); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PT_TriangleList; if (!bRenderSkyPixel && GSupportsDepthBoundsTest) { // We do not want to process sky pixels so we take advantage of depth bound test when available to skip launching pointless GPU wavefront/work. GraphicsPSOInit.bDepthBounds = true; FDepthBounds::FDepthBoundsValues Values = FDepthBounds::CalculateNearFarDepthExcludingSky(); RHICmdListLambda.SetDepthBounds(Values.MinDepth, Values.MaxDepth); } SetGraphicsPipelineState(RHICmdListLambda, GraphicsPSOInit, 0); SetShaderParameters(RHICmdListLambda, PixelShader, PixelShader.GetPixelShader(), *PsPassParameters); FRenderSkyAtmosphereVS::FParameters VsPassParameters; VsPassParameters.StartDepthZ = StartDepthZ; SetShaderParameters(RHICmdListLambda, VertexShader, VertexShader.GetVertexShader(), VsPassParameters); RHICmdListLambda.DrawPrimitive(0, 1, 1); }); } } void FSceneRenderer::RenderSkyAtmosphere(FRDGBuilder& GraphBuilder, const FMinimalSceneTextures& SceneTextures) { check(!IsMobilePlatform(Scene->GetShaderPlatform())); check(ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)); // This should not be called if we should not render SkyAtmosphere RDG_EVENT_SCOPE_STAT(GraphBuilder, SkyAtmosphere, "SkyAtmosphere"); RDG_GPU_STAT_SCOPE(GraphBuilder, SkyAtmosphere); SCOPED_NAMED_EVENT(SkyAtmosphere, FColor::Emerald); FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy(); FVolumetricCloudRenderSceneInfo* CloudInfo = Scene->GetVolumetricCloudSceneInfo(); FSkyAtmosphereRenderContext SkyRC; SkyRC.ViewMatrices = nullptr; SkyRC.SceneUniformBuffer = GetSceneUniforms().GetBuffer(GraphBuilder); const FAtmosphereSetup& Atmosphere = SkyAtmosphereSceneProxy.GetAtmosphereSetup(); SkyRC.bFastSky = CVarSkyAtmosphereFastSkyLUT.GetValueOnRenderThread() > 0; SkyRC.bFastAerialPerspective = CVarSkyAtmosphereAerialPerspectiveApplyOnOpaque.GetValueOnRenderThread() > 0; SkyRC.bFastAerialPerspectiveDepthTest = CVarSkyAtmosphereAerialPerspectiveDepthTest.GetValueOnRenderThread() > 0; SkyRC.bSecondAtmosphereLightEnabled = Scene->IsSecondAtmosphereLightEnabled(); SkyAtmosphereLightShadowData LightShadowData; SkyRC.bShouldSampleOpaqueShadow = ShouldSkySampleAtmosphereLightsOpaqueShadow(*Scene, VisibleLightInfos, LightShadowData); SkyRC.bUseDepthBoundTestIfPossible = true; SkyRC.bForceRayMarching = false; SkyRC.bDepthReadDisabled = false; SkyRC.bDisableBlending = false; SkyRC.TransmittanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetTransmittanceLutTexture()); SkyRC.MultiScatteredLuminanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetMultiScatteredLuminanceLutTexture()); SkyRC.RenderTargets[0] = FRenderTargetBinding(SceneTextures.Color.Target, ERenderTargetLoadAction::ELoad); SkyRC.RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilRead); SkyRC.MSAASampleCount = SceneTextures.Depth.Target->Desc.NumSamples; SkyRC.MSAADepthTexture = SceneTextures.Depth.Target; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); FViewInfo& View = Views[ViewIndex]; SkyRC.ViewMatrices = &View.ViewMatrices; SkyRC.ViewUniformBuffer = View.ViewUniformBuffer; SkyRC.bSceneHasSkyMaterial = View.bSceneHasSkyMaterial; SkyRC.Viewport = View.ViewRect; SkyRC.bIsReflectionCapture = View.bIsReflectionCapture; SkyRC.AerialPerspectiveStartDepthInCm = GetValidAerialPerspectiveStartDepthInCm(View, SkyAtmosphereSceneProxy); SkyRC.NearClippingDistance = View.NearClippingDistance; SkyRC.FeatureLevel = View.FeatureLevel; // If the scene contains Sky material then it is likely rendering the sky using a sky dome mesh. // In this case we can use a simpler shader during this pass to only render aerial perspective // and sky pixels can likely be optimised out. SkyRC.bRenderSkyPixel = !View.bSceneHasSkyMaterial; SkyRC.SkyAtmosphereViewLutTexture = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereViewLutTexture); SkyRC.SkyAtmosphereCameraAerialPerspectiveVolume = GraphBuilder.RegisterExternalTexture(View.SkyAtmosphereCameraAerialPerspectiveVolume); GetSkyAtmosphereLightsUniformBuffers(GraphBuilder, SkyRC.LightShadowShaderParams0UniformBuffer, SkyRC.LightShadowShaderParams1UniformBuffer, LightShadowData, View, SkyRC.bShouldSampleOpaqueShadow, UniformBuffer_SingleDraw); SkyRC.VirtualShadowMapId0 = LightShadowData.VirtualShadowMapId0; SkyRC.VirtualShadowMapId1 = LightShadowData.VirtualShadowMapId1; FCloudShadowAOData CloudShadowAOData; GetCloudShadowAOData(CloudInfo, View, GraphBuilder, CloudShadowAOData); SkyRC.bShouldSampleCloudShadow = CloudShadowAOData.bShouldSampleCloudShadow; SkyRC.VolumetricCloudShadowMap[0] = CloudShadowAOData.VolumetricCloudShadowMap[0]; SkyRC.VolumetricCloudShadowMap[1] = CloudShadowAOData.VolumetricCloudShadowMap[1]; SkyRC.bShouldSampleCloudSkyAO = CloudShadowAOData.bShouldSampleCloudSkyAO; SkyRC.VolumetricCloudSkyAO = CloudShadowAOData.VolumetricCloudSkyAO; RenderSkyAtmosphereInternal(GraphBuilder, GetSceneTextureShaderParameters(SceneTextures.UniformBuffer), SkyRC); } #if WITH_EDITOR if (CVarSkyAtmosphereFastSkyLUT.GetValueOnAnyThread() == 0 && CVarSkyAtmosphereAerialPerspectiveApplyOnOpaque.GetValueOnAnyThread() > 0) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; AddDrawCanvasPass(GraphBuilder, {}, View, FScreenPassRenderTarget(SceneTextures.Color.Target, View.ViewRect, ERenderTargetLoadAction::ELoad), [&View](FCanvas& Canvas) { const float ViewPortWidth = float(View.ViewRect.Width()); const float ViewPortHeight = float(View.ViewRect.Height()); FLinearColor TextColor(1.0f, 0.5f, 0.0f); FString Text = TEXT("You are using a FastAerialPespective without FastSky, visuals might look wrong."); Canvas.DrawShadowedString(ViewPortWidth * 0.5f - Text.Len() * 7.0f, ViewPortHeight * 0.4f, *Text, GetStatsFont(), TextColor); }); } } #endif } bool FSceneRenderer::ShouldRenderSkyAtmosphereEditorNotifications(TArrayView InViews) { #if WITH_EDITOR if (CVarSkyAtmosphereEditorNotifications.GetValueOnAnyThread() > 0) { bool bAnyViewHasSkyMaterial = false; for (int32 ViewIndex = 0; ViewIndex < InViews.Num(); ViewIndex++) { const FViewInfo& View = InViews[ViewIndex]; bAnyViewHasSkyMaterial |= (View.bSceneHasSkyMaterial && View.Family->EngineShowFlags.Atmosphere); } return bAnyViewHasSkyMaterial; } #endif return false; } void FSceneRenderer::RenderSkyAtmosphereEditorNotifications(FRDGBuilder& GraphBuilder, TArrayView InViews, FRDGTextureRef SceneColorTexture) const { #if WITH_EDITOR RDG_EVENT_SCOPE_STAT(GraphBuilder, SkyAtmosphereEditor, "SkyAtmosphereEditor"); RDG_GPU_STAT_SCOPE(GraphBuilder, SkyAtmosphereEditor); for (int32 ViewIndex = 0; ViewIndex < InViews.Num(); ViewIndex++) { const FViewInfo& View = InViews[ViewIndex]; if (View.bSceneHasSkyMaterial) { RenderSkyAtmosphereEditorHudPS::FPermutationDomain PermutationVector; TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); RenderSkyAtmosphereEditorHudPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneColorTexture, ERenderTargetLoadAction::ELoad); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->MiniFontTexture = GSystemTextures.AsciiTexture->GetRHI(); FPixelShaderUtils::AddFullscreenPass(GraphBuilder, View.ShaderMap, RDG_EVENT_NAME("SkyAtmosphereEditor"), PixelShader, PassParameters, View.ViewRect); } } #endif } bool ShouldRenderSkyAtmosphereDebugPasses(const FScene* Scene, const FEngineShowFlags& EngineShowFlags) { return EngineShowFlags.VisualizeSkyAtmosphere && ShouldRenderSkyAtmosphere(Scene, EngineShowFlags); } FScreenPassTexture AddSkyAtmosphereDebugPasses(FRDGBuilder& GraphBuilder, FScene* Scene, const FSceneViewFamily& ViewFamily, const FViewInfo& View, FScreenPassTexture& ScreenPassSceneColor) { #if WITH_EDITOR check(ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)); // This should not be called if we should not render SkyAtmosphere RDG_EVENT_SCOPE_STAT(GraphBuilder, SkyAtmosphereDebugVisualize, "SkyAtmosphereDebugVisualize"); RDG_GPU_STAT_SCOPE(GraphBuilder, SkyAtmosphereDebugVisualize); const bool bSkyAtmosphereVisualizeShowFlag = ViewFamily.EngineShowFlags.VisualizeSkyAtmosphere; FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo(); const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy(); const FAtmosphereSetup& Atmosphere = SkyAtmosphereSceneProxy.GetAtmosphereSetup(); if (bSkyAtmosphereVisualizeShowFlag) { FRDGTextureRef TransmittanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetTransmittanceLutTexture()); FRDGTextureRef MultiScatteredLuminanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetMultiScatteredLuminanceLutTexture()); FRHIBlendState* PreMultipliedColorTransmittanceBlend = TStaticBlendState::GetRHI(); FRHIDepthStencilState* DepthStencilStateWrite = TStaticDepthStencilState::GetRHI(); FRHISamplerState* SamplerLinearClamp = TStaticSamplerState::GetRHI(); // Render the sky and atmosphere on the scene luminance buffer { FRenderDebugSkyAtmospherePS::FPermutationDomain PermutationVector; TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); FRenderDebugSkyAtmospherePS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Atmosphere = SkyInfo.GetAtmosphereUniformBuffer(); PassParameters->SkyAtmosphere = SkyInfo.GetInternalCommonParametersUniformBuffer(); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->RenderTargets[0] = FRenderTargetBinding(ScreenPassSceneColor.Texture, ERenderTargetLoadAction::ELoad); PassParameters->TransmittanceLutTextureSampler = SamplerLinearClamp; PassParameters->MultiScatteredLuminanceLutTextureSampler = SamplerLinearClamp; PassParameters->TransmittanceLutTexture = TransmittanceLut; PassParameters->MultiScatteredLuminanceLutTexture = MultiScatteredLuminanceLut; PassParameters->ViewPortWidth = float(View.ViewRect.Width()); PassParameters->ViewPortHeight = float(View.ViewRect.Height()); FPixelShaderUtils::AddFullscreenPass(GraphBuilder, View.ShaderMap, RDG_EVENT_NAME("SkyAtmosphere"), PixelShader, PassParameters, View.ViewRect, PreMultipliedColorTransmittanceBlend, nullptr, DepthStencilStateWrite); } } // Now debug print AddDrawCanvasPass(GraphBuilder, {}, View, FScreenPassRenderTarget(ScreenPassSceneColor, ERenderTargetLoadAction::ELoad), [bSkyAtmosphereVisualizeShowFlag, &View, &Atmosphere](FCanvas& Canvas) { FLinearColor TextColor(FLinearColor::White); FLinearColor GrayTextColor(FLinearColor::Gray); FLinearColor WarningColor(1.0f, 0.5f, 0.0f); FString Text; const float ViewPortWidth = float(View.ViewRect.Width()); const float ViewPortHeight = float(View.ViewRect.Height()); if (bSkyAtmosphereVisualizeShowFlag) { const float ViewPlanetAltitude = (View.ViewLocation * FAtmosphereSetup::CmToSkyUnit - (FVector)Atmosphere.PlanetCenterKm).Size() - Atmosphere.BottomRadiusKm; const bool bViewUnderGroundLevel = ViewPlanetAltitude < 0.0f; if (bViewUnderGroundLevel) { Text = FString::Printf(TEXT("SkyAtmosphere: View is %.3f km under the planet ground level!"), -ViewPlanetAltitude); Canvas.DrawShadowedString(ViewPortWidth * 0.5 - 250.0f, ViewPortHeight * 0.5f, *Text, GetStatsFont(), WarningColor); } // This needs to stay in sync with RenderSkyAtmosphereDebugPS. const float DensityViewTop = ViewPortHeight * 0.1f; const float DensityViewBottom = ViewPortHeight * 0.8f; const float DensityViewLeft = ViewPortWidth * 0.8f; const float Margin = 2.0f; const float TimeOfDayViewHeight = 64.0f; const float TimeOfDayViewTop = ViewPortHeight - (TimeOfDayViewHeight + Margin * 2.0); const float HemiViewHeight = ViewPortWidth * 0.25f; const float HemiViewTop = ViewPortHeight - HemiViewHeight - TimeOfDayViewHeight - Margin * 2.0; Text = FString::Printf(TEXT("Atmosphere top = %.1f km"), Atmosphere.TopRadiusKm - Atmosphere.BottomRadiusKm); Canvas.DrawShadowedString(DensityViewLeft, DensityViewTop, *Text, GetStatsFont(), TextColor); Text = FString::Printf(TEXT("Rayleigh extinction")); Canvas.DrawShadowedString(DensityViewLeft + 60.0f, DensityViewTop + 30.0f, *Text, GetStatsFont(), FLinearColor(FLinearColor::Red)); Text = FString::Printf(TEXT("Mie extinction")); Canvas.DrawShadowedString(DensityViewLeft + 60.0f, DensityViewTop + 45.0f, *Text, GetStatsFont(), FLinearColor(FLinearColor::Green)); Text = FString::Printf(TEXT("Absorption")); Canvas.DrawShadowedString(DensityViewLeft + 60.0f, DensityViewTop + 60.0f, *Text, GetStatsFont(), FLinearColor(FLinearColor::Blue)); Text = FString::Printf(TEXT("<=== Low visual contribution")); Canvas.DrawShadowedString(DensityViewLeft + 2.0, DensityViewTop + 150.0f, *Text, GetStatsFont(), GrayTextColor); Text = FString::Printf(TEXT("High visual contribution ===>")); Canvas.DrawShadowedString(ViewPortWidth - 170.0f, DensityViewTop + 166.0f, *Text, GetStatsFont(), GrayTextColor); Text = FString::Printf(TEXT("Ground level")); Canvas.DrawShadowedString(DensityViewLeft, DensityViewBottom, *Text, GetStatsFont(), TextColor); Text = FString::Printf(TEXT("Time-of-day preview")); Canvas.DrawShadowedString(ViewPortWidth * 0.5f - 80.0f, TimeOfDayViewTop, *Text, GetStatsFont(), TextColor); Text = FString::Printf(TEXT("Hemisphere view")); Canvas.DrawShadowedString(Margin, HemiViewTop, *Text, GetStatsFont(), TextColor); } }); #endif return MoveTemp(ScreenPassSceneColor); }