版本不同,我这里延迟渲染是FDeferredShadingSceneRenderer类
即函数
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList);
逐渐填充大象无形中的注释
一,按需要重新分配渲染目标
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
.............
//获得渲染目标单例
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
......
//分配渲染目标
SceneContext.AllocDummyGBufferTargets(RHICmdList);
....
}
查看其实现
static TGlobalResource<FSceneRenderTargets> SceneRenderTargetsSingleton;
FSceneRenderTargets& FSceneRenderTargets::Get(FRHICommandListImmediate& RHICmdList)
{
.......
return SceneRenderTargetsSingleton;
}
void FSceneRenderTargets::AllocDummyGBufferTargets(FRHICommandList& RHICmdList)
{
if (GBufferDummyResourcesUniformBuffer)
{
return;
}
FTextureRHIRef BlackDummy = GSystemTextures.BlackDummy->GetRenderTargetItem().ShaderResourceTexture;
FGBufferResourceStruct GBufferResourceStruct;
GBufferResourceStruct.GBufferATexture = BlackDummy;
........
GBufferResourceStruct.GBufferATextureSampler = TStaticSamplerState<>::GetRHI();
.......
GBufferDummyResourcesUniformBuffer = FGBufferResourceStruct::CreateUniformBuffer(GBufferResourceStruct, UniformBuffer_SingleFrame);
}
二,初始化视口(可以多个),包括确定可见性与设置阴影相关参数
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
.......
FGraphEventArray SortEvents;
FILCUpdatePrimTaskData ILCTaskData;
bool bDoInitViewAftersPrepass = InitViews(RHICmdList, ILCTaskData, SortEvents);
.........
}
现在看下initViews函数
bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, struct FILCUpdatePrimTaskData& ILCTaskData, FGraphEventArray& SortEvents)
{
........
//预设置可见性
PreVisibilityFrameSetup(RHICmdList);
//可见性计算
ComputeViewVisibility(RHICmdList);
.....
//完成可见性计算
PostVisibilityFrameSetup(ILCTaskData);
.......
}
1, //预设置可见性
void FSceneRenderer::PreVisibilityFrameSetup(FRHICommandListImmediate& RHICmdList)
{
//告诉RHI命令队列,开始渲染
RHICmdList.BeginScene();
......
//根据当前画质设置,设置TemporalAA的采样方式,同时确定采样位置,
//这个采样位置用于微调接下来的矩阵,进行像素偏移,混合前面几帧的渲染结果,提高精度
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
FSceneViewState* ViewState = View.ViewState;
......
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
// set up the screen area for occlusion
float NumPossiblePixels = SceneContext.UseDownsizedOcclusionQueries() && IsValidRef(SceneContext.GetSmallDepthSurface()) ?
(float)View.ViewRect.Width() / SceneContext.GetSmallColorDepthDownsampleFactor() * (float)View.ViewRect.Height() / SceneContext.GetSmallColorDepthDownsampleFactor() :
View.ViewRect.Width() * View.ViewRect.Height();
View.OneOverNumPossiblePixels = NumPossiblePixels > 0.0 ? 1.0f / NumPossiblePixels : 0.0f;
// Still need no jitter to be set for temporal feedback on SSR (it is enabled even when temporal AA is off).
View.TemporalJitterPixelsX = 0.0f;
View.TemporalJitterPixelsY = 0.0f;
if (ViewState)
{
ViewState->SetupDistanceFieldTemporalOffset(ViewFamily);
}
if( View.AntiAliasingMethod == AAM_TemporalAA && ViewState )
{
// Subpixel jitter for temporal AA
int32 TemporalAASamples = CVarTemporalAASamples.GetValueOnRenderThread();
if( TemporalAASamples > 1 && View.bAllowTemporalJitter )
{
float SampleX, SampleY;
if (Scene->GetFeatureLevel() < ERHIFeatureLevel::SM4)
{
// Only support 2 samples for mobile temporal AA.
TemporalAASamples = 2;
}
if( TemporalAASamples == 2 )
{
#if 0
// 2xMSAA
// Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx
// N.
// .S
float SamplesX[] = { -4.0f/16.0f, 4.0/16.0f };
float SamplesY[] = { -4.0f/16.0f, 4.0/16.0f };
#else
// This pattern is only used for mobile.
// Shift to reduce blur.
float SamplesX[] = { -8.0f/16.0f, 0.0/16.0f };
float SamplesY[] = { /* - */ 0.0f/16.0f, 8.0/16.0f };
#endif
ViewState->OnFrameRenderingSetup(ARRAY_COUNT(SamplesX), ViewFamily);
uint32 Index = ViewState->GetCurrentTemporalAASampleIndex();
SampleX = SamplesX[ Index ];
SampleY = SamplesY[ Index ];
}
else if( TemporalAASamples == 3 )
{
// 3xMSAA
// A..
// ..B
// .C.
// Rolling circle pattern (A,B,C).
float SamplesX[] = { -2.0f/3.0f, 2.0/3.0f, 0.0/3.0f };
float SamplesY[] = { -2.0f/3.0f, 0.0/3.0f, 2.0/3.0f };
ViewState->OnFrameRenderingSetup(ARRAY_COUNT(SamplesX), ViewFamily);
uint32 Index = ViewState->GetCurrentTemporalAASampleIndex();
SampleX = SamplesX[ Index ];
SampleY = SamplesY[ Index ];
}
else if( TemporalAASamples == 4 )
{
// 4xMSAA
// Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx
// .N..
// ...E
// W...
// ..S.
// Rolling circle pattern (N,E,S,W).
float SamplesX[] = { -2.0f/16.0f, 6.0/16.0f, 2.0/16.0f, -6.0/16.0f };
float SamplesY[] = { -6.0f/16.0f, -2.0/16.0f, 6.0/16.0f, 2.0/16.0f };
ViewState->OnFrameRenderingSetup(ARRAY_COUNT(SamplesX), ViewFamily);
uint32 Index = ViewState->GetCurrentTemporalAASampleIndex();
SampleX = SamplesX[ Index ];
SampleY = SamplesY[ Index ];
}
else if( TemporalAASamples == 5 )
{
// Compressed 4 sample pattern on same vertical and horizontal line (less temporal flicker).
// Compressed 1/2 works better than correct 2/3 (reduced temporal flicker).
// . N .
// W . E
// . S .
// Rolling circle pattern (N,E,S,W).
float SamplesX[] = { 0.0f/2.0f, 1.0/2.0f, 0.0/2.0f, -1.0/2.0f };
float SamplesY[] = { -1.0f/2.0f, 0.0/2.0f, 1.0/2.0f, 0.0/2.0f };
ViewState->OnFrameRenderingSetup(ARRAY_COUNT(SamplesX), ViewFamily);
uint32 Index = ViewState->GetCurrentTemporalAASampleIndex();
SampleX = SamplesX[ Index ];
SampleY = SamplesY[ Index ];
}
else
{
ViewState->OnFrameRenderingSetup(TemporalAASamples, ViewFamily);
uint32 Index = ViewState->GetCurrentTemporalAASampleIndex();
float u1 = Halton( Index + 1, 2 );
float u2 = Halton( Index + 1, 3 );
// Generates samples in normal distribution
// exp( x^2 / Sigma^2 )
static auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.TemporalAAFilterSize"));
float FilterSize = CVar->GetFloat();
// Scale distribution to set non-unit variance
// Variance = Sigma^2
float Sigma = 0.47f * FilterSize;
// Window to [-0.5, 0.5] output
// Without windowing we could generate samples far away on the infinite tails.
float OutWindow = 0.5f;
float InWindow = FMath::Exp( -0.5 * FMath::Square( OutWindow / Sigma ) );
// Box-Muller transform
float Theta = 2.0f * PI * u2;
float r = Sigma * FMath::Sqrt( -2.0f * FMath::Loge( (1.0f - u1) * InWindow + u1 ) );
SampleX = r * FMath::Cos( Theta );
SampleY = r * FMath::Sin( Theta );
}
View.TemporalJitterPixelsX = SampleX;
View.TemporalJitterPixelsY = SampleY;
View.ViewMatrices.HackAddTemporalAAProjectionJitter(FVector2D(SampleX * 2.0f / View.ViewRect.Width(), SampleY * -2.0f / View.ViewRect.Height()));
}
.....
//设置视口矩阵,包括视口投影矩阵和转换矩阵
ViewState->PrevViewMatrices.ApplyWorldOffset(View.OriginOffsetThisFrame);
ViewState->PendingPrevViewMatrices.ApplyWorldOffset(View.OriginOffsetThisFrame);
......
}
2,可见性计算
void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList)
{
。。。。。。
//(1)初始化一系列用于可视化检测的缓冲区,(位数组,0和1代表是否可见
int32 NumPrimitives = Scene->Primitives.Num();
FPrimitiveViewMasks HasDynamicMeshElementsMasks;
HasDynamicMeshElementsMasks.AddZeroed(NumPrimitives);
FPrimitiveViewMasks HasDynamicEditorMeshElementsMasks;
HasDynamicEditorMeshElementsMasks.AddZeroed(NumPrimitives);
uint8 ViewBit = 0x1;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex, ViewBit <<= 1)
{
FViewInfo& View = Views[ViewIndex];
......
View.PrimitiveVisibilityMap.Init(false,Scene->Primitives.Num());
。。。。。。
//(2)先用六棱锥进行筛选(a,b代表true或者false),不可见的对象的可视化检测缓冲区对应的比特位设置为0
int32 NumCulledPrimitivesForView = FrustumCull<true, false>(Scene, View);
........
//(3)对过小的线框直接剔除
//线框模式下,剔除所有的非线框
if (View.Family->EngineShowFlags.Wireframe)
{
float ScreenSizeScale = FMath::Max(View.ViewMatrices.GetProjectionMatrix().M[0][0] * View.ViewRect.Width(), View.ViewMatrices.GetProjectionMatrix().M[1][1] * View.ViewRect.Height());
for (FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap); BitIt; ++BitIt)
{
if (ScreenSizeScale * Scene->PrimitiveBounds[BitIt.GetIndex()].BoxSphereBounds.SphereRadius <= GWireframeCullThreshold)
{
View.PrimitiveVisibilityMap.AccessCorrespondingBit(BitIt) = false;
}
}
}
//(4)在非线框模式下,对于处于视口范围内,但是被其他对象遮挡的对象进行一次剔除
if (!View.Family->EngineShowFlags.Wireframe)
{
int32 NumOccludedPrimitivesInView = OcclusionCull(RHICmdList, Scene, View);
STAT(NumOccludedPrimitives += NumOccludedPrimitivesInView);
}
//(5)根据所有的可见性位图,设置每个需要渲染的对象的可见性状况,即Hiddenflags
FLODSceneTree& HLODTree = Scene->SceneLODHierarchy;
if (HLODTree.IsActive())
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_ViewVisibilityTime_HLOD);
HLODTree.UpdateAndApplyVisibilityStates(View);
}
。。。。
//(6)给每个对象返回自己是否可见的机会
if (FPlatformProperties::SupportsWindowedMode() && ViewState && ViewState->IsViewParent())
{
for (FSceneDualSetBitIterator BitIt(View.PrimitiveVisibilityMap, View.PrimitiveDefinitelyUnoccludedMap); BitIt; ++BitIt)
{
ViewState->ParentPrimitives.Add(Scene->PrimitiveComponentIds[BitIt.GetIndex()]);
}
}
。。。。。。
//(7)获取所有动态对象的渲染信息
GatherDynamicMeshElements(Views, Scene, ViewFamily, HasDynamicMeshElementsMasks, HasDynamicEditorMeshElementsMasks, MeshCollector);
.......
}
2.1对于第(2)步,剔除方式是Parrallefor线性剔除,而不是八叉树等树状结构
template<bool UseCustomCulling, bool bAlsoUseSphereTest>
static int32 FrustumCull(const FScene* Scene, FViewInfo& View)
{
.....
ParallelFor(NumTasks,
[&NumCulledPrimitives, Scene, &View, MaxDrawDistanceScale](int32 TaskIndex)
{
...
for (int32 WordIndex = TaskWordOffset; WordIndex < TaskWordOffset + FrustumCullNumWordsPerTask && WordIndex * NumBitsPerDWORD < BitArrayNumInner; WordIndex++)
{
.....
for (int32 BitSubIndex = 0; BitSubIndex < NumBitsPerDWORD && WordIndex * NumBitsPerDWORD + BitSubIndex < BitArrayNumInner; BitSubIndex++, Mask <<= 1)
{
....
if (DistanceSquared > FMath::Square(MaxDrawDistance + FadeRadius) ||
(DistanceSquared < Bounds.MinDrawDistanceSq) ||
(UseCustomCulling && !View.CustomVisibilityQuery->IsVisible(VisibilityId, FBoxSphereBounds(Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.BoxExtent, Bounds.BoxSphereBounds.SphereRadius))) ||
(bAlsoUseSphereTest && View.ViewFrustum.IntersectSphere(Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.SphereRadius) == false) ||
View.ViewFrustum.IntersectBox(Bounds.BoxSphereBounds.Origin, Bounds.BoxSphereBounds.BoxExtent) == false ||
(UseMonoCulling && Scene->Primitives[Index]->Proxy->RenderInMono()))
{
STAT(NumCulledPrimitives.Increment());
}
....
return NumCulledPrimitives.GetValue();
}
3,完成可见性计算
void FSceneRenderer::PostVisibilityFrameSetup(FILCUpdatePrimTaskData& OutILCTaskData)
{
.......
//(1)对半透明对象进行排序,半透明对象的渲染由于涉及到互相遮挡,则必须按照从后往前的顺序来渲染,才能保证渲染结果的正确性,因此,必须在此时完成排序
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
View.TranslucentPrimSet.SortPrimitives();
View.MeshDecalPrimSet.SortPrimitives();
if (View.State)
{
((FSceneViewState*)View.State)->TrimHistoryRenderTargets(Scene);
}
}
.....
//(2)对每个光照确定当前光照可见的对象列表,这里也是使用平截头体剔除,只需要测点光源和聚光灯,而平行光始终可见
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
const FLightSceneProxy* Proxy = LightSceneInfo->Proxy;
FViewInfo& View = Views[ViewIndex];
FVisibleLightViewInfo& VisibleLightViewInfo = View.VisibleLightInfos[LightIt.GetIndex()];
if (Proxy->GetLightType() == LightType_Point
|| Proxy->GetLightType() == LightType_Spot)
{
const float Radius = Proxy->GetRadius();
if (View.ViewFrustum.IntersectSphere(Proxy->GetOrigin(), Radius))
{
if (View.IsPerspectiveProjection())
{
FSphere Bounds = Proxy->GetBoundingSphere();
float DistanceSquared = (Bounds.Center - View.ViewMatrices.GetViewOrigin()).SizeSquared();
float MaxDistSquared = Proxy->GetMaxDrawDistance() * Proxy->GetMaxDrawDistance() * GLightMaxDrawDistanceScale * GLightMaxDrawDistanceScale;
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;
....
//(3)初始化雾与大气的常量值
InitFogConstants();
具体实现
void FSceneRenderer::InitFogConstants()
{
float FogDensityOverride = -1.0f;
float FogStartDistanceOverride = -1.
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
InitAtmosphereConstantsInView(View);
// set fog consts based on height fog components
if(ShouldRenderFog(*View.Family))
{
...
到这里为止,进行了重分配渲染目标和初始化视口两部分。估计看到这里,自己都要迷糊了。
总结下;
延迟渲染
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
一,按需要重新分配渲染目标
1,void FSceneRenderTargets::AllocDummyGBufferTargets(FRHICommandList& RHICmdList)
1.1, template<typename InitializerType,typename RHIRefType,typename RHIParamRefType> static RHIParamRefType TStaticStateRHI<typename InitializerType,typename RHIRefType,typename RHIParamRefType>::GetRHI();
1.2,FGBufferResourceStruct::CreateUniformBuffer(GBufferResourceStruct, UniformBuffer_SingleFrame);
二,初始化视口(可以多个),包括确定可见性与设置阴影相关参数
1,bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, struct FILCUpdatePrimTaskData& ILCTaskData, FGraphEventArray& SortEvents)
1.1,预设置可见性
void FSceneRenderer::PreVisibilityFrameSetup(FRHICommandListImmediate& RHICmdList)
1.1.1,根据当前画质设置,设置TemporalAA的采样方式,同时确定采样位置,
1.1.1.1, uint32 FSceneViewState::GetCurrentTemporalAASampleIndex() const
1.1.1.2,void FViewMatrices::HackAddTemporalAAProjectionJitter(const FVector2D& InTemporalAAProjectionJitter)
1.1.2,设置视口矩阵,包括视口投影矩阵和转换矩阵
void FViewMatrices::ApplyWorldOffset(const FVector& InOffset)
1.2,可见性计算
void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList)
1.2.1,初始化一系列用于可视化检测的缓冲区,(位数组,0和1代表是否可见)
1.2.2,用六棱锥进行筛选(a,b代表true或者false),不可见的对象的可视化检测缓冲区对应的比特位设置为0
template<bool UseCustomCulling, bool bAlsoUseSphereTest>
static int32 FrustumCull(const FScene* Scene, FViewInfo& View)
1.2.3,线框模式下,对直接剔除,剔除所有的非线框和过小的线框
1.2.4,在非线框模式下,对于处于视口范围内,但是被其他对象遮挡的对象进行一次剔除
static int32 OcclusionCull(FRHICommandListImmediate& RHICmdList, const FScene* Scene, FViewInfo& View)
1.2.5,根据所有的可见性位图,设置每个需要渲染的对象的可见性状况,即Hiddenflags
void FLODSceneTree::UpdateAndApplyVisibilityStates(FViewInfo& View)
1.2.6,给每个对象返回自己是否可见的机会
1.2.7,获取所有动态对象的渲染信息
void FSceneRenderer::GatherDynamicMeshElements(
TArray<FViewInfo>& InViews,
const FScene* InScene,
const FSceneViewFamily& InViewFamily,
const FPrimitiveViewMasks& HasDynamicMeshElementsMasks,
const FPrimitiveViewMasks& HasDynamicEditorMeshElementsMasks,
FMeshElementCollector& Collector)
1.3,完成可见性计算
void FSceneRenderer::PostVisibilityFrameSetup(FILCUpdatePrimTaskData& OutILCTaskData)
1.3.1,对半透明对象进行排序,半透明对象的渲染由于涉及到互相遮挡,则必须按照从后往前的顺序来渲染,才能保 证渲染结果的正确性,因此,必须在此时完成排序
1.3.1.1,void FTranslucentPrimSet::SortPrimitives()
1.3.1.2,void FSceneViewState::TrimHistoryRenderTargets(const FScene* Scene)
1.3.2,对每个光照确定当前光照可见的对象列表,这里也是使用平截头体剔除,只需要测点光源和聚光灯,而平行光始 终可见
bool FConvexVolume::IntersectSphere(const FVector& Origin,const float& Radius) const
1.3.3,初始化雾与大气的常量值
void FSceneRenderer::InitFogConstants()