当前位置:网站首页>UE source code reading [1]--- starting with problems delayed rendering in UE
UE source code reading [1]--- starting with problems delayed rendering in UE
2022-07-05 13:50:00 【This is MX】
Catalog
UE The starting point of the rendered part :
How to deal with multiple light sources
Deal with different lighting models on the same screen :
Deferred Shading :
problem :
1.UE Delayed rendering Gbuffer What does it look like ?
2. How to deal with multiple light sources
3. How to deal with different lighting models on the same screen
UE The starting point of the rendered part :
UE Render part of from GameEngine.h and GameEngine.cpp
voidUGameEngine::Tick( float DeltaSeconds, bool bIdleMode )
{
[... ...]
// Check the settings , For example, whether it is a dedicated server , Only the command line
if (!bIdleMode && !IsRunningDedicatedServer() && !IsRunningCommandlet() && FEmbeddedCommunication::IsAwakeForRendering())
{
// Render window / All objects
RedrawViewports();
// Callback after rendering
GetRendererModule().PostRenderAllViewports();
}
[... ...]
}
// Aforementioned RedrawViewports();
void UGameEngine::RedrawViewports( bool bShouldPresent /*= true*/ )
{
SCOPE_CYCLE_COUNTER(STAT_RedrawViewports);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ViewportMisc);
if ( GameViewport != NULL )
{
GameViewport->LayoutPlayers();
if ( GameViewport->Viewport != NULL )
{
GameViewport->Viewport->Draw(bShouldPresent);
}
}
}
// Aforementioned GameViewport->Viewport->Draw(bShouldPresent);
void UGameViewportClient::Draw()
{
[... ...]
if (!bDisableWorldRendering && PlayerViewMap.Num() > 0 && FSlateApplication::Get().GetPlatformApplication()->IsAllowedToRender()) //-V560
{
// Render player windows , Send a message from the game thread call to the rendering thread that is rendering the view family .
GetRendererModule().BeginRenderingViewFamily(SceneCanvas,&ViewFamily);
}
[... ...]
}
void FRendererModule::BeginRenderingViewFamily(FCanvas* Canvas, FSceneViewFamily* ViewFamily)
{
[... ...]
// Create a scene render ,FSceneRenderer There are two subclasses , Namely FMobileSceneRenderer and FDeferredShadingSceneRenderer
FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, Canvas->GetHitProxyConsumer());
// Send command to render thread to draw scene .
ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)(
[SceneRenderer, DrawSceneEnqueue](FRHICommandListImmediate& RHICmdList)
{
const float StartDelayMillisec = FPlatformTime::ToMilliseconds(FPlatformTime::Cycles() - DrawSceneEnqueue);
CSV_CUSTOM_STAT_GLOBAL(DrawSceneCommand_StartDelay, StartDelayMillisec, ECsvCustomStatOp::Set);
[... ...]
// What the rendering thread actually does
RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
FlushPendingDeleteRHIResources_RenderThread();
});
[... ...]
}
//RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
static void RenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer)
{
[... ...]
// Update any resources that require delayed updates
FDeferredUpdateResource::UpdateResources(RHICmdList);
[... ...]
// Render the scene
if(SceneRenderer->ViewFamily.EngineShowFlags.HitProxies)
{
// Render the scene's hit proxies.
SceneRenderer->RenderHitProxies(RHICmdList);
}
else
{
// Render the scene.
SceneRenderer->Render(RHICmdList);
}
[... ...]
}
Because it depends on delayed rendering , So let's look directly at FDeferredShadingSceneRenderer.h and FDeferredShadingSceneRenderer.cpp
FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) This function has more than 1300 lines , It probably contains data for preparing atmospheric scattering ,Prepass Render depth , Light chases ,Base pass Delay the geometric phase of rendering , Simplified bounding box depth value rendering for occlusion culling , Render shadows and depth , Rendering motion vector, Render hair , Render lighting , Render the atmosphere , Render atmospheric scattering , Render fog , Render volume clouds , Notify the special effects system , Rendering translucent objects , post-processing
How to deal with multiple light sources
We won't talk about cluster shading To deal with
FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
[... ...]
bool bDoInitViewAftersPrepass = false;
{
SCOPED_GPU_STAT(RHICmdList, VisibilityCommands);
bDoInitViewAftersPrepass = InitViews(RHICmdList, BasePassDepthStencilAccess, ILCTaskData)
}
[... ...]
}
bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, struct FILCUpdatePrimTaskData& ILCTaskData)
{
[... ...]
// Flat cut , Bounding box calculation , Occlusion culling , Collect grid information , Create light source information
ComputeViewVisibility(RHICmdList, BasePassDepthStencilAccess, ViewCommandsPerView, DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews);
[... ...]
// Handle Decal sorting , Deal with light visibility
PostVisibilityFrameSetup(ILCTaskData);
[... ...]
}
void FSceneRenderer::PostVisibilityFrameSetup(FILCUpdatePrimTaskData& OutILCTaskData)
{
[... ...]
// Determine the visibility of the light
for(TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights);LightIt;++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
// Use the visual cone of the camera to cut the light source
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
const FLightSceneProxy* Proxy = LightSceneInfo->Proxy;
FViewInfo& View = Views[ViewIndex];
FVisibleLightViewInfo& VisibleLightViewInfo = View.VisibleLightInfos[LightIt.GetIndex()];
// Direct light is always visible , Spot light / Spotlights / Face light needs judgment
if( Proxy->GetLightType() == LightType_Point ||
Proxy->GetLightType() == LightType_Spot ||
Proxy->GetLightType() == LightType_Rect )
{
FSphere const& BoundingSphere = Proxy->GetBoundingSphere();
if (View.ViewFrustum.IntersectSphere(BoundingSphere.Center, BoundingSphere.W))
{
// Perspective cameras need to raise light sources too far
if (View.IsPerspectiveProjection())
{
FSphere Bounds = Proxy->GetBoundingSphere();
float DistanceSquared = (Bounds.Center - View.ViewMatrices.GetViewOrigin()).SizeSquared();
float MaxDistSquared = Proxy->GetMaxDrawDistance() * Proxy->GetMaxDrawDistance() * GLightMaxDrawDistanceScale * GLightMaxDrawDistanceScale;
// The radius of the light source is considered 、 View's LOD factor 、 The minimum light source screen radius and other factors determine whether the final light source needs to be drawn , In order to eliminate the light source with a small proportion of remote screen .
const bool bDrawLight = (FMath::Square(FMath::Min(0.0002f, GMinScreenRadiusForLights / Bounds.W) * View.LODDistanceFactor) * DistanceSquared < 1.0f)
&& (MaxDistSquared == 0 || DistanceSquared < MaxDistSquared);
VisibleLightViewInfo.bInViewFrustum = bDrawLight;
}
else
{
VisibleLightViewInfo.bInViewFrustum = true;
}
}
}
else
{
VisibleLightViewInfo.bInViewFrustum = true;
// Set a single sun axis to move from the direction light .
if (bSetupMobileLightShafts && LightSceneInfo->bEnableLightShaftBloom && ShouldRenderLightShaftsForLight(View, *LightSceneInfo->Proxy))
{
View.MobileLightShaft = GetMobileLightShaftInfo(View, *LightSceneInfo);
}
}
[... ...]
}
It can be seen from the above that the treatment of the light source is : Use the bounding box of the light source to intersect with the viewing cone , Eliminate disjoint , Then the light source with small screen occupation is proposed , And record its visibility to VisibleLightViewInfo.bInViewFrustum
Gbuffer Layout:
RenderBasePass():
The code of rendering geometry stage should be RenderBasePass() This function
void FDeferredShadingSceneRenderer::RenderBasePass(
FRDGBuilder& GraphBuilder,
FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
FRDGTextureRef SceneColorTexture,
FRDGTextureRef SceneDepthTexture,
ERenderTargetLoadAction SceneDepthLoadAction,
FRDGTextureRef ForwardShadowMaskTexture)
{
const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread();
static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod"));
bool bRequiresRHIClear = true;
bool bRequiresFarZQuadClear = false;
if (ClearMethodCVar)
{
int32 ClearMethod = ClearMethodCVar->GetValueOnRenderThread();
if (ClearMethod == 0 && !ViewFamily.EngineShowFlags.Game)
{
// Do not clear the scene only if the view family is in game mode.
ClearMethod = 1;
}
switch (ClearMethod)
{
case 0: // No clear
bRequiresRHIClear = false;
bRequiresFarZQuadClear = false;
break;
case 1: // RHICmdList.Clear
bRequiresRHIClear = true;
bRequiresFarZQuadClear = false;
break;
case 2: // Clear using far-z quad
bRequiresFarZQuadClear = true;
bRequiresRHIClear = false;
break;
}
}
// Always perform a full buffer clear for wireframe, shader complexity view mode, and stationary light overlap viewmode.
if (ViewFamily.EngineShowFlags.Wireframe || ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
{
bRequiresRHIClear = true;
bRequiresFarZQuadClear = false;
}
const bool bIsWireframeRenderpass = ViewFamily.EngineShowFlags.Wireframe && FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]);
const bool bDebugViewMode = ViewFamily.UseDebugViewPS();
const bool bRenderLightmapDensity = ViewFamily.EngineShowFlags.LightMapDensity && AllowDebugViewmodes();
const bool bRenderSkyAtmosphereEditorNotifications = ShouldRenderSkyAtmosphereEditorNotifications();
const bool bDoParallelBasePass = bEnableParallelBasePasses && !bDebugViewMode && !bRenderLightmapDensity; // DebugView and LightmapDensity are non-parallel substitutions inside BasePass
const bool bNeedsBeginRender = AllowDebugViewmodes() &&
(ViewFamily.EngineShowFlags.RequiredTextureResolution ||
ViewFamily.EngineShowFlags.MaterialTextureScaleAccuracy ||
ViewFamily.EngineShowFlags.MeshUVDensityAccuracy ||
ViewFamily.EngineShowFlags.PrimitiveDistanceAccuracy ||
ViewFamily.EngineShowFlags.ShaderComplexity ||
ViewFamily.EngineShowFlags.LODColoration ||
ViewFamily.EngineShowFlags.HLODColoration);
const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess);
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(GraphBuilder.RHICmdList);
TStaticArray<FRDGTextureRef, MaxSimultaneousRenderTargets> BasePassTextures;
int32 GBufferDIndex = INDEX_NONE;
uint32 BasePassTextureCount = SceneContext.GetGBufferRenderTargets(GraphBuilder, BasePassTextures, GBufferDIndex);
TArrayView<FRDGTextureRef> BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
FRDGTextureRef BasePassDepthTexture = SceneDepthTexture;
FLinearColor SceneColorClearValue;
if (bRequiresRHIClear)
{
if (ViewFamily.EngineShowFlags.ShaderComplexity)
{
SceneContext.ClearQuadOverdrawUAV(GraphBuilder);
}
if (ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
{
SceneColorClearValue = FLinearColor(0, 0, 0, kSceneColorClearAlpha);
}
else
{
SceneColorClearValue = FLinearColor(Views[0].BackgroundColor.R, Views[0].BackgroundColor.G, Views[0].BackgroundColor.B, kSceneColorClearAlpha);
}
ERenderTargetLoadAction ColorLoadAction = ERenderTargetLoadAction::ELoad;
if (SceneColorTexture->Desc.ClearValue.GetClearColor() == SceneColorClearValue)
{
ColorLoadAction = ERenderTargetLoadAction::EClear;
}
else
{
ColorLoadAction = ERenderTargetLoadAction::ENoAction;
}
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets = GetRenderTargetBindings(ColorLoadAction, BasePassTexturesView);
static TConsoleVariableData<int32>* CVarNoGBufferDClear = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.NoGBufferDClear"));
if (CVarNoGBufferDClear && !!CVarNoGBufferDClear->GetValueOnRenderThread() && GBufferDIndex != INDEX_NONE)
{
PassParameters->RenderTargets[GBufferDIndex].SetLoadAction(ERenderTargetLoadAction::ENoAction);
}
if (SceneDepthLoadAction == ERenderTargetLoadAction::EClear)
{
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, SceneDepthLoadAction, SceneDepthLoadAction, ExclusiveDepthStencil);
}
GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster,
[PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList)
{
// If no fast-clear action was used, we need to do an MRT shader clear.
if (ColorLoadAction == ERenderTargetLoadAction::ENoAction)
{
const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets;
FLinearColor ClearColors[MaxSimultaneousRenderTargets];
FRHITexture* Textures[MaxSimultaneousRenderTargets];
int32 TextureIndex = 0;
RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget)
{
FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI();
ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor();
Textures[TextureIndex] = TextureRHI;
++TextureIndex;
});
// Clear color only; depth-stencil is fast cleared.
DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0);
}
});
if (bRenderSkyAtmosphereEditorNotifications)
{
// We only render this warning text when bRequiresRHIClear==true to make sure the scene color buffer is allocated at this stage.
// When false, the option specifies that all pixels must be written to by a sky dome anyway.
RenderSkyAtmosphereEditorNotifications(GraphBuilder, SceneColorTexture);
}
}
if (ViewFamily.EngineShowFlags.Wireframe)
{
checkf(ExclusiveDepthStencil.IsDepthWrite(), TEXT("Wireframe base pass requires depth-write, but it is set to read-only."));
SceneContext.GetEditorPrimitivesColor(GraphBuilder.RHICmdList);
SceneContext.GetEditorPrimitivesDepth(GraphBuilder.RHICmdList);
BasePassTextureCount = 1;
BasePassTextures[0] = GraphBuilder.RegisterExternalTexture(SceneContext.EditorPrimitivesColor, ERenderTargetTexture::Targetable);
BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
BasePassDepthTexture = GraphBuilder.RegisterExternalTexture(SceneContext.EditorPrimitivesDepth, ERenderTargetTexture::Targetable);
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, BasePassTexturesView);
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, ExclusiveDepthStencil);
GraphBuilder.AddPass(RDG_EVENT_NAME("WireframeClear"), PassParameters, ERDGPassFlags::Raster, [](FRHICommandList&) {});
}
// Render targets bindings should remain constant at this point.
FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
BasePassRenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, ViewFamily, nullptr, EVRSType::None);
AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_BasePass));
RenderBasePassInternal(GraphBuilder, BasePassRenderTargets, BasePassDepthStencilAccess, ForwardShadowMaskTexture, bDoParallelBasePass, bRenderLightmapDensity);
AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AfterBasePass));
if (ViewFamily.ViewExtensions.Num() > 0)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass);
RDG_EVENT_SCOPE(GraphBuilder, "BasePass_ViewExtensions");
auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets = BasePassRenderTargets;
for (auto& ViewExtension : ViewFamily.ViewExtensions)
{
for (FViewInfo& View : Views)
{
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
GraphBuilder.AddPass(
{},
PassParameters,
ERDGPassFlags::Raster,
[&ViewExtension, &View](FRHICommandListImmediate& RHICmdList)
{
ViewExtension->PostRenderBasePass_RenderThread(RHICmdList, View);
});
}
}
}
if (bRequiresFarZQuadClear)
{
ClearGBufferAtMaxZ(GraphBuilder, Views, BasePassRenderTargets, SceneColorClearValue);
}
if (ShouldRenderAnisotropyPass())
{
AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AnisotropyPass));
RenderAnisotropyPass(GraphBuilder, SceneDepthTexture, bEnableParallelBasePasses);
AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AfterAnisotropyPass));
}
if (SceneContext.GBufferA)
{
AddAsyncComputeSRVTransitionHackPass(GraphBuilder, GraphBuilder.RegisterExternalTexture(SceneContext.GBufferA));
}
}
GetGBufferRenderTargets()
First of all, I'll determine how many he has MRT, Track them BasePassRenderTargets Variable , Step by step, you can go deep in
Find int32 FSceneRenderTargets::GetGBufferRenderTargets()
int32 FSceneRenderTargets::GetGBufferRenderTargets(const TRefCountPtr<IPooledRenderTarget>* OutRenderTargets[MaxSimultaneousRenderTargets], int32& OutVelocityRTIndex, int32& OutGBufferDIndex) const
{
int32 MRTCount = 0;
//rt0 Color the scene
OutRenderTargets[MRTCount++] = &GetSceneColor();
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(CurrentFeatureLevel);
const bool bUseGBuffer = IsUsingGBuffers(ShaderPlatform);
if (bUseGBuffer)
{
OutRenderTargets[MRTCount++] = &GBufferA;
OutRenderTargets[MRTCount++] = &GBufferB;
OutRenderTargets[MRTCount++] = &GBufferC;
}
//rtVelocity = Speed buffer rt
// The velocity buffer needs to be bound before other optionnal rendertargets (when UseSelectiveBasePassOutputs() is true).
// Otherwise there is an issue on some AMD hardware where the target does not get updated. Seems to be related to the velocity buffer format as it works fine with other targets.
if (bAllocateVelocityGBuffer && !IsSimpleForwardShadingEnabled(ShaderPlatform))
{
OutVelocityRTIndex = MRTCount;
check(OutVelocityRTIndex == 4 || (!bUseGBuffer && OutVelocityRTIndex == 1)); // As defined in BasePassPixelShader.usf
OutRenderTargets[MRTCount++] = &SceneVelocity;
}
else
{
OutVelocityRTIndex = -1;
}
OutGBufferDIndex = INDEX_NONE;
if (bUseGBuffer)
{
OutGBufferDIndex = MRTCount;
OutRenderTargets[MRTCount++] = &GBufferD;
if (bAllowStaticLighting)
{
check(MRTCount == (bAllocateVelocityGBuffer ? 6 : 5)); // As defined in BasePassPixelShader.usf
OutRenderTargets[MRTCount++] = &GBufferE;
}
}
check(MRTCount <= MaxSimultaneousRenderTargets);
return MRTCount;
}
among MaxSimultaneousRenderTargets as follows :
According to the above code, you can know ,RT At most 8 individual ( according to MaxSimultaneousRenderTargets You know , But it seems that there are only about seven above ?), Respectively SceneColor,GBufferA,GBufferB,GBufferC,SceneVelocity,GBufferD,GBufferE, Then its relevant definition is BasePassPixelShader.usf In this pixel shader
BasePassPixelShader.usf
Let's look at BasePassPixelShader.usf
BasePassPixelShader The main entrance of is void FPixelShaderInOut_MainPS()
It can be downloaded from MRT Write here to analyze each RT What on earth is stored
#if MATERIAL_DOMAIN_POSTPROCESS
#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
Out.MRT[0] = half4(Color, Opacity);
#else
Out.MRT[0] = half4(Color, 0);
#endif
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
// MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT must come first because it also has MATERIALBLENDING_TRANSLUCENT defined
#elif MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT
// After thin translucency, the final color is going to be:
// FinalColor = DualBlendColorAdd + DualBlendColorMul * BackgroundColor;
// To apply fogging, we want the final equation to be:
// FinalColor = Fogging.rgb + Fogging.a * (DualBlendColorAdd + DualBlendColorMul * BackgroundColor);
// FinalColor = (Fogging.rgb + Fogging.a * DualBlendColorAdd) + Fogging.a * DualBlendColorMul * BackgroundColor;
// Or in other words:
// AdjustedDualBlendAdd = Fogging.rgb + Fogging.a * DualBlendColorAdd;
// AdjustedDualBlendMul = Fogging.a * DualBlendColorMul;
// FinalColor = AdjustedDualBlendAdd + AdjustedDualBlendMul * BackgroundColor;
float3 AdjustedDualBlendAdd = Fogging.rgb + Fogging.a * DualBlendColorAdd;
float3 AdjustedDualBlendMul = Fogging.a * DualBlendColorMul;
#if THIN_TRANSLUCENT_USE_DUAL_BLEND
// no RETURN_COLOR because these values are explicit multiplies and adds
Out.MRT[0] = half4(AdjustedDualBlendAdd,0.0);
Out.MRT[1] = half4(AdjustedDualBlendMul,1.0);
#else
// In the fallback case, we are blending with the mode
float AdjustedAlpha = saturate(1-dot(AdjustedDualBlendMul,float3(1.0f,1.0f,1.0f)/3.0f));
Out.MRT[0] = half4(AdjustedDualBlendAdd,AdjustedAlpha);
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
#endif
#elif MATERIALBLENDING_ALPHAHOLDOUT
// not implemented for holdout
Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb * Opacity, Opacity);
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
#elif MATERIALBLENDING_ALPHACOMPOSITE
Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb * Opacity, Opacity);
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
#elif MATERIALBLENDING_TRANSLUCENT
Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb, Opacity);
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
#elif MATERIALBLENDING_ADDITIVE
Out.MRT[0] = half4(Color * Fogging.a * Opacity, 0.0f);
Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
#elif MATERIALBLENDING_MODULATE
// RETURN_COLOR not needed with modulative blending
half3 FoggedColor = lerp(float3(1, 1, 1), Color, Fogging.aaa * Fogging.aaa);
Out.MRT[0] = half4(FoggedColor, Opacity);
#else
{
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
// Apply vertex fog
Color = Color * Fogging.a + Fogging.rgb;
#if POST_PROCESS_SUBSURFACE
// Apply vertex fog to diffuse color
DiffuseColor = DiffuseColor * Fogging.a + Fogging.rgb;
if (UseSubsurfaceProfile(GBuffer.ShadingModelID) &&
View.bSubsurfacePostprocessEnabled > 0 && View.bCheckerboardSubsurfaceProfileRendering > 0 )
{
// Adjust for checkerboard. only apply non-diffuse lighting (including emissive)
// to the specular component, otherwise lighting is applied twice
Color *= !bChecker;
}
LightAccumulator_Add(LightAccumulator, Color + DiffuseColor, DiffuseColor, 1.0f, UseSubsurfaceProfile(GBuffer.ShadingModelID));
#else
LightAccumulator_Add(LightAccumulator, Color, 0, 1.0f, false);
#endif
Out.MRT[0] = RETURN_COLOR(LightAccumulator_GetResult(LightAccumulator));
#if !USES_GBUFFER
// Without deferred shading the SSS pass will not be run to reset scene color alpha for opaque / masked to 0
// Scene color alpha is used by scene captures and planar reflections
Out.MRT[0].a = 0;
#endif
}
#endif
#if USES_GBUFFER
GBuffer.IndirectIrradiance = IndirectIrradiance;
// -0.5 .. 0.5, could be optimzed as lower quality noise would be sufficient
float QuantizationBias = PseudoRandom( MaterialParameters.SvPosition.xy ) - 0.5f;
EncodeGBuffer(GBuffer, Out.MRT[1], Out.MRT[2], Out.MRT[3], OutGBufferD, OutGBufferE, OutVelocity, QuantizationBias);
#endif
if(bEditorWeightedZBuffering)
{
Out.MRT[0].a = 1;
#if MATERIALBLENDING_MASKED
// some material might have a opacity value
Out.MRT[0].a = GetMaterialMaskInputRaw(PixelMaterialInputs);
#endif
#if EDITOR_ALPHA2COVERAGE != 0
// per MSAA sample
if(View.NumSceneColorMSAASamples > 1)
{
Out.Coverage = In.Coverage & CustomAlpha2Coverage(Out.MRT[0]);
}
else
{
// no MSAA is handle like per pixel
clip(Out.MRT[0].a - GetMaterialOpacityMaskClipValue());
}
#else
// per pixel
clip(Out.MRT[0].a - GetMaterialOpacityMaskClipValue());
#endif
}
#if USES_GBUFFER
#if GBUFFER_HAS_VELOCITY
Out.MRT[4] = OutVelocity;
#endif
Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = OutGBufferD;
#if GBUFFER_HAS_PRECSHADOWFACTOR
Out.MRT[GBUFFER_HAS_VELOCITY ? 6 : 5] = OutGBufferE;
#endif
#else
// If not using the full gbuffer (forward shading) the velocity buffer can still be written to in the basepass.
#if GBUFFER_HAS_VELOCITY
Out.MRT[1] = OutVelocity;
#endif
#endif
#if !MATERIALBLENDING_MODULATE && USE_PREEXPOSURE
#if MATERIAL_IS_SKY
// Dynamic capture exposure is 1 as of today.
const float ViewPreExposure = View.RealTimeReflectionCapture>0.0f ? View.RealTimeReflectionCapturePreExposure : View.PreExposure;
#else
const float ViewPreExposure = View.PreExposure;
#endif
// We need to multiply pre-exposure by all components including A, otherwise the ratio of
// diffuse to specular lighting will get messed up in the SSS pass.
// RGB: Full color (Diffuse + Specular)
// A: Diffuse Intensity, but only if we are not blending
#if MATERIAL_DOMAIN_POSTPROCESS || MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT || MATERIALBLENDING_ALPHAHOLDOUT || MATERIALBLENDING_ALPHACOMPOSITE || MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE
Out.MRT[0].rgb *= ViewPreExposure;
#else
Out.MRT[0].rgba *= ViewPreExposure;
#endif
#endif
#if MATERIAL_IS_SKY
// Sky materials can result in high luminance values, e.g. the sun disk.
// This is so we make sure to at least stay within the boundaries of fp10 and not cause NaN on some platforms.
// We also half that range to also make sure we have room for other additive elements such as bloom, clouds or particle visual effects.
Out.MRT[0].xyz = min(Out.MRT[0].xyz, Max10BitsFloat.xxx * 0.5f);
#endif
First analysis SceneColor , because ue In order to be compatible with different lighting models , Used a bunch of macros , The basic idea is to call the actual lighting model variant calculation basis Diffuse color + Spontaneous light + Superimposed fog effect 【 It will change according to different shading models 】
Then there is a EncodeGBuffer Function will GBuffer Part of the data in is encoded to MRT in , This function is defined in DeferredShadingCommon.ush
/** Populates OutGBufferA, B and C */
void EncodeGBuffer(
FGBufferData GBuffer,
out float4 OutGBufferA,
out float4 OutGBufferB,
out float4 OutGBufferC,
out float4 OutGBufferD,
out float4 OutGBufferE,
out float4 OutGBufferVelocity,
float QuantizationBias = 0 // -0.5 to 0.5 random float. Used to bias quantization.
)
{
if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT)
{
OutGBufferA = 0;
SetGBufferForUnlit(OutGBufferB);
OutGBufferC = 0;
OutGBufferD = 0;
OutGBufferE = 0;
}
else
{
#if MOBILE_DEFERRED_SHADING
OutGBufferA.rg = UnitVectorToOctahedron( normalize(GBuffer.WorldNormal) ) * 0.5f + 0.5f;
OutGBufferA.b = GBuffer.PrecomputedShadowFactors.x;
OutGBufferA.a = GBuffer.PerObjectGBufferData;
#elif 1
OutGBufferA.rgb = EncodeNormal( GBuffer.WorldNormal );
OutGBufferA.a = GBuffer.PerObjectGBufferData;
#else
float3 Normal = GBuffer.WorldNormal;
uint NormalFace = 0;
EncodeNormal( Normal, NormalFace );
OutGBufferA.rg = Normal.xy;
OutGBufferA.b = 0;
OutGBufferA.a = GBuffer.PerObjectGBufferData;
#endif
OutGBufferB.r = GBuffer.Metallic;
OutGBufferB.g = GBuffer.Specular;
OutGBufferB.b = GBuffer.Roughness;
OutGBufferB.a = EncodeShadingModelIdAndSelectiveOutputMask(GBuffer.ShadingModelID, GBuffer.SelectiveOutputMask);
OutGBufferC.rgb = EncodeBaseColor( GBuffer.BaseColor );
#if ALLOW_STATIC_LIGHTING
// No space for AO. Multiply IndirectIrradiance by AO instead of storing.
OutGBufferC.a = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0);
#else
OutGBufferC.a = GBuffer.GBufferAO;
#endif
OutGBufferD = GBuffer.CustomData;
OutGBufferE = GBuffer.PrecomputedShadowFactors;
}
#if WRITES_VELOCITY_TO_GBUFFER
OutGBufferVelocity = GBuffer.Velocity;
#else
OutGBufferVelocity = 0;
#endif
}
Here are some functions used by the above functions :
① Compression of unit vectors , Octahedral mapping compression :
// Octahedron Normal Vectors
// [Cigolle 2014, "A Survey of Efficient Representations for Independent Unit Vectors"]
// Mean Max
// oct 8:8 0.33709 0.94424
// snorm 8:8:8 0.17015 0.38588
// oct 10:10 0.08380 0.23467
// snorm 10:10:10 0.04228 0.09598
// oct 12:12 0.02091 0.05874
float2 UnitVectorToOctahedron( float3 N )
{
N.xy /= dot( 1, abs(N) );
if( N.z <= 0 )
{
N.xy = ( 1 - abs(N.yx) ) * ( N.xy >= 0 ? float2(1,1) : float2(-1,-1) );
}
return N.xy;
}
② Compress ShadingModelId and SelectiveOutputMask
loat EncodeShadingModelIdAndSelectiveOutputMask(uint ShadingModelId, uint SelectiveOutputMask)
{
uint Value = (ShadingModelId & SHADINGMODELID_MASK) | SelectiveOutputMask;
return (float)Value / (float)0xFF;
}
③ code IndirectIrradiance
float EncodeIndirectIrradiance(float IndirectIrradiance)
{
float L = IndirectIrradiance;
#if USE_PREEXPOSURE
L *= View.PreExposure; // Apply pre-exposure as a mean to prevent compression overflow.
#endif
const float LogBlackPoint = 0.00390625; // exp2(-8);
return log2( L + LogBlackPoint ) / 16 + 0.5;
}
Then according to this coding function, we can know GBuffer Layout , Although according to different lighting models / There will be some differences in the pipeline ,
But it can be summarized as follows :
GBuffer Layout :
①GBufferA:rg/b Store normals / Pre calculated shadow factor ,a Store pre calculated object data
②GBufferB:r Storage metallicity ,g Store highlight values ,b Store roughness ,a Store encoded illumination models and SelectiveOutputMask
SelectiveOutputMask Record the opening results of the following macros when drawing :
- MATERIAL_USES_ANISOTROPY It is forbidden to calculate anisotropy
- !GBUFFER_HAS_PRECSHADOWFACTOR Read forbidden GBufferE Data as pre calculated shadows
- GBUFFER_HAS_PRECSHADOWFACTOR && WRITES_PRECSHADOWFACTOR_ZERO When not reading GBufferE when , If this value is 1 when , The pre calculated shadow is set to 0, Otherwise 1.
- WRITES_VELOCITY_TO_GBUFFER Prohibition from Gbuffer Read the speed value in .
③GBufferC :rgb Storage basecolor,a Storage AO, If static lighting is allowed a Storage IndirectIrradiance*Material AO
④GBufferD: Store custom data
⑤GBufferE: Store the pre calculated shadow value
⑥GBufferVelocity: Storage SceneVelocity
Deal with different lighting models on the same screen :
The basic idea :
UE Of shader It's using Uber Shader The design of the , Through another shader It defines a bunch of macros , And then compiling Shader When , Pass in different macro parameters , Compile different shader Code (bgfx That's what it does inside ),
At compile time , How do different macros get into the compiler ,UE It mainly establishes a Shader Permutation The concept of , To store a unique hash key , Through different Shader Permutation to HLSL, Compile the corresponding shader code , And then through ShaderMap Get the instance shader
ShaderPermutation:
Shader Permutation The relevant code of is in ShaderPermutation.h in ,
template <typename TDimension, typename... Ts>
struct TShaderPermutationDomain<TDimension, Ts...>
{
/** Setup the dimension's type in permutation domain as itself so that a permutation domain can be
* used as a dimension of another domain.
*/
using Type = TShaderPermutationDomain<TDimension, Ts...>;
/** Define a domain as a multidimensional dimension so that ModifyCompilationEnvironment() is used. */
static constexpr bool IsMultiDimensional = true;
/** Parent type in the variadic template to reduce code. */
using Super = TShaderPermutationDomain<Ts...>;
/** Total number of permutation within the domain. */
static constexpr int32 PermutationCount = Super::PermutationCount * TDimension::PermutationCount;
/** Constructors. */
TShaderPermutationDomain<TDimension, Ts...>()
: DimensionValue(TDimension::FromDimensionValueId(0))
{
}
explicit TShaderPermutationDomain<TDimension, Ts...>(int32 PermutationId)
: DimensionValue(TDimension::FromDimensionValueId(PermutationId % TDimension::PermutationCount))
, Tail(PermutationId / TDimension::PermutationCount)
{
checkf(PermutationId >= 0 && PermutationId < PermutationCount, TEXT("Invalid shader permutation id %i."), PermutationId);
}
/** Set dimension's value. */
template<class DimensionToSet>
void Set(typename DimensionToSet::Type Value)
{
return TShaderPermutationDomainSpetialization<TIsSame<TDimension, DimensionToSet>::Value>::template SetDimension<Type, DimensionToSet>(*this, Value);
}
/** Get dimension's value. */
template<class DimensionToGet>
const typename DimensionToGet::Type& Get() const
{
return TShaderPermutationDomainSpetialization<TIsSame<TDimension, DimensionToGet>::Value>::template GetDimension<Type, DimensionToGet>(*this);
}
/** Modify the shader's compilation environment. */
void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) const
{
TShaderPermutationDomainSpetialization<TDimension::IsMultiDimensional>::template ModifyCompilationEnvironment<Type, TDimension>(*this, OutEnvironment);
}
/** Converts domain permutation vector to domain's value id. */
static int32 ToDimensionValueId(const Type& PermutationVector)
{
return PermutationVector.ToDimensionValueId();
}
int32 ToDimensionValueId() const
{
return TDimension::ToDimensionValueId(DimensionValue) + TDimension::PermutationCount * Tail.ToDimensionValueId();
}
/** Returns the permutation domain from the unique ID. */
static Type FromDimensionValueId(const int32 PermutationId)
{
return Type(PermutationId);
}
/** Test if equal. */
bool operator==(const Type& Other) const
{
return DimensionValue == Other.DimensionValue && Tail == Other.Tail;
}
/** Test if not equal. */
bool operator!=(const Type& Other) const
{
return !(*this == Other);
}
private:
template<bool BooleanSpetialization>
friend class TShaderPermutationDomainSpetialization;
typename TDimension::Type DimensionValue;
Super Tail;
};
This template is actually not easy to understand , According to the combination class FDeferredLightPS : public FGlobalShader Simply sort out the code in
FDeferredLightPS:
First, declare the corresponding array , Among them string Namely shader Macro in
class FSourceShapeDim : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_SOURCE_SHAPE", ELightSourceShape);
class FSourceTextureDim : SHADER_PERMUTATION_BOOL("USE_SOURCE_TEXTURE");
class FIESProfileDim : SHADER_PERMUTATION_BOOL("USE_IES_PROFILE");
class FInverseSquaredDim : SHADER_PERMUTATION_BOOL("INVERSE_SQUARED_FALLOFF");
class FVisualizeCullingDim : SHADER_PERMUTATION_BOOL("VISUALIZE_LIGHT_CULLING");
class FLightingChannelsDim : SHADER_PERMUTATION_BOOL("USE_LIGHTING_CHANNELS");
class FTransmissionDim : SHADER_PERMUTATION_BOOL("USE_TRANSMISSION");
class FHairLighting : SHADER_PERMUTATION_INT("USE_HAIR_LIGHTING", 2);
class FAtmosphereTransmittance : SHADER_PERMUTATION_BOOL("USE_ATMOSPHERE_TRANSMITTANCE");
class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE");
class FAnistropicMaterials : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
Then get the instance shader The time is as follows , Let me declare one Permutation Domain , And setting its value indicates whether the corresponding compilation option is enabled , And then use this Permutation Domain from ShaderMap Get the corresponding instance shader, Then one shader Whether you need to compile is through a static function static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
FDeferredLightPS::FPermutationDomain PermutationVector;
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( ELightSourceShape::Directional );
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( false );
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( false );
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( View.bUsesLightingChannels );
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(ShouldRenderAnisotropyPass());
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( bTransmission );
PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
// Only directional lights are rendered in this path, so we only need to check if it is use to light the atmosphere
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(bAtmospherePerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(bLight0CloudPerPixelTransmittance || bLight1CloudPerPixelTransmittance);
TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, &RenderLightParams);
边栏推荐
- [public class preview]: basis and practice of video quality evaluation
- What happened to the communication industry in the first half of this year?
- ::ffff:192.168.31.101 是一个什么地址?
- Network security HSRP protocol
- leetcode 10. Regular Expression Matching 正则表达式匹配 (困难)
- Internal JSON-RPC error. {"code":-32000, "message": "execution reverted"} solve the error
- Liar report query collection network PHP source code
- Nantong online communication group
- 华为推送服务内容,阅读笔记
- Aikesheng sqle audit tool successfully completed the evaluation of "SQL quality management platform grading ability" of the Academy of communications and communications
猜你喜欢
Godson 2nd generation burn PMON and reload system
Win10——轻量级小工具
Solve the problem of invalid uni app configuration page and tabbar
Jetpack Compose入门到精通
[notes of in-depth study paper]transbtsv2: wider instead of deep transformer for medical image segmentation
Idea set method annotation and class annotation
ZABBIX monitoring
zabbix 监控
redis6事务和锁机制
Huawei push service content, read notes
随机推荐
Network security HSRP protocol
Redis6 data type and operation summary
Etcd database source code analysis -- rawnode simple package
那些考研后才知道的事
uplad_ Labs first three levels
Primary code audit [no dolls (modification)] assessment
【公开课预告】:视频质量评价基础与实践
[machine learning notes] several methods of splitting data into training sets and test sets
Laravel框架运行报错:No application encryption key has been specified
[MySQL usage Script] catch all MySQL time and date types and related operation functions (3)
jasypt配置文件加密|快速入门|实战
Attack and defense world crypto WP
Don't know these four caching modes, dare you say you understand caching?
Scientific running robot pancakeswap clip robot latest detailed tutorial
Usage, installation and use of TortoiseSVN
Operational research 68 | the latest impact factors in 2022 were officially released. Changes in journals in the field of rapid care
面试官灵魂拷问:为什么代码规范要求 SQL 语句不要过多的 join?
Aspx simple user login
Zhubo Huangyu: these spot gold investment skills are not really bad
Internal JSON-RPC error. {"code":-32000, "message": "execution reverted"} solve the error