// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= LightMapDensityRendering.cpp: Implementation for rendering lightmap density. =============================================================================*/ #include "LightMapDensityRendering.h" #include "DeferredShadingRenderer.h" #include "LightMap.h" #include "LightMapRendering.h" #include "Materials/Material.h" #include "ScenePrivate.h" #include "TextureResource.h" #include "MeshPassProcessor.inl" #if !UE_BUILD_DOCS // Typedef is necessary because the C preprocessor thinks the comma in the template parameter list is a comma in the macro parameter list. #define IMPLEMENT_DENSITY_VERTEXSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ typedef TLightMapDensityVS< LightMapPolicyType > TLightMapDensityVS##LightMapPolicyName; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TLightMapDensityVS##LightMapPolicyName,TEXT("/Engine/Private/LightMapDensityShader.usf"),TEXT("MainVertexShader"),SF_Vertex); #define IMPLEMENT_DENSITY_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ typedef TLightMapDensityPS< LightMapPolicyType > TLightMapDensityPS##LightMapPolicyName; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TLightMapDensityPS##LightMapPolicyName,TEXT("/Engine/Private/LightMapDensityShader.usf"),TEXT("MainPixelShader"),SF_Pixel); // Implement a pixel shader type for skylights and one without, and one vertex shader that will be shared between them #define IMPLEMENT_DENSITY_LIGHTMAPPED_SHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ IMPLEMENT_DENSITY_VERTEXSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \ IMPLEMENT_DENSITY_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName); IMPLEMENT_DENSITY_LIGHTMAPPED_SHADER_TYPE(TUniformLightMapPolicy, FNoLightMapPolicy); IMPLEMENT_DENSITY_LIGHTMAPPED_SHADER_TYPE(TUniformLightMapPolicy, FDummyLightMapPolicy); IMPLEMENT_DENSITY_LIGHTMAPPED_SHADER_TYPE(TUniformLightMapPolicy, TLightMapPolicyLQ); IMPLEMENT_DENSITY_LIGHTMAPPED_SHADER_TYPE(TUniformLightMapPolicy, TLightMapPolicyHQ); #endif BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FLightmapDensityPassUniformParameters, ) SHADER_PARAMETER_STRUCT(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER(FVector4f, LightMapDensity) SHADER_PARAMETER(FVector4f, DensitySelectedColor) // The color to apply to selected objects. SHADER_PARAMETER(FVector4f, VertexMappedColor) // The color to apply to vertex mapped objects. SHADER_PARAMETER_TEXTURE(Texture2D, GridTexture) // The "Grid" texture to visualize resolution. SHADER_PARAMETER_SAMPLER(SamplerState, GridTextureSampler) END_GLOBAL_SHADER_PARAMETER_STRUCT() IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FLightmapDensityPassUniformParameters, "LightmapDensityPass", SceneTextures); void SetupLightmapDensityPassUniformBuffer(FRDGBuilder& GraphBuilder, const FSceneTextures* SceneTextures, ERHIFeatureLevel::Type FeatureLevel, FLightmapDensityPassUniformParameters& LightmapDensityPassParameters) { SetupSceneTextureUniformParameters(GraphBuilder, SceneTextures, FeatureLevel, ESceneTextureSetupMode::None, LightmapDensityPassParameters.SceneTextures); LightmapDensityPassParameters.GridTexture = GEngine->LightMapDensityTexture->GetResource()->TextureRHI; LightmapDensityPassParameters.GridTextureSampler = TStaticSamplerState::GetRHI(); LightmapDensityPassParameters.LightMapDensity = FVector4f( 1.0f, GEngine->MinLightMapDensity * GEngine->MinLightMapDensity, GEngine->IdealLightMapDensity * GEngine->IdealLightMapDensity, GEngine->MaxLightMapDensity * GEngine->MaxLightMapDensity); LightmapDensityPassParameters.DensitySelectedColor = GEngine->LightMapDensitySelectedColor; LightmapDensityPassParameters.VertexMappedColor = GEngine->LightMapDensityVertexMappedColor; } TRDGUniformBufferRef CreateLightmapDensityPassUniformBuffer(FRDGBuilder& GraphBuilder, const FSceneTextures* SceneTextures, ERHIFeatureLevel::Type FeatureLevel) { auto* UniformBufferParameters = GraphBuilder.AllocParameters(); SetupLightmapDensityPassUniformBuffer(GraphBuilder, SceneTextures, FeatureLevel, *UniformBufferParameters); return GraphBuilder.CreateUniformBuffer(UniformBufferParameters); } BEGIN_SHADER_PARAMETER_STRUCT(FLightMapDensitiesPassParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FLightmapDensityPassUniformParameters, Pass) SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() void RenderLightMapDensities( FRDGBuilder& GraphBuilder, TArrayView Views, const FRenderTargetBindingSlots& RenderTargets) { RDG_EVENT_SCOPE(GraphBuilder, "LightMapDensity"); // Draw the scene's emissive and light-map color. for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = const_cast(Views[ViewIndex]); if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::LightmapDensity]) { RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask); RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex); View.BeginRenderView(); auto* PassParameters = GraphBuilder.AllocParameters(); PassParameters->View = View.GetShaderParameters(); PassParameters->Pass = CreateLightmapDensityPassUniformBuffer(GraphBuilder, View.GetSceneTexturesChecked(), View.GetFeatureLevel()); PassParameters->RenderTargets = RenderTargets; FScene* Scene = View.Family->Scene->GetRenderScene(); check(Scene != nullptr); Pass->BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams); GraphBuilder.AddPass( {}, PassParameters, ERDGPassFlags::Raster, [&View, Pass, PassParameters](FRDGAsyncTask, FRHICommandList& RHICmdList) { RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1); Pass->Draw(RHICmdList, &PassParameters->InstanceCullingDrawParams); }); } } } template bool FLightmapDensityMeshProcessor::Process( const FMeshBatch& MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, const FMaterial& RESTRICT MaterialResource, const LightMapPolicyType& RESTRICT LightMapPolicy, const typename LightMapPolicyType::ElementDataType& RESTRICT LightMapElementData, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode) { const FVertexFactory* VertexFactory = MeshBatch.VertexFactory; FVertexFactoryType* VertexFactoryType = VertexFactory->GetType(); FMaterialShaderTypes ShaderTypes; ShaderTypes.AddShaderType>(); ShaderTypes.AddShaderType>(); FMaterialShaders Shaders; if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactoryType, Shaders)) { return false; } TMeshProcessorShaders< TLightMapDensityVS, TLightMapDensityPS> LightmapDensityPassShaders; Shaders.TryGetVertexShader(LightmapDensityPassShaders.VertexShader); Shaders.TryGetPixelShader(LightmapDensityPassShaders.PixelShader); TLightMapDensityElementData ShaderElementData(LightMapElementData); ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, true); { // BuiltLightingAndSelectedFlags informs the shader is lighting is built or not for this primitive ShaderElementData.BuiltLightingAndSelectedFlags = FVector3f(0.0f, 0.0f, 0.0f); // LightMapResolutionScale is the physical resolution of the lightmap texture ShaderElementData.LightMapResolutionScale = FVector2f::UnitVector; bool bHighQualityLightMaps = AllowHighQualityLightmaps(FeatureLevel); ShaderElementData.bTextureMapped = false; if (MeshBatch.LCI && MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetType() == LMIT_Texture && (MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetTexture(bHighQualityLightMaps) || MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetVirtualTexture())) { static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VirtualTexturedLightmaps")); if (CVar->GetValueOnRenderThread() == 1) { IAllocatedVirtualTexture* AllocatedVT = MeshBatch.LCI->GetResourceCluster()->AllocatedVT; if (AllocatedVT) { ShaderElementData.LightMapResolutionScale.X = AllocatedVT->GetWidthInPixels(); ShaderElementData.LightMapResolutionScale.Y = AllocatedVT->GetHeightInPixels() * 2.0f; // Compensates the VT specific math in GetLightMapCoordinates (used to pack more coefficients per texture) } } else { ShaderElementData.LightMapResolutionScale.X = MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetTexture(bHighQualityLightMaps)->GetSizeX(); ShaderElementData.LightMapResolutionScale.Y = MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetTexture(bHighQualityLightMaps)->GetSizeY(); } ShaderElementData.bTextureMapped = true; ShaderElementData.BuiltLightingAndSelectedFlags.X = 1.0f; ShaderElementData.BuiltLightingAndSelectedFlags.Y = 0.0f; } else if (PrimitiveSceneProxy) { int32 LightMapResolution = PrimitiveSceneProxy->GetLightMapResolution(); #if WITH_EDITOR if (GLightmassDebugOptions.bPadMappings) { LightMapResolution -= 2; } #endif if (PrimitiveSceneProxy->IsStatic() && LightMapResolution > 0) { ShaderElementData.bTextureMapped = true; ShaderElementData.LightMapResolutionScale = FVector2f(LightMapResolution, LightMapResolution); if (bHighQualityLightMaps) { // Compensates the math in GetLightMapCoordinates (used to pack more coefficients per texture) ShaderElementData.LightMapResolutionScale.Y *= 2.f; } ShaderElementData.BuiltLightingAndSelectedFlags.X = 1.0f; ShaderElementData.BuiltLightingAndSelectedFlags.Y = 0.0f; } else { ShaderElementData.LightMapResolutionScale = FVector2f::ZeroVector; ShaderElementData.BuiltLightingAndSelectedFlags.X = 0.0f; ShaderElementData.BuiltLightingAndSelectedFlags.Y = 1.0f; } } if (PrimitiveSceneProxy && PrimitiveSceneProxy->IsSelected()) { ShaderElementData.BuiltLightingAndSelectedFlags.Z = 1.0f; } else { ShaderElementData.BuiltLightingAndSelectedFlags.Z = 0.0f; } // Adjust for the grid texture being 2x2 repeating pattern... ShaderElementData.LightMapResolutionScale *= 0.5f; } ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false); const FMeshDrawCommandSortKey SortKey = CalculateMeshStaticSortKey(LightmapDensityPassShaders.VertexShader, LightmapDensityPassShaders.PixelShader); BuildMeshDrawCommands( MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, PassDrawRenderState, LightmapDensityPassShaders, MeshFillMode, MeshCullMode, SortKey, EMeshPassFeatures::Default, ShaderElementData); return true; } bool FLightmapDensityMeshProcessor::TryAddMeshBatch( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy& InMaterialRenderProxy, const FMaterial& InMaterial) { // Determine the mesh's material and blend mode. const FMaterialRenderProxy* MaterialRenderProxy = &InMaterialRenderProxy; const FMaterial* Material = &InMaterial; const bool bMaterialMasked = Material->IsMasked(); const bool bTranslucentBlendMode = IsTranslucentBlendMode(*Material); const bool bIsLitMaterial = Material->GetShadingModels().IsLit(); const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch); const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(*Material, OverrideSettings); const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(*Material, OverrideSettings); const FLightMapInteraction LightMapInteraction = (MeshBatch.LCI && bIsLitMaterial) ? MeshBatch.LCI->GetLightMapInteraction(FeatureLevel) : FLightMapInteraction(); // Force simple lightmaps based on system settings. bool bAllowHighQualityLightMaps = AllowHighQualityLightmaps(FeatureLevel) && LightMapInteraction.AllowsHighQualityLightmaps(); static const auto SupportLowQualityLightmapsVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportLowQualityLightmaps")); const bool bAllowLowQualityLightMaps = (!SupportLowQualityLightmapsVar) || (SupportLowQualityLightmapsVar->GetValueOnAnyThread() != 0); if ((!bTranslucentBlendMode || ViewIfDynamicMeshCommand->Family->EngineShowFlags.Wireframe) && ShouldIncludeMaterialInDefaultOpaquePass(*Material)) { if (!bMaterialMasked && !Material->MaterialModifiesMeshPosition_RenderThread()) { // Override with the default material for opaque materials that are not two sided MaterialRenderProxy = GEngine->LevelColorationLitMaterial->GetRenderProxy(); // If LevelColorationLitMaterial happens to be compiling, use the fallback material and overwrite MaterialRenderProxy Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel); if (!Material) { return false; } } if (!MaterialRenderProxy) { MaterialRenderProxy = MeshBatch.MaterialRenderProxy; } check(Material && MaterialRenderProxy); if (bIsLitMaterial && PrimitiveSceneProxy && (LightMapInteraction.GetType() == LMIT_Texture || (PrimitiveSceneProxy->IsStatic() && PrimitiveSceneProxy->GetLightMapResolution() > 0))) { // Should this object be texture lightmapped? Ie, is lighting not built for it? bool bUseDummyLightMapPolicy = MeshBatch.LCI == nullptr || MeshBatch.LCI->GetLightMapInteraction(FeatureLevel).GetType() != LMIT_Texture; // Use dummy if we don't support either lightmap quality. bUseDummyLightMapPolicy |= (!bAllowHighQualityLightMaps && !bAllowLowQualityLightMaps); if (!bUseDummyLightMapPolicy) { if (bAllowHighQualityLightMaps) { return Process>( MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material, TUniformLightMapPolicy(), MeshBatch.LCI, MeshFillMode, MeshCullMode); } else { return Process>( MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material, TUniformLightMapPolicy(), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else { return Process>( MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material, TUniformLightMapPolicy(), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } else { return Process>( MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material, TUniformLightMapPolicy(), MeshBatch.LCI, MeshFillMode, MeshCullMode); } } return true; } void FLightmapDensityMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId) { if (FeatureLevel >= ERHIFeatureLevel::SM5 && ViewIfDynamicMeshCommand->Family->EngineShowFlags.LightMapDensity && AllowDebugViewmodes() && MeshBatch.bUseForMaterial) { const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy; while (MaterialRenderProxy) { const FMaterial* Material = MaterialRenderProxy->GetMaterialNoFallback(FeatureLevel); if (Material) { if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, StaticMeshId, *MaterialRenderProxy, *Material)) { break; } } MaterialRenderProxy = MaterialRenderProxy->GetFallback(FeatureLevel); } } } FLightmapDensityMeshProcessor::FLightmapDensityMeshProcessor(const FScene* Scene, ERHIFeatureLevel::Type FeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) : FMeshPassProcessor(EMeshPass::LightmapDensity, Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext) { // Opaque blending, depth tests and writes. PassDrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI()); PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); } FMeshPassProcessor* CreateLightmapDensityPassProcessor(ERHIFeatureLevel::Type FeatureLevel, const FScene* Scene, const FSceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext) { return new FLightmapDensityMeshProcessor(Scene, FeatureLevel, InViewIfDynamicMeshCommand, InDrawListContext); } FRegisterPassProcessorCreateFunction RegisterLightmapDensityPass(&CreateLightmapDensityPassProcessor, EShadingPath::Deferred, EMeshPass::LightmapDensity, EMeshPassFlags::MainView);