UE4的渲染模塊是一個獨(dú)立的模塊诈乒,這篇文章從該模塊的設(shè)計(jì)理念和思路進(jìn)行剖析郎哭。 通常渲染模塊由如下幾個部分組成:
- 場景的描述
- 場景遍歷和揀選
- 渲染的執(zhí)行
場景的描述
UE4場景管理相關(guān)的數(shù)據(jù)結(jié)構(gòu)如下:
- FScene 場景類
- FPrimitiveSceneProxy 場景里的幾何體類
- FPrimitiveSceneInfo 場景里的結(jié)點(diǎn)(擁有幾何體和狀態(tài)信息)
每個幾何體具有材質(zhì)屬性缰泡,相關(guān)的數(shù)據(jù)結(jié)構(gòu)如下:
- FMaterial 材質(zhì)接口類跌帐,提供材質(zhì)屬性的查詢(eg. blend mode)和shader查找谦纱。
- FMaterialResoruce UMaterial實(shí)現(xiàn)的具體的FMaterial
- FMaterialRenderProxy 渲染線程用的Material對象打掘,提供FMaterial的訪問和材質(zhì)參數(shù)的訪問(eg. scaler, vector, texture parameter等參數(shù))华畏。
場景的遍歷和揀選
在簡單的3D渲染引擎中,通常的做法是:遍歷場景結(jié)點(diǎn)尊蚁,根據(jù)視景體進(jìn)行判斷該結(jié)點(diǎn)是否可見亡笑,如果可見則保存到渲染隊(duì)列中,否則忽略之横朋。最后對渲染隊(duì)列中的結(jié)點(diǎn)進(jìn)行按材質(zhì)排序仑乌,然后繪制它們。
在UE4中琴锭,使用了不同于上面的方式進(jìn)行處理晰甚,它對幾何體進(jìn)行分類處理(Static Primitive和Dynamic Primitive)。
Static Render Path
在FScene對象中存在一些static draw list,在PrimitiveSceneProxy被插入場景中時祠够,會通過調(diào)用FPrimitveSceneProxy::DrawStaticElements()來收集FStaticMeshElements數(shù)據(jù)压汪。 然后創(chuàng)建相應(yīng)的drawing policy對象實(shí)例放入到draw list中去。這個drawing policy對象是按照材質(zhì)排序放入到draw list中的古瓤。Dynamic Render Path
對于動態(tài)渲染路徑止剖,InitViews在判定某個PrimitiveScenceProxy是可見后,通過傳入TDynamicPrimitiveDrawer對象調(diào)用FPrimitiveSceneProxy::DrawDynamicElements()來收集FMeshElements,供以后的渲染使用落君。
上面的兩種渲染路徑并不沖突穿香,一個FPrimitiveSceneProxy可以實(shí)現(xiàn)DrawStaticElements()和DrawDynamicElements()來同時支持它們,也就是說這個SceneProxy既有Static FMeshElements又有Dynamic FMeshElements绎速。
可見性判定####
參考代碼:
bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, struct FILCUpdatePrimTaskData& ILCTaskData, FGraphEventArray& SortEvents)
void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList)
template<bool UseCustomCulling, bool bAlsoUseSphereTest>
static int32 FrustumCull(const FScene* Scene, FViewInfo& View)
void FSceneRenderer::GatherDynamicMeshElements(
TArray<FViewInfo>& InViews,
const FScene* InScene,
const FSceneViewFamily& InViewFamily,
const FPrimitiveViewMasks& HasDynamicMeshElementsMasks,
const FPrimitiveViewMasks& HasDynamicEditorMeshElementsMasks,
FMeshElementCollector& Collector)
重要數(shù)據(jù)結(jié)構(gòu):
- FViewInfo
針對每個ViewPort分配一個FViewInfo對象皮获,這個用于存放裁剪后的幾何體可見性。
渲染的執(zhí)行
對于簡單的渲染引擎纹冤,只會簡單地對可見的幾何體執(zhí)行渲染(設(shè)置渲染狀態(tài)洒宝、GPU Shader和參數(shù)购公、發(fā)射Draw指令),而UE4的渲染比較復(fù)雜雁歌,進(jìn)行多pass繪制宏浩,下面列出它的各個pass順序并逐一介紹.
- PASS_0: PrePass/Depth Only Pass
bool FDeferredShadingSceneRenderer::RenderPrePassView(FRHICommandList& RHICmdList, const FViewInfo& View)
bool RenderPrePassViewDynamic(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState);
該pass使用FDepthDrawingPolicy策略進(jìn)行繪制,只繪制depth到Depth-Buffer靠瞎,這個有利于減少后面的Base pass中的pixel填充比庄,節(jié)省pixel-shader的執(zhí)行。
下面分析一下RenderPrePassViewDynamic()乏盐,驗(yàn)證上述的Dynamic Render Path的機(jī)制佳窑。
bool FDeferredShadingSceneRenderer::RenderPrePassViewDynamic(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState)
{
FDepthDrawingPolicyFactory::ContextType Context(EarlyZPassMode, true);
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.DynamicMeshElements.Num(); MeshBatchIndex++)
{
const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicMeshElements[MeshBatchIndex];
if (MeshBatchAndRelevance.GetHasOpaqueOrMaskedMaterial() && MeshBatchAndRelevance.GetRenderInMainPass())
{
const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatchAndRelevance.PrimitiveSceneProxy;
bool bShouldUseAsOccluder = true;
if (EarlyZPassMode < DDM_AllOccluders)
{
extern float GMinScreenRadiusForDepthPrepass;
//@todo - move these proxy properties into FMeshBatchAndRelevance so we don't have to dereference the proxy in order to reject a mesh
const float LODFactorDistanceSquared = (PrimitiveSceneProxy->GetBounds().Origin - View.ViewMatrices.GetViewOrigin()).SizeSquared() * FMath::Square(View.LODDistanceFactor);
// Only render primitives marked as occluders
bShouldUseAsOccluder = PrimitiveSceneProxy->ShouldUseAsOccluder()
// Only render static objects unless movable are requested
&& (!PrimitiveSceneProxy->IsMovable() || bEarlyZPassMovable)
&& (FMath::Square(PrimitiveSceneProxy->GetBounds().SphereRadius) > GMinScreenRadiusForDepthPrepass * GMinScreenRadiusForDepthPrepass * LODFactorDistanceSquared);
}
if (bShouldUseAsOccluder)
{
FDepthDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, Context, MeshBatch, true, DrawRenderState, PrimitiveSceneProxy, MeshBatch.BatchHitProxyId, View.IsInstancedStereoPass());
}
}
}
return true;
}
bool FDepthDrawingPolicyFactory::DrawDynamicMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
bool bPreFog,
const FDrawingPolicyRenderState& DrawRenderState,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
const bool bIsInstancedStereo,
const bool bIsInstancedStereoEmulated
)
{
FScopedStrictGraphicsPipelineStateUse UsePSOOnly(RHICmdList);
return DrawMesh(
RHICmdList,
View,
DrawingContext,
Mesh,
Mesh.Elements.Num()==1 ? 1 : (1<<Mesh.Elements.Num())-1, // 1 bit set for each mesh element
DrawRenderState,
bPreFog,
PrimitiveSceneProxy,
HitProxyId,
bIsInstancedStereo,
bIsInstancedStereoEmulated
);
}
bool FDepthDrawingPolicyFactory::DrawMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
const uint64& BatchElementMask,
const FDrawingPolicyRenderState& DrawRenderState,
bool bPreFog,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
const bool bIsInstancedStereo,
const bool bIsInstancedStereoEmulated
)
{
bool bDirty = false;
//Do a per-FMeshBatch check on top of the proxy check in RenderPrePass to handle the case where a proxy that is relevant
//to the depth only pass has to submit multiple FMeshElements but only some of them should be used as occluders.
if (Mesh.bUseAsOccluder || !DrawingContext.bRespectUseAsOccluderFlag || DrawingContext.DepthDrawingMode == DDM_AllOpaque)
{
const FMaterialRenderProxy* MaterialRenderProxy = Mesh.MaterialRenderProxy;
const FMaterial* Material = MaterialRenderProxy->GetMaterial(View.GetFeatureLevel());
const EBlendMode BlendMode = Material->GetBlendMode();
const bool bUsesMobileColorValue = (DrawingContext.MobileColorValue != 0.0f);
// Check to see if the primitive is currently fading in or out using the screen door effect. If it is,
// then we can't assume the object is opaque as it may be forcibly masked.
const FSceneViewState* SceneViewState = static_cast<const FSceneViewState*>( View.State );
FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(Mesh);
OverrideSettings.MeshOverrideFlags |= Material->IsTwoSided() ? EDrawingPolicyOverrideFlags::TwoSided : EDrawingPolicyOverrideFlags::None;
if ( BlendMode == BLEND_Opaque
&& Mesh.VertexFactory->SupportsPositionOnlyStream()
&& !Material->MaterialModifiesMeshPosition_RenderThread()
&& Material->WritesEveryPixel()
&& !bUsesMobileColorValue
)
{
//render opaque primitives that support a separate position-only vertex buffer
const FMaterialRenderProxy* DefaultProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(false);
OverrideSettings.MeshOverrideFlags |= Material->IsWireframe() ? EDrawingPolicyOverrideFlags::Wireframe : EDrawingPolicyOverrideFlags::None;
// 臨時對象 DrawingPolicy.
FPositionOnlyDepthDrawingPolicy DrawingPolicy(
Mesh.VertexFactory,
DefaultProxy,
*DefaultProxy->GetMaterial(View.GetFeatureLevel()),
OverrideSettings
);
FDrawingPolicyRenderState DrawRenderStateLocal(&RHICmdList, DrawRenderState);
DrawingPolicy.SetSharedState(RHICmdList, &View, FPositionOnlyDepthDrawingPolicy::ContextDataType(bIsInstancedStereo, bIsInstancedStereoEmulated), DrawRenderStateLocal);
int32 BatchElementIndex = 0;
uint64 Mask = BatchElementMask;
do
{
if(Mask & 1)
{
// We draw instanced static meshes twice when rendering with instanced stereo. Once for each eye.
const bool bIsInstancedMesh = Mesh.Elements[BatchElementIndex].bIsInstancedMesh;
const uint32 InstancedStereoDrawCount = (bIsInstancedStereo && bIsInstancedMesh) ? 2 : 1;
for (uint32 DrawCountIter = 0; DrawCountIter < InstancedStereoDrawCount; ++DrawCountIter)
{
DrawingPolicy.SetInstancedEyeIndex(RHICmdList, DrawCountIter);
TDrawEvent<FRHICommandList> MeshEvent;
BeginMeshDrawEvent(RHICmdList, PrimitiveSceneProxy, Mesh, MeshEvent);
DrawingPolicy.SetMeshRenderState(RHICmdList, View, PrimitiveSceneProxy, Mesh, BatchElementIndex, DrawRenderStateLocal, FPositionOnlyDepthDrawingPolicy::ElementDataType(), FPositionOnlyDepthDrawingPolicy::ContextDataType());
DrawingPolicy.DrawMesh(RHICmdList, Mesh, BatchElementIndex, bIsInstancedStereo);
}
}
Mask >>= 1;
BatchElementIndex++;
} while(Mask);
bDirty = true;
}
else if (!IsTranslucentBlendMode(BlendMode) || Material->IsTranslucencyWritingCustomDepth())
{
const bool bMaterialMasked = !Material->WritesEveryPixel() || Material->IsTranslucencyWritingCustomDepth();
bool bDraw = true;
switch(DrawingContext.DepthDrawingMode)
{
case DDM_AllOpaque:
break;
case DDM_AllOccluders:
break;
case DDM_NonMaskedOnly:
bDraw = !bMaterialMasked;
break;
default:
check(!"Unrecognized DepthDrawingMode");
}
if(bDraw)
{
if (!bMaterialMasked && !Material->MaterialModifiesMeshPosition_RenderThread())
{
// Override with the default material for opaque materials that are not two sided
MaterialRenderProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy(false);
}
FDepthDrawingPolicy DrawingPolicy(
Mesh.VertexFactory,
MaterialRenderProxy,
*MaterialRenderProxy->GetMaterial(View.GetFeatureLevel()),
OverrideSettings,
View.GetFeatureLevel(),
DrawingContext.MobileColorValue
);
FDrawingPolicyRenderState DrawRenderStateLocal(&RHICmdList, DrawRenderState);
DrawingPolicy.SetSharedState(RHICmdList, &View, FDepthDrawingPolicy::ContextDataType(bIsInstancedStereo, bIsInstancedStereoEmulated), DrawRenderStateLocal);
int32 BatchElementIndex = 0;
uint64 Mask = BatchElementMask;
do
{
if(Mask & 1)
{
// We draw instanced static meshes twice when rendering with instanced stereo. Once for each eye.
const bool bIsInstancedMesh = Mesh.Elements[BatchElementIndex].bIsInstancedMesh;
const uint32 InstancedStereoDrawCount = (bIsInstancedStereo && bIsInstancedMesh) ? 2 : 1;
for (uint32 DrawCountIter = 0; DrawCountIter < InstancedStereoDrawCount; ++DrawCountIter)
{
DrawingPolicy.SetInstancedEyeIndex(RHICmdList, DrawCountIter);
TDrawEvent<FRHICommandList> MeshEvent;
BeginMeshDrawEvent(RHICmdList, PrimitiveSceneProxy, Mesh, MeshEvent);
DrawingPolicy.SetMeshRenderState(RHICmdList, View, PrimitiveSceneProxy, Mesh, BatchElementIndex, DrawRenderStateLocal, FMeshDrawingPolicy::ElementDataType(), FDepthDrawingPolicy::ContextDataType());
DrawingPolicy.DrawMesh(RHICmdList, Mesh, BatchElementIndex, bIsInstancedStereo);
}
}
Mask >>= 1;
BatchElementIndex++;
} while(Mask);
bDirty = true;
}
}
}
return bDirty;
}
-
PASS_1: Base pass
該pass繪制不透明的和masked material的屬性的幾何體,輸入材質(zhì)屬性到G-Buffer; 同時計(jì)算Lightmap和sky lighting的貢獻(xiàn)量到scene color buffer中父能。下面羅列出相關(guān)函數(shù)神凑。
/**
* Renders the scene's base pass
* @return true if anything was rendered
*/
bool FDeferredShadingSceneRenderer::RenderBasePass(FRHICommandListImmediate& RHICmdList)
bool FDeferredShadingSceneRenderer::RenderBasePassView(FRHICommandListImmediate& RHICmdList, FViewInfo& View)
{
bool bDirty = false;
FDrawingPolicyRenderState DrawRenderState(&RHICmdList, View);
SetupBasePassView(RHICmdList, View, DrawRenderState, ViewFamily.EngineShowFlags.ShaderComplexity);
// 繪制靜態(tài)幾何體
bDirty |= RenderBasePassStaticData(RHICmdList, View, DrawRenderState);
// 繪制動態(tài)幾何體
RenderBasePassDynamicData(RHICmdList, View, DrawRenderState, bDirty);
return bDirty;
}
bool FBasePassOpaqueDrawingPolicyFactory::DrawDynamicMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
bool bPreFog,
const FDrawingPolicyRenderState& DrawRenderState,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
const bool bIsInstancedStereo
);
// 使用臨時對象DrawingPolicy
TBasePassDrawingPolicy<LightMapPolicyType> DrawingPolicy(
Parameters.Mesh.VertexFactory,
Parameters.Mesh.MaterialRenderProxy,
*Parameters.Material,
Parameters.FeatureLevel,
LightMapPolicy,
Parameters.BlendMode,
Parameters.TextureMode,
bRenderSkylight,
bRenderAtmosphericFog,
ComputeMeshOverrideSettings(Parameters.Mesh),
View.Family->GetDebugViewShaderMode(),
Parameters.bEditorCompositeDepthTest,
bEnableReceiveDecalOutput
);
-
PASS_2: Issue Occlusion Queries / BeginOcclusionTests
執(zhí)行遮擋查詢,在繪制下一幀時法竞,InitView會使用這些信息進(jìn)行可見性判斷耙厚。遮擋查詢的原理是通過繪制幾何體的包圍盒進(jìn)行z-depth測試强挫,以粗略地判斷該幾何體是否被遮擋岔霸。
void FDeferredShadingSceneRenderer::RenderOcclusion(FRHICommandListImmediate& RHICmdList, bool bRenderQueries, bool bRenderHZB)
-
PASS_3: ShadowMap(陰影計(jì)算)
針對每個光源渲染相應(yīng)的Shadowmap, 光源也被累積到translucency lighting volumes中(這塊不明白,理解估計(jì)有誤)俯渤。
void FSceneRenderer::RenderShadowDepthMaps(FRHICommandListImmediate& RHICmdList);
void FSceneRenderer::RenderShadowDepthMapAtlases(FRHICommandListImmediate& RHICmdList);
具體實(shí)現(xiàn)
void FSceneRenderer::RenderShadowDepthMaps(FRHICommandListImmediate& RHICmdList)
{
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
SCOPED_DRAW_EVENT(RHICmdList, ShadowDepths);
SCOPED_GPU_STAT(RHICmdList, Stat_GPU_ShadowDepths);
FSceneRenderer::RenderShadowDepthMapAtlases(RHICmdList);
for (int32 CubemapIndex = 0; CubemapIndex < SortedShadowsForShadowDepthPass.ShadowMapCubemaps.Num(); CubemapIndex++)
{
const FSortedShadowMapAtlas& ShadowMap = SortedShadowsForShadowDepthPass.ShadowMapCubemaps[CubemapIndex];
FSceneRenderTargetItem& RenderTarget = ShadowMap.RenderTargets.DepthTarget->GetRenderTargetItem();
FIntPoint TargetSize = ShadowMap.RenderTargets.DepthTarget->GetDesc().Extent;
check(ShadowMap.Shadows.Num() == 1);
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMap.Shadows[0];
GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, ShadowMap.RenderTargets.DepthTarget.GetReference());
FString LightNameWithLevel;
GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightNameWithLevel);
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepths, TEXT("Cubemap %s %u^2"), *LightNameWithLevel, TargetSize.X, TargetSize.Y);
auto SetShadowRenderTargets = [this, &RenderTarget, &SceneContext](FRHICommandList& InRHICmdList, bool bPerformClear)
{
FRHISetRenderTargetsInfo Info(0, nullptr, FRHIDepthRenderTargetView(RenderTarget.TargetableTexture,
bPerformClear ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad,
ERenderTargetStoreAction::EStore,
ERenderTargetLoadAction::ELoad,
ERenderTargetStoreAction::EStore));
check(Info.DepthStencilRenderTarget.Texture->GetDepthClearValue() == 1.0f);
Info.ColorRenderTarget[0].StoreAction = ERenderTargetStoreAction::ENoAction;
if (!GSupportsDepthRenderTargetWithoutColorRenderTarget)
{
Info.NumColorRenderTargets = 1;
Info.ColorRenderTarget[0].Texture = SceneContext.GetOptionalShadowDepthColorSurface(InRHICmdList, Info.DepthStencilRenderTarget.Texture->GetTexture2D()->GetSizeX(), Info.DepthStencilRenderTarget.Texture->GetTexture2D()->GetSizeY());
InRHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, Info.ColorRenderTarget[0].Texture);
}
InRHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, Info.DepthStencilRenderTarget.Texture);
InRHICmdList.SetRenderTargetsAndClear(Info);
};
{
bool bDoClear = true;
if (ProjectedShadowInfo->CacheMode == SDCM_MovablePrimitivesOnly
&& Scene->CachedShadowMaps.FindChecked(ProjectedShadowInfo->GetLightSceneInfo().Id).bCachedShadowMapHasPrimitives)
{
// Skip the clear when we'll copy from a cached shadowmap
bDoClear = false;
}
SCOPED_CONDITIONAL_DRAW_EVENT(RHICmdList, Clear, bDoClear);
SetShadowRenderTargets(RHICmdList, bDoClear);
}
ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_Normal);
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, RenderTarget.TargetableTexture);
}
if (SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num() > 0)
{
FSceneRenderTargetItem& RenderTarget = SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget->GetRenderTargetItem();
GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget.GetReference());
SCOPED_DRAW_EVENT(RHICmdList, PreshadowCache);
for (int32 ShadowIndex = 0; ShadowIndex < SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = SortedShadowsForShadowDepthPass.PreshadowCache.Shadows[ShadowIndex];
if (!ProjectedShadowInfo->bDepthsCached)
{
auto SetShadowRenderTargets = [this, ProjectedShadowInfo](FRHICommandList& InRHICmdList, bool bPerformClear)
{
FTextureRHIParamRef PreShadowCacheDepthZ = Scene->PreShadowCacheDepthZ->GetRenderTargetItem().TargetableTexture.GetReference();
InRHICmdList.TransitionResources(EResourceTransitionAccess::EWritable, &PreShadowCacheDepthZ, 1);
// Must preserve existing contents as the clear will be scissored
SetRenderTarget(InRHICmdList, FTextureRHIRef(), PreShadowCacheDepthZ, ESimpleRenderTargetMode::EExistingColorAndDepth);
ProjectedShadowInfo->ClearDepth(InRHICmdList, this, 0, nullptr, PreShadowCacheDepthZ, bPerformClear);
};
SetShadowRenderTargets(RHICmdList, true);
ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_Normal);
ProjectedShadowInfo->bDepthsCached = true;
}
}
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, RenderTarget.TargetableTexture);
}
for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases.Num(); AtlasIndex++)
{
const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases[AtlasIndex];
FIntPoint TargetSize = ShadowMapAtlas.RenderTargets.ColorTargets[0]->GetDesc().Extent;
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepths, TEXT("TranslucencyAtlas%u %u^2"), AtlasIndex, TargetSize.X, TargetSize.Y);
FSceneRenderTargetItem ColorTarget0 = ShadowMapAtlas.RenderTargets.ColorTargets[0]->GetRenderTargetItem();
FSceneRenderTargetItem ColorTarget1 = ShadowMapAtlas.RenderTargets.ColorTargets[1]->GetRenderTargetItem();
FTextureRHIParamRef RenderTargetArray[2] =
{
ColorTarget0.TargetableTexture,
ColorTarget1.TargetableTexture
};
SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargetArray), RenderTargetArray, FTextureRHIParamRef(), 0, NULL, true);
for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex];
ProjectedShadowInfo->RenderTranslucencyDepths(RHICmdList, this);
}
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, ColorTarget0.TargetableTexture);
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, ColorTarget1.TargetableTexture);
}
for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.RSMAtlases.Num(); AtlasIndex++)
{
const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.RSMAtlases[AtlasIndex];
FSceneRenderTargetItem ColorTarget0 = ShadowMapAtlas.RenderTargets.ColorTargets[0]->GetRenderTargetItem();
FSceneRenderTargetItem ColorTarget1 = ShadowMapAtlas.RenderTargets.ColorTargets[1]->GetRenderTargetItem();
FSceneRenderTargetItem DepthTarget = ShadowMapAtlas.RenderTargets.DepthTarget->GetRenderTargetItem();
FIntPoint TargetSize = ShadowMapAtlas.RenderTargets.DepthTarget->GetDesc().Extent;
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepths, TEXT("RSM%u %ux%u"), AtlasIndex, TargetSize.X, TargetSize.Y);
for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex];
FSceneViewState* ViewState = (FSceneViewState*)ProjectedShadowInfo->DependentView->State;
FLightPropagationVolume* LightPropagationVolume = ViewState->GetLightPropagationVolume(FeatureLevel);
// 計(jì)算 FLightPropagationVolume
auto SetShadowRenderTargets = [this, LightPropagationVolume, ProjectedShadowInfo, &ColorTarget0, &ColorTarget1, &DepthTarget](FRHICommandList& InRHICmdList, bool bPerformClear)
{
FTextureRHIParamRef RenderTargets[2];
RenderTargets[0] = ColorTarget0.TargetableTexture;
RenderTargets[1] = ColorTarget1.TargetableTexture;
// Hook up the geometry volume UAVs
FUnorderedAccessViewRHIParamRef Uavs[4];
Uavs[0] = LightPropagationVolume->GetGvListBufferUav();
Uavs[1] = LightPropagationVolume->GetGvListHeadBufferUav();
Uavs[2] = LightPropagationVolume->GetVplListBufferUav();
Uavs[3] = LightPropagationVolume->GetVplListHeadBufferUav();
InRHICmdList.TransitionResources(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EGfxToGfx, Uavs, ARRAY_COUNT(Uavs));
SetRenderTargets(InRHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, DepthTarget.TargetableTexture, ARRAY_COUNT(Uavs), Uavs);
ProjectedShadowInfo->ClearDepth(InRHICmdList, this, ARRAY_COUNT(RenderTargets), RenderTargets, DepthTarget.TargetableTexture, bPerformClear);
};
{
SCOPED_DRAW_EVENT(RHICmdList, Clear);
SetShadowRenderTargets(RHICmdList, true);
}
LightPropagationVolume->SetVplInjectionConstants(*ProjectedShadowInfo, ProjectedShadowInfo->GetLightSceneInfo().Proxy);
ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_Normal);
// Render emissive only meshes as they are held in a separate list.
ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_EmissiveOnly);
// Render gi blocking volume meshes.
ProjectedShadowInfo->RenderDepth(RHICmdList, this, SetShadowRenderTargets, ShadowDepthRenderMode_GIBlockingVolumes);
{
// Resolve the shadow depth z surface.
RHICmdList.CopyToResolveTarget(DepthTarget.TargetableTexture, DepthTarget.ShaderResourceTexture, false, FResolveParams());
RHICmdList.CopyToResolveTarget(ColorTarget0.TargetableTexture, ColorTarget0.ShaderResourceTexture, false, FResolveParams());
RHICmdList.CopyToResolveTarget(ColorTarget1.TargetableTexture, ColorTarget1.ShaderResourceTexture, false, FResolveParams());
FUnorderedAccessViewRHIParamRef UavsToReadable[2];
UavsToReadable[0] = LightPropagationVolume->GetGvListBufferUav();
UavsToReadable[1] = LightPropagationVolume->GetGvListHeadBufferUav();
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EGfxToGfx, UavsToReadable, ARRAY_COUNT(UavsToReadable));
// Unset render targets
FTextureRHIParamRef RenderTargets[2] = {NULL};
FUnorderedAccessViewRHIParamRef Uavs[2] = {NULL};
SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIParamRef(), ARRAY_COUNT(Uavs), Uavs);
}
}
}
}
-
PASS_4: Lighting(光照計(jì)算)
分為如下子階段:
- Pre-lighting composition lighting stage
預(yù)處理組合型光照(eg. deferred decals, SSAO) - Render lights
光照計(jì)算
參考代碼如下:
// Pre-lighting composition lighting stage
// e.g. deferred decals, SSAO
if (FeatureLevel >= ERHIFeatureLevel::SM4)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_AfterBasePass);
GRenderTargetPool.AddPhaseEvent(TEXT("AfterBasePass"));
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex);
GCompositionLighting.ProcessAfterBasePass(RHICmdList, Views[ViewIndex]);
}
ServiceLocalQueue();
}
// TODO: Could entirely remove this by using STENCIL_SANDBOX_BIT in ShadowRendering.cpp and DistanceFieldSurfaceCacheLighting.cpp
if (!IsForwardShadingEnabled(FeatureLevel))
{
SCOPED_DRAW_EVENT(RHICmdList, ClearStencilFromBasePass);
FRHISetRenderTargetsInfo Info(0, NULL, FRHIDepthRenderTargetView(
SceneContext.GetSceneDepthSurface(),
ERenderTargetLoadAction::ENoAction,
ERenderTargetStoreAction::ENoAction,
ERenderTargetLoadAction::EClear,
ERenderTargetStoreAction::EStore,
FExclusiveDepthStencil::DepthNop_StencilWrite));
// Clear stencil to 0 now that deferred decals are done using what was setup in the base pass
// Shadow passes and other users of stencil assume it is cleared to 0 going in
RHICmdList.SetRenderTargetsAndClear(Info);
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, SceneContext.GetSceneDepthSurface());
}
// Render lighting.
if (ViewFamily.EngineShowFlags.Lighting
&& FeatureLevel >= ERHIFeatureLevel::SM4
&& ViewFamily.EngineShowFlags.DeferredLighting
&& bUseGBuffer)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Lighting);
GRenderTargetPool.AddPhaseEvent(TEXT("Lighting"));
// These modulate the scenecolor output from the basepass, which is assumed to be indirect lighting
RenderIndirectCapsuleShadows(
RHICmdList,
SceneContext.GetSceneColorSurface(),
SceneContext.bScreenSpaceAOIsValid ? SceneContext.ScreenSpaceAO->GetRenderTargetItem().TargetableTexture : NULL);
TRefCountPtr<IPooledRenderTarget> DynamicBentNormalAO;
// These modulate the scenecolor output from the basepass, which is assumed to be indirect lighting
RenderDFAOAsIndirectShadowing(RHICmdList, VelocityRT, DynamicBentNormalAO);
// Clear the translucent lighting volumes before we accumulate
if ((GbEnableAsyncComputeTranslucencyLightingVolumeClear && GSupportsEfficientAsyncCompute) == false)
{
ClearTranslucentVolumeLighting(RHICmdList);
}
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Lighting));
RenderLights(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterLighting));
ServiceLocalQueue();
GRenderTargetPool.AddPhaseEvent(TEXT("AfterRenderLights"));
InjectAmbientCubemapTranslucentVolumeLighting(RHICmdList);
ServiceLocalQueue();
// Filter the translucency lighting volume now that it is complete
FilterTranslucentVolumeLighting(RHICmdList);
ServiceLocalQueue();
// Pre-lighting composition lighting stage
// e.g. LPV indirect
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
if(IsLpvIndirectPassRequired(View))
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView,Views.Num() > 1, TEXT("View%d"), ViewIndex);
GCompositionLighting.ProcessLpvIndirect(RHICmdList, View);
ServiceLocalQueue();
}
}
RenderDynamicSkyLighting(RHICmdList, VelocityRT, DynamicBentNormalAO);
ServiceLocalQueue();
// SSS need the SceneColor finalized as an SRV.
SceneContext.FinishRenderingSceneColor(RHICmdList);
// Render reflections that only operate on opaque pixels
RenderDeferredReflections(RHICmdList, DynamicBentNormalAO, VelocityRT);
ServiceLocalQueue();
// Post-lighting composition lighting stage
// e.g. ScreenSpaceSubsurfaceScattering
for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView,Views.Num() > 1, TEXT("View%d"), ViewIndex);
GCompositionLighting.ProcessAfterLighting(RHICmdList, Views[ViewIndex]);
}
ServiceLocalQueue();
}
FLightShaftsOutput LightShaftOutput;
// Draw Lightshafts
if (ViewFamily.EngineShowFlags.LightShafts)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderLightShaftOcclusion);
RenderLightShaftOcclusion(RHICmdList, LightShaftOutput);
ServiceLocalQueue();
}
-
PASS_5: Draw atmosphere
對非透明表面繪制大氣效果.
// Draw atmosphere
if (ShouldRenderAtmosphere(ViewFamily))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderAtmosphere);
if (Scene->AtmosphericFog)
{
// Update RenderFlag based on LightShaftTexture is valid or not
if (LightShaftOutput.LightShaftOcclusion)
{
Scene->AtmosphericFog->RenderFlag &= EAtmosphereRenderFlag::E_LightShaftMask;
}
else
{
Scene->AtmosphericFog->RenderFlag |= EAtmosphereRenderFlag::E_DisableLightShaft;
}
#if WITH_EDITOR
if (Scene->bIsEditorScene)
{
// Precompute Atmospheric Textures
Scene->AtmosphericFog->PrecomputeTextures(RHICmdList, Views.GetData(), &ViewFamily);
}
#endif
RenderAtmosphere(RHICmdList, LightShaftOutput);
ServiceLocalQueue();
}
}
負(fù)責(zé)該效果函數(shù)
void FDeferredShadingSceneRenderer::RenderAtmosphere(FRHICommandListImmediate& RHICmdList, const FLightShaftsOutput& LightShaftsOutput)
-
PASS_6: Draw Fog
針對非透明表面逐像素就算Fog.
// Draw fog.
if (ShouldRenderFog(ViewFamily))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFog);
RenderFog(RHICmdList, LightShaftOutput);
ServiceLocalQueue();
}
bool FDeferredShadingSceneRenderer::RenderFog(FRHICommandListImmediate& RHICmdList, const FLightShaftsOutput& LightShaftsOutput)
-
PASS_7: Draw translucency
繪制半透明幾何體.
Translucency is accumulated into an offscreen render target where it has fogging applied per-vertex so it can integrate into the scene. Lit translucency computes final lighting in a single pass to blend correctly.
// Draw translucency.
if (ViewFamily.EngineShowFlags.Translucency)
{
SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_Translucency));
RenderTranslucency(RHICmdList);
ServiceLocalQueue();
if(GetRefractionQuality(ViewFamily) > 0)
{
// To apply refraction effect by distorting the scene color.
// After non separate translucency as that is considered at scene depth anyway
// It allows skybox translucency (set to non separate translucency) to be refracted.
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_RenderDistortion));
RenderDistortion(RHICmdList);
ServiceLocalQueue();
}
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterTranslucency));
}
void FDeferredShadingSceneRenderer::RenderTranslucency(FRHICommandListImmediate& RHICmdList)
-
PASS_8: Post Processing
繪制后處理效果
// Resolve the scene color for post processing.
SceneContext.ResolveSceneColor(RHICmdList, FResolveRect(0, 0, ViewFamily.FamilySizeX, ViewFamily.FamilySizeY));
GetRendererModule().RenderPostResolvedSceneColorExtension(RHICmdList, SceneContext);
CopySceneCaptureComponentToTarget(RHICmdList);
// Finish rendering for each view.
if (ViewFamily.bResolveScene)
{
SCOPED_DRAW_EVENT(RHICmdList, PostProcessing);
SCOPED_GPU_STAT(RHICmdList, Stat_GPU_Postprocessing);
SCOPE_CYCLE_COUNTER(STAT_FinishRenderViewTargetTime);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_PostProcessing));
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex);
// 執(zhí)行后處理效果!!
GPostProcessing.Process(RHICmdList, Views[ ViewIndex ], VelocityRT);
}
// End of frame, we don't need it anymore
FSceneRenderTargets::Get(RHICmdList).FreeSeparateTranslucencyDepth();
// we rendered to it during the frame, seems we haven't made use of it, because it should be released
check(!FSceneRenderTargets::Get(RHICmdList).SeparateTranslucencyRT);
}
else
{
// Release the original reference on the scene render targets
SceneContext.AdjustGBufferRefCount(RHICmdList, -1);
}
后處理類
/**
* The center for all post processing activities.
*/
class FPostProcessing
{
public:
bool AllowFullPostProcessing(const FViewInfo& View, ERHIFeatureLevel::Type FeatureLevel);
// @param VelocityRT only valid if motion blur is supported
void Process(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, TRefCountPtr<IPooledRenderTarget>& VelocityRT);
void ProcessES2(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, bool bViewRectSource);
void ProcessPlanarReflection(FRHICommandListImmediate& RHICmdList, FViewInfo& View, TRefCountPtr<IPooledRenderTarget>& VelocityRT, TRefCountPtr<IPooledRenderTarget>& OutFilteredSceneColor);
};
Shaders and Materials
-
Global Shaders
全局shader是用于操作固定幾何體(如:full screen quad)的shader,并且它不需要與materials產(chǎn)生交互,例如:陰影過濾呆细、后處理。這種類型的shader在內(nèi)存中只有一份實(shí)例八匠。 -
Material
材質(zhì)由一些控制材質(zhì)如何渲染的狀態(tài)和一些控制材質(zhì)如何與渲染Pass shader交互的材質(zhì)輸入(material inputs)組成絮爷。 -
Vertex Factories And Mesh Types
Material必須能夠賦給各種類型的Mesh Types, 這個是通過和Vertex factories配合完成的。 一個FVertexFactoryType表示唯一的Mesh Type梨树。一個FVertexFactory的實(shí)例存有每個幾何體的數(shù)據(jù)坑夯,以便支持特定的Mesh Type. 例如:FGPUSkinVertexFactory存放用于蒙皮的bone matrics、各種Vertex Buffers抡四,GPU Skin vertex factory shader代碼需要它們作為輸入柜蜈。Vertex Factory shader code是一個隱式接口,抽象了不同類型的Mesh types指巡,屏蔽了這些mesh types的差異淑履,方便各種pass shaders訪問mesh數(shù)據(jù)。vertex factories shader提供如下接口:
函數(shù) | 描述 |
---|---|
FVertexFactoryInput | Defines what the vertex factory needs as input to the vertex shader. These must match the vertex declaration in the C++ side FVertexFactory. For example, LocalVertexFactory's FVertexFactoryInput has float4 Position : POSITION;, which corresponds to the position stream declaration in FStaticMeshLODResources::SetupVertexFactory. |
FVertexFactoryIntermediates | Used to store cached intermediate data that will be used in multiple vertex factory functions. A common example is the TangentToLocal matrix, which had to be computed from unpacked vertex inputs. |
FVertexFactoryInterpolantsVSToPS | Vertex factory data to be passed from the vertex shader to the pixel shader. |
VertexFactoryGetWorldPosition | This is called from the vertex shader to get the world space vertex position. For Static Meshes this merely transforms the local space positions from the vertex buffer into world space using the LocalToWorld matrix. For GPU skinned meshes, the position is skinned first and then transformed to world space. |
VertexFactoryGetInterpolantsVSToPS | Transforms the FVertexFactoryInput to FVertexFactoryInterpolants, which will be interpolated by the graphics hardware before getting passed into the pixel shader. |
GetMaterialPixelParameters | This is called in the pixel shader and converts vertex factory specific interpolants (FVertexFactoryInterpolants) to the FMaterialPixelParameters structure which is used by the pass pixel shaders. |
-
Material Shaders
FMaterialShaderType類型的shaders是特定的渲染pass的shaders, 它需要訪問材質(zhì)屬性(material's attributes), 針對每個material都要相應(yīng)地編譯一下藻雪,這種類型的shaders不需要訪問mesh attributes(例如: light function pass shaders)秘噪。FMeshMaterialShaderType類型的shaders是特定的渲染pass的shaders,它依賴于材質(zhì)屬性和mesh type, 因此針對每個material, vertex factory組合都需要相應(yīng)地編譯一下勉耀,成為最終的可供gpu認(rèn)可的shader(ie. vs, ps), 例如:TBasePassVS/TBasePassPS需要和所有的material指煎、mesh type進(jìn)行組合編譯蹋偏。
一個material引發(fā)出的最終gpu shaders(FMaterialShader實(shí)例或FMeshMaterialShader實(shí)例)放在FMaterialShaderMap中。
綜上所述至壤,一個最終的gpu shader的合成方式如下表:
所屬Shader類型 | 組成部分 |
---|---|
GlobalShaderType | pass_shader_x.usf |
MaterialShaderType | material_x.usf + pass_shader_x.usf |
MeshMaterialShaderType | vertex_factory_x.usf + material_x.usf + pass_shader_x.usf |
Note: pass_shader_x.usf和vertex_factory_x.usf表示引擎Shders目錄下的usf文件, material_x.usf表示由UE4材質(zhì)編輯器生成的材質(zhì)結(jié)點(diǎn)代碼
- UE4源碼中相關(guān)類
-
FShader類
FShader類的繼承體系如下:
這些類實(shí)例對象代表一個GPU shader資源(vs,ps,cs),它由UE4的ShaderCompiler系統(tǒng)生成暖侨。同時每個類都對應(yīng)一個ShaderType實(shí)例,用于描述這些類用什么pass shader(即哪個usf源文件). 例如下面幾個Shader類FShader FGlobalShader FClearBufferReplacementCS FClearReplacementPS FClearReplacementVS FClearTexture2DReplacementCS FClearTexture2DReplacementScissorCS FCopyTexture2DCS FCubemapTexturePropertiesPS FCubemapTexturePropertiesVS FFillTextureCS FHdrCustomResolve2xPS FHdrCustomResolve4xPS FHdrCustomResolve8xPS FHdrCustomResolveVS FIESLightProfilePS FLongGPUTaskPS FMedaShadersVS FNULLPS FOneColorPS TOneClorPixelShaderMRT FResolveDepthNonMSPS FResolveDepthPS FResolveSingleSamplePS FResolveVS FScreenPS FScreenVS FScreenVSForGS FSimpleElementColorChannelMaskPS FSimpleElementHitProxyPS FSimpleElementPS FSimpleElementAlphaOnlyPS FSimpleElementGammaBasePS FSimpleElementGammaAlphaOnlyPS FSimpleElementGammaPS FSimpleElementMaskedGammaBasePS FSimpleElementDistanceFieldGammaPS FSimpleElementMaskedGammaPS FSimpleElementVS FUpdateTexture2DSubresouceCS FUpdateTexture3DSubresouceCS FYCbCrConvertShaderPS TOneColorVS FMaterialShader FPostProcessMaterialVS FPostProcessMaterialPS FLightFunctionVS FLightFunctionPS ... FMeshMaterialShader TDepthOnlyVS TDepthOnlyPS TDistortionMeshVS TDistortionMeshPS FShadowDepthVS FShadowDepthPS ...
class FClearReplacementVS : public FGlobalShader
{
// 聲明ShaderType對象,注意關(guān)鍵字Global.
DECLARE_EXPORTED_SHADER_TYPE(FClearReplacementVS, Global, UTILITYSHADERS_API);
public:
FClearReplacementVS() {}
FClearReplacementVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader( Initializer )
{
}
// FShader interface.
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
return bShaderHasOutdatedParameters;
}
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4);
}
};
// 使用的pass shader是ClearReplacementShaders.usf, 入口為ClearVS.
IMPLEMENT_SHADER_TYPE(,FClearReplacementVS,TEXT("ClearReplacementShaders"),TEXT("ClearVS"),SF_Vertex);
```
```cpp
template<EPostProcessMaterialTarget MaterialTarget>
class FPostProcessMaterialVS : public FMaterialShader
{
// 聲明ShaderType對象崇渗,注意關(guān)鍵字Material.
DECLARE_SHADER_TYPE(FPostProcessMaterialVS, Material);
public:
FPostProcessMaterialVS( ) { }
FPostProcessMaterialVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
PostprocessParameter.Bind(Initializer.ParameterMap);
}
private:
FPostProcessPassParameters PostprocessParameter;
};
typedef FPostProcessMaterialVS<EPostProcessMaterialTarget::HighEnd> FPostProcessMaterialVS_HighEnd;
typedef FPostProcessMaterialVS<EPostProcessMaterialTarget::Mobile> FPostProcessMaterialVS_Mobile;
// 使用的pass shader是PostProcessMaterialShaders.usf, 入口為MainVS.
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,FPostProcessMaterialVS_HighEnd,TEXT("PostProcessMaterialShaders"),TEXT("MainVS"),SF_Vertex);
```
```cpp
// A vertex shader for rendering the depth of a mesh.
class FShadowDepthVS : public FMeshMaterialShader
{
// 聲明ShaderType對象字逗,注意關(guān)鍵字MeshMaterial.
DECLARE_SHADER_TYPE(FShadowDepthVS,MeshMaterial);
public:
static bool ShouldCache(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType)
{
return false;
}
FShadowDepthVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
FMeshMaterialShader(Initializer)
{
ShadowParameters.Bind(Initializer.ParameterMap);
ShadowViewProjectionMatrices.Bind(Initializer.ParameterMap, TEXT("ShadowViewProjectionMatrices"));
MeshVisibleToFace.Bind(Initializer.ParameterMap, TEXT("MeshVisibleToFace"));
InstanceCount.Bind(Initializer.ParameterMap, TEXT("InstanceCount"));
}
FShadowDepthVS() {}
private:
FShadowDepthShaderParameters ShadowParameters;
FShaderParameter ShadowViewProjectionMatrices;
FShaderParameter MeshVisibleToFace;
FShaderParameter InstanceCount;
};
-
FShaderType類
該類用于描述Shader的類型。FShaderType FGlobalShaderType FMaterialShaderType FMeshMaterialShaderType
- FGlobalShaderType
A shader meta type for the simplest shaders; shaders which are not material or vertex factory linked. There should only a single instance of each simple shader type - FMaterialShaderType
A shader meta type for material-linked shaders. - FMeshMaterialShaderType
A shader meta type for material-linked shaders which use a vertex factory.
- FGlobalShaderType
-
Material Classes
用于描述材質(zhì)的類按所屬子系統(tǒng)可以分為2組:- 對象子系統(tǒng)
類名 描述 UMaterialInterface [抽象類] 聲明訪問材質(zhì)屬性接口供game thread使用 UMaterial 材質(zhì)資源,可用材質(zhì)編輯器打開進(jìn)行材質(zhì)編程宅广,提供材質(zhì)根結(jié)點(diǎn)中的material attribute實(shí)現(xiàn)代碼葫掉,另外提供blend mode,cull mode等等渲染狀態(tài) UMaterialInstance [抽象類] 基于UMaterial跟狱,它提供一些材質(zhì)編程中的結(jié)點(diǎn)參數(shù)(scalars, vectors, textures, static switchs)俭厚。Each instance has a parent UMaterialInterface. Therefore a material instance's parent may be a UMaterial or another UMaterialInstance. This creates a chain that will eventually lead to a UMaterial. UMaterialInstanceConstant 提供材質(zhì)編程中的結(jié)點(diǎn)參數(shù)(scalars, vectors, textures, static switchs), 這些參數(shù)只能在材質(zhì)編輯器中調(diào)整。 UMaterialInstanceDynamic A UMaterialInstance that may be modified at runtime. May provide scalar, vector, and texture parameters. It cannot provide static switch parameters and it cannot be the parent of another UMaterialInstance. 類層次圖:
UObject UMaterialInterface UMaterial UMaterialInstance UMaterialInstanceConstant UMaterialInstanceDynamic
- 渲染子系統(tǒng)
類名 描述 FMaterial An interface to a material used for rendering. Provides access to material properties (e.g. blend mode). Contains a shader map used by the renderer to retrieve individual shaders. FMaterialResource UMaterial's implementation of the FMaterial interface. UMaterial對象會生成FMaterialResource對象 FMaterialRenderProxy A material's representation on the rendering thread. Provides access to an FMaterial interface and the current value of each scalar, vector, and texture parameter. 它帶有材質(zhì)參數(shù)值驶臊,這些值是從UMaterialInstance對象同步過來的, 供render-thread訪問 類層次圖:
FMaterial FMaterialResource
FRenderResource FMaterialRenderProxy
現(xiàn)在來看一下這幾個類的關(guān)系:
UMaterial提供下面的接口可以獲得FMaterialRenderProxy對象和FMaterialResource對象.virtual FMaterialResource* GetMaterialResource(ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type QualityLevel = EMaterialQualityLevel::Num) override; virtual FMaterialRenderProxy* GetRenderProxy(bool Selected, bool bHovered=false) const override;
FMaterialRenderProxy提供下面的接口獲得FMaterial(或FMaterialResource)對象
virtual const class FMaterial* GetMaterial(ERHIFeatureLevel::Type InFeatureLevel) const = 0;
FMaterial提供如下接口獲取FShader對象
template<typename ShaderType> ShaderType* GetShader(FVertexFactoryType* VertexFactoryType) const { return (ShaderType*)GetShader(&ShaderType::StaticType, VertexFactoryType); }
綜上所述挪挤,渲染器獲得了FMaterialRenderProxy實(shí)例,就可以針對要渲染的Mesh取得最終的GPU Shader关翎。
繪制Mesh
-
場景中幾何體需要提供給渲染器的信息
現(xiàn)在以源碼中的UJCustomMeshComponent的實(shí)現(xiàn)為研究對象來了解一下繪制一個Mesh時需要哪些信息扛门。
在Engine\Plugins\Runtime\CustomMeshComponent\Source\CustomMeshComponent\Private\JCustomMeshComponent.cpp中
FJCustomMeshSceneProxy的對象是自定義的場景幾何體Proxy,它通過GetDynamicMeshElements()接口向渲染層提供要繪制的dynamic Mesh.
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
QUICK_SCOPE_CYCLE_COUNTER( STAT_CustomMeshSceneProxy_GetDynamicMeshElements );
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
auto WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : NULL,
FLinearColor(0, 0.5f, 1.f)
);
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
FMaterialRenderProxy* MaterialProxy = NULL;
if(bWireframe)
{
MaterialProxy = WireframeMaterialInstance;
}
else
{
MaterialProxy = Material->GetRenderProxy(IsSelected());
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
// Draw the mesh.
FMeshBatch& Mesh = Collector.AllocateMesh();
FMeshBatchElement& BatchElement = Mesh.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer; // element需要IndexBuffer
Mesh.bWireframe = bWireframe; // 是否線框模式繪制
Mesh.VertexFactory = &VertexFactory; // Vertex Factory對象
Mesh.MaterialRenderProxy = MaterialProxy; // Material Render Proxy對象
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, UseEditorDepthTest()); // mesh element的傳遞給shader的mvp參數(shù)
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = VertexBuffer.Vertices.Num() - 1;
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
Mesh.Type = PT_TriangleList;
Mesh.DepthPriorityGroup = SDPG_World;
Mesh.bCanApplyViewModeOverrides = false;
Collector.AddMesh(ViewIndex, Mesh);
}
}
}
名稱 | 描述 |
---|---|
Vertex Factory Object | 提供GPU的輸入數(shù)據(jù)流, GPU shader參數(shù) |
Matrial Render Proxy Object | 提供渲染時的狀態(tài)設(shè)置纵寝、GPU Shader對象论寨、Shader的參數(shù) |
Batch Element | Mesh的sub-mesh (它們共用材質(zhì)和頂點(diǎn)數(shù)據(jù)) |
Mesh Types | mesh的圖元類型 |
一個FMeshBatch的繪制,主要需要如下信息:
名稱 | 描述 |
---|---|
Vertex Factory Object | 提供GPU的輸入數(shù)據(jù)流, GPU shader參數(shù) |
Matrial Render Proxy Object | 提供渲染時的狀態(tài)設(shè)置爽茴、GPU Shader對象葬凳、Shader的參數(shù) |
Batch Element | Mesh的sub-mesh (它們共用材質(zhì)和頂點(diǎn)數(shù)據(jù)) |
Mesh Types | mesh的圖元類型 |
-
Render Policy
RenderPolicy負(fù)責(zé)渲染meshes,不同類型的Render Policy使用不同的pass shader。它從FMeshBatch對象中知道了Vertex Factory, Material Render Proxy, Mesh Elements, 圖元類型室奏。通過執(zhí)行如下步驟來繪制mesh. - 綁定Vertex Factory的Vertex Buffers火焰,設(shè)置數(shù)據(jù)流input layout
- 通過VertexFactory的類型和Pass Shader類型,從FMaterial對象獲取特定的GPU Shader(vs, ps. etc), 設(shè)置當(dāng)前的vertex shader和pixel shader胧沫。
- 設(shè)置GPU shader參數(shù)
- 發(fā)射RHI Draw命令
Function | Description |
---|---|
Constructor | Finds the appropriate shader from the given vertex factory and material shader map, stores these references |
CreateBoundShaderState | Creates an RHI bound shader state for the drawing policy. |
Matches/Compare | Provides methods to sort the drawing policy with others in the static draw lists. Matches must compare on all the factors that DrawShared depends on. |
DrawShared | Sets RHI state that is constant between drawing policies that return true from Matches. For example, most drawing policies sort on material and vertex factory, so shader parameters depending only on the material can be set, and the vertex buffers specific to the vertex factory can be bound. State should always be set here if possible instead of SetMeshRenderState, since DrawShared is called less times in the static rendering path. |
SetMeshRenderState | Sets RHI state that is specific to this mesh, or anything not set in DrawShared. This is called many more times than DrawShared so performance is especially critical here. |
DrawMesh | Actually issues the RHI draw call. |
FMeshDrawingPolicy的方法
Function | Description |
---|---|
Constructor | Finds the appropriate shader from the given vertex factory and material shader map, stores these references |
CreateBoundShaderState | Creates an RHI bound shader state for the drawing policy. |
Matches/Compare | Provides methods to sort the drawing policy with others in the static draw lists. Matches must compare on all the factors that DrawShared depends on. |
DrawShared | Sets RHI state that is constant between drawing policies that return true from Matches. For example, most drawing policies sort on material and vertex factory, so shader parameters depending only on the material can be set, and the vertex buffers specific to the vertex factory can be bound. State should always be set here if possible instead of SetMeshRenderState, since DrawShared is called less times in the static rendering path. |
SetMeshRenderState | Sets RHI state that is specific to this mesh, or anything not set in DrawShared. This is called many more times than DrawShared so performance is especially critical here. |
DrawMesh | Actually issues the RHI draw call. |
FMeshDrawingPolicy的類層次圖:
FMeshDrawingPolicy
FLandscapeGrassWeightDrawingPolicy
FMeshDecalsDrawingPolicy
FBasePassDrawingPolicy
FConvertToUniformMeshDrawingPolicy
FDepthDrawingPolicy
FPositionOnlyDepthDrawingPolicy
TDistortionMeshDrawingPolicy
TLightMapDensityDrawingPolicy
FHitProxyDrawingPolicy
FShadowDepthDrawingPolicy
FTranslucencyShadowDepthDrawingPolicy
FVelocityDrawingPolicy
FVoxelizeVolumeDrawingPolicy
下面閱讀分析一下BasepassRender時,RenderPolicy的使用:
/**
* Renders the basepass for the dynamic data of a given DPG and View.
*
* @return true if anything was rendered to scene color
*/
void FDeferredShadingSceneRenderer::RenderBasePassDynamicData(FRHICommandList& RHICmdList, const FViewInfo& View, const FDrawingPolicyRenderState& DrawRenderState, bool& bOutDirty)
{
SCOPE_CYCLE_COUNTER(STAT_DynamicPrimitiveDrawTime);
SCOPED_DRAW_EVENT(RHICmdList, Dynamic);
FBasePassOpaqueDrawingPolicyFactory::ContextType Context(false, ESceneRenderTargetsMode::DontSet);
for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.DynamicMeshElements.Num(); MeshBatchIndex++)
{
const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicMeshElements[MeshBatchIndex];
if ((MeshBatchAndRelevance.GetHasOpaqueOrMaskedMaterial() || ViewFamily.EngineShowFlags.Wireframe)
&& MeshBatchAndRelevance.GetRenderInMainPass())
{
const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh;
FBasePassOpaqueDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, View, Context, MeshBatch, true, DrawRenderState, MeshBatchAndRelevance.PrimitiveSceneProxy, MeshBatch.BatchHitProxyId, View.IsInstancedStereoPass());
}
}
}
bool FBasePassOpaqueDrawingPolicyFactory::DrawDynamicMesh(
FRHICommandList& RHICmdList,
const FViewInfo& View,
ContextType DrawingContext,
const FMeshBatch& Mesh,
bool bPreFog,
const FDrawingPolicyRenderState& DrawRenderState,
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
FHitProxyId HitProxyId,
const bool bIsInstancedStereo
)
{
// Determine the mesh's material and blend mode.
const FMaterial* Material = Mesh.MaterialRenderProxy->GetMaterial(View.GetFeatureLevel());
const EBlendMode BlendMode = Material->GetBlendMode();
// Only draw opaque materials.
if (!IsTranslucentBlendMode(BlendMode) && ShouldIncludeDomainInMeshPass(Material->GetMaterialDomain()))
{
ProcessBasePassMesh(
RHICmdList,
FProcessBasePassMeshParameters(
Mesh,
Material,
PrimitiveSceneProxy,
!bPreFog,
DrawingContext.bEditorCompositeDepthTest,
DrawingContext.TextureMode,
View.GetFeatureLevel(),
bIsInstancedStereo
),
FDrawBasePassDynamicMeshAction(
RHICmdList,
View,
Mesh.DitheredLODTransitionAlpha,
DrawRenderState,
HitProxyId
)
);
return true;
}
else
{
return false;
}
}
// BasePassDrawPolicy的使用流程昌简,請看下面代碼中的注釋
/** The action used to draw a base pass dynamic mesh element. */
class FDrawBasePassDynamicMeshAction
{
public:
const FViewInfo& View;
FDrawingPolicyRenderState DrawRenderState;
FHitProxyId HitProxyId;
/** Initialization constructor. */
FDrawBasePassDynamicMeshAction(
FRHICommandList& InRHICmdList,
const FViewInfo& InView,
float InDitheredLODTransitionAlpha,
const FDrawingPolicyRenderState& InDrawRenderState,
const FHitProxyId InHitProxyId
)
: View(InView)
, DrawRenderState(InDrawRenderState)
, HitProxyId(InHitProxyId)
{
DrawRenderState.SetDitheredLODTransitionAlpha(InDitheredLODTransitionAlpha);
}
bool UseTranslucentSelfShadowing() const { return false; }
const FProjectedShadowInfo* GetTranslucentSelfShadow() const { return NULL; }
bool AllowIndirectLightingCache() const
{
const FScene* Scene = (const FScene*)View.Family->Scene;
return View.Family->EngineShowFlags.IndirectLightingCache && Scene && Scene->PrecomputedLightVolumes.Num() > 0;
}
bool AllowIndirectLightingCacheVolumeTexture() const
{
return true;
}
/** Draws the translucent mesh with a specific light-map type, and shader complexity predicate. */
template<typename LightMapPolicyType>
void Process(
FRHICommandList& RHICmdList,
const FProcessBasePassMeshParameters& Parameters,
const LightMapPolicyType& LightMapPolicy,
const typename LightMapPolicyType::ElementDataType& LightMapElementData
)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
//TODO this codepath is probably disabled by SetDepthStencilStateForBasePass
if(View.Family->EngineShowFlags.ShaderComplexity)
{
// When rendering masked materials in the shader complexity viewmode,
// We want to overwrite complexity for the pixels which get depths written,
// And accumulate complexity for pixels which get killed due to the opacity mask being below the clip value.
// This is accomplished by forcing the masked materials to render depths in the depth only pass,
// Then rendering in the base pass with additive complexity blending, depth tests on, and depth writes off.
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false,CF_DepthNearOrEqual>::GetRHI());
}
else if (View.Family->UseDebugViewPS() && View.Family->GetDebugViewShaderMode() != DVSM_OutputMaterialTextureScales)
{
if (Parameters.PrimitiveSceneProxy && Parameters.PrimitiveSceneProxy->IsSelected())
{
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
}
else // If not selected, use depth equal to make alpha test stand out (goes with EarlyZPassMode = DDM_AllOpaque)
{
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Equal>::GetRHI());
}
}
#endif
const FScene* Scene = Parameters.PrimitiveSceneProxy ? Parameters.PrimitiveSceneProxy->GetPrimitiveSceneInfo()->Scene : NULL;
const bool bRenderSkylight = Scene && Scene->ShouldRenderSkylightInBasePass(Parameters.BlendMode) && Parameters.ShadingModel != MSM_Unlit;
const bool bRenderAtmosphericFog = IsTranslucentBlendMode(Parameters.BlendMode) && (Scene && Scene->HasAtmosphericFog() && Scene->ReadOnlyCVARCache.bEnableAtmosphericFog) && View.Family->EngineShowFlags.AtmosphericFog;
bool bEnableReceiveDecalOutput = Scene != nullptr;
// NOTE:
// 臨時RenderPolicy對象
//
TBasePassDrawingPolicy<LightMapPolicyType> DrawingPolicy(
Parameters.Mesh.VertexFactory,
Parameters.Mesh.MaterialRenderProxy,
*Parameters.Material,
Parameters.FeatureLevel,
LightMapPolicy,
Parameters.BlendMode,
Parameters.TextureMode,
bRenderSkylight,
bRenderAtmosphericFog,
ComputeMeshOverrideSettings(Parameters.Mesh),
View.Family->GetDebugViewShaderMode(),
Parameters.bEditorCompositeDepthTest,
bEnableReceiveDecalOutput
);
// 設(shè)置渲染狀態(tài)
SetDepthStencilStateForBasePass(DrawRenderState, View, Parameters.Mesh, Parameters.PrimitiveSceneProxy, bEnableReceiveDecalOutput, DrawingPolicy.UseDebugViewPS(), nullptr, Parameters.bEditorCompositeDepthTest);
DrawingPolicy.SetupPipelineState(DrawRenderState, View);
// 設(shè)置GPU Shaders
CommitGraphicsPipelineState(RHICmdList, DrawingPolicy, DrawRenderState, DrawingPolicy.GetBoundShaderStateInput(View.GetFeatureLevel()));
// 設(shè)置GPU Shaders的uniforms參數(shù)
DrawingPolicy.SetSharedState(RHICmdList, DrawRenderState, &View, typename TBasePassDrawingPolicy<LightMapPolicyType>::ContextDataType(Parameters.bIsInstancedStereo));
for( int32 BatchElementIndex = 0, Num = Parameters.Mesh.Elements.Num(); BatchElementIndex < Num; BatchElementIndex++ )
{
// We draw instanced static meshes twice when rendering with instanced stereo. Once for each eye.
const bool bIsInstancedMesh = Parameters.Mesh.Elements[BatchElementIndex].bIsInstancedMesh;
const uint32 InstancedStereoDrawCount = (Parameters.bIsInstancedStereo && bIsInstancedMesh) ? 2 : 1;
for (uint32 DrawCountIter = 0; DrawCountIter < InstancedStereoDrawCount; ++DrawCountIter)
{
DrawingPolicy.SetInstancedEyeIndex(RHICmdList, DrawCountIter);
TDrawEvent<FRHICommandList> MeshEvent;
BeginMeshDrawEvent(RHICmdList, Parameters.PrimitiveSceneProxy, Parameters.Mesh, MeshEvent);
// 設(shè)置狀態(tài)和參數(shù)
DrawingPolicy.SetMeshRenderState(
RHICmdList,
View,
Parameters.PrimitiveSceneProxy,
Parameters.Mesh,
BatchElementIndex,
DrawRenderState,
typename TBasePassDrawingPolicy<LightMapPolicyType>::ElementDataType(LightMapElementData),
typename TBasePassDrawingPolicy<LightMapPolicyType>::ContextDataType()
);
// 發(fā)射Draw命令
DrawingPolicy.DrawMesh(RHICmdList, Parameters.Mesh, BatchElementIndex, Parameters.bIsInstancedStereo);
}
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
//TODO this codepath is probably disabled by SetDepthStencilStateForBasePass
if(View.Family->EngineShowFlags.ShaderComplexity)
{
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true,CF_DepthNearOrEqual>::GetRHI());
}
#endif
}
};
Shader和Shader Parameters
每個GPU Shader編譯后,都會有個shader參數(shù)表(用FShaderParameterMap表示), FShaderParameter描述shader參數(shù)名與分配的寄存器信息琳袄。因?yàn)橐粋€GPU Shader是由vertex-factory shader江场、material shader和 pass shader組合成的,所以這3個部分均可以產(chǎn)生shader parameters窖逗。下面我們要弄清楚,在RenderPolicy渲染mesh時址否,哪些對象參與設(shè)置了shader parameters,提供的接口是什么。
- 從FShader(含子類)對象考慮:
- FGlobalShader
template<typename TViewUniformShaderParameters, typename ShaderRHIParamRef, typename TRHICmdList>
inline void SetParameters(TRHICmdList& RHICmdList, const ShaderRHIParamRef ShaderRHI, const FUniformBufferRHIParamRef ViewUniformBuffer)
{
const auto& ViewUniformBufferParameter = static_cast<const FShaderUniformBufferParameter&>(GetUniformBufferParameter<TViewUniformShaderParameters>());
CheckShaderIsValid();
SetUniformBufferParameter(RHICmdList, ShaderRHI, ViewUniformBufferParameter, ViewUniformBuffer);
}
- FMaterialShader
template<typename ShaderRHIParamRef>
FORCEINLINE_DEBUGGABLE void SetViewParameters(FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI, const FSceneView& View, const TUniformBufferRef<FViewUniformShaderParameters>& ViewUniformBuffer)
{
const auto& ViewUniformBufferParameter = GetUniformBufferParameter<FViewUniformShaderParameters>();
const auto& BuiltinSamplersUBParameter = GetUniformBufferParameter<FBuiltinSamplersParameters>();
CheckShaderIsValid();
SetUniformBufferParameter(RHICmdList, ShaderRHI, ViewUniformBufferParameter, ViewUniformBuffer);
#if USE_GBuiltinSamplersUniformBuffer
SetUniformBufferParameter(RHICmdList, ShaderRHI, BuiltinSamplersUBParameter, GBuiltinSamplersUniformBuffer.GetUniformBufferRHI());
#endif
if (View.bShouldBindInstancedViewUB && View.Family->Views.Num() > 0)
{
// When drawing the left eye in a stereo scene, copy the right eye view values into the instanced view uniform buffer.
const EStereoscopicPass StereoPassIndex = (View.StereoPass != eSSP_FULL) ? eSSP_RIGHT_EYE : eSSP_FULL;
const FSceneView& InstancedView = View.Family->GetStereoEyeView(StereoPassIndex);
const auto& InstancedViewUniformBufferParameter = GetUniformBufferParameter<FInstancedViewUniformShaderParameters>();
SetUniformBufferParameter(RHICmdList, ShaderRHI, InstancedViewUniformBufferParameter, InstancedView.ViewUniformBuffer);
}
}
/** Sets pixel parameters that are material specific but not FMeshBatch specific. */
template< typename ShaderRHIParamRef >
void SetParameters(
FRHICommandList& RHICmdList,
const ShaderRHIParamRef ShaderRHI,
const FMaterialRenderProxy* MaterialRenderProxy,
const FMaterial& Material,
const FSceneView& View,
const TUniformBufferRef<FViewUniformShaderParameters>& ViewUniformBuffer,
bool bDeferredPass,
ESceneRenderTargetsMode::Type TextureMode);
- FMeshMaterialShader
// 設(shè)置材質(zhì)(material)需要的參數(shù), 不排除其它參數(shù) template< typename ShaderRHIParamRef > void SetParameters( FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI, const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial& Material, const FSceneView& View, const TUniformBufferRef<FViewUniformShaderParameters>& ViewUniformBuffer, ESceneRenderTargetsMode::Type TextureMode) { FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialRenderProxy, Material, View, ViewUniformBuffer, false, TextureMode); }
// 設(shè)置Vertex Factory的shader參數(shù) void SetVFParametersOnly(FRHICommandList& RHICmdList, const FVertexFactory* VertexFactory,const FSceneView& View,const FMeshBatchElement& BatchElement) { VertexFactoryParameters.SetMesh(RHICmdList, this,VertexFactory,View,BatchElement, 0); }
另外, FMeshMaterialShader的子類根據(jù)不同pass shader的實(shí)現(xiàn)佑附,會改寫SetParameters()和SetMesh()來設(shè)置gpu shader參數(shù), 例如:FShadowDepthVS.// 設(shè)置跟mesh相關(guān)的參數(shù) template< typename ShaderRHIParamRef > void FMeshMaterialShader::SetMesh( FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI, const FVertexFactory* VertexFactory, const FSceneView& View, const FPrimitiveSceneProxy* Proxy, const FMeshBatchElement& BatchElement, const FDrawingPolicyRenderState& DrawRenderState, uint32 DataFlags ) { // Set the mesh for the vertex factory VertexFactoryParameters.SetMesh(RHICmdList, this,VertexFactory,View,BatchElement, DataFlags); // 設(shè)置LocalToworld, WorldToLocal等參數(shù)!! if(IsValidRef(BatchElement.PrimitiveUniformBuffer)) { SetUniformBufferParameter(RHICmdList, ShaderRHI,GetUniformBufferParameter<FPrimitiveUniformShaderParameters>(),BatchElement.PrimitiveUniformBuffer); } else { check(BatchElement.PrimitiveUniformBufferResource); SetUniformBufferParameter(RHICmdList, ShaderRHI,GetUniformBufferParameter<FPrimitiveUniformShaderParameters>(),*BatchElement.PrimitiveUniformBufferResource); } TShaderUniformBufferParameter<FDistanceCullFadeUniformShaderParameters> LODParameter = GetUniformBufferParameter<FDistanceCullFadeUniformShaderParameters>(); if( LODParameter.IsBound() ) { SetUniformBufferParameter(RHICmdList, ShaderRHI,LODParameter,GetPrimitiveFadeUniformBufferParameter(View, Proxy)); } if (NonInstancedDitherLODFactorParameter.IsBound()) { SetShaderValue(RHICmdList, ShaderRHI, NonInstancedDitherLODFactorParameter, DrawRenderState.GetDitheredLODTransitionAlpha()); } }
SetParameters()在FMeshDrawingPolicy::SetSharedState()中被調(diào)用:
SetMesh()在FMeshDrawingPolicy::SetMeshRenderState()中被調(diào)用/** * Executes the draw commands which can be shared between any meshes using this drawer. * @param CI - The command interface to execute the draw commands on. * @param View - The view of the scene being drawn. */ void SetSharedState(FRHICommandList& RHICmdList, const FDrawingPolicyRenderState& DrawRenderState, const FSceneView* View, const FMeshDrawingPolicy::ContextDataType PolicyContext) const;
/** * Sets the render states for drawing a mesh. * @param PrimitiveSceneProxy - The primitive drawing the dynamic mesh. If this is a view element, this will be NULL. */ FORCEINLINE_DEBUGGABLE void SetMeshRenderState( FRHICommandList& RHICmdList, const FSceneView& View, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMeshBatch& Mesh, int32 BatchElementIndex, const FDrawingPolicyRenderState& DrawRenderState, const ElementDataType& ElementData, const ContextDataType PolicyContext ) const;
Uniform Buffer
源碼路徑[Engine\Source\Runtime\RenderCore\Public\UniformBuffer.h]
Uniform Buffer用于從CPU傳送shader parameters到GPU樊诺。下面的宏用于在CPP代碼中定義Uniform Buffer結(jié)構(gòu)體,并且被ShaderCompiler使用音同,產(chǎn)生usf文件中uniform buffer的結(jié)構(gòu)定義,例如:Primitive, View, InstancedView, BuiltinSamplers, MobileDirectionalLight词爬。
在C++中的定義如下。
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FPrimitiveUniformShaderParameters,TEXT("Primitive"));
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FViewUniformShaderParameters,TEXT("View"));
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FInstancedViewUniformShaderParameters, TEXT("InstancedView"));
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FBuiltinSamplersParameters, TEXT("BuiltinSamplers"));
IMPLEMENT_UNIFORM_BUFFER_STRUCT(FMobileDirectionalLightShaderParameters, TEXT("MobileDirectionalLight"));
//
// Macros for declaring uniform buffer structures.
//
#define IMPLEMENT_UNIFORM_BUFFER_STRUCT(StructTypeName,ShaderVariableName) \
FUniformBufferStruct StructTypeName::StaticStruct( \
FName(TEXT(#StructTypeName)), \
TEXT(#StructTypeName), \
ShaderVariableName, \
StructTypeName::ConstructUniformBufferParameter, \
sizeof(StructTypeName), \
StructTypeName::zzGetMembers(), \
true);
/** Begins a uniform buffer struct declaration. */
#define BEGIN_UNIFORM_BUFFER_STRUCT_EX(StructTypeName,PrefixKeywords,ConstructorSuffix) \
MS_ALIGN(UNIFORM_BUFFER_STRUCT_ALIGNMENT) class PrefixKeywords StructTypeName \
{ \
public: \
StructTypeName () ConstructorSuffix \
static FUniformBufferStruct StaticStruct; \
static FShaderUniformBufferParameter* ConstructUniformBufferParameter() { return new TShaderUniformBufferParameter<StructTypeName>(); } \
static FUniformBufferRHIRef CreateUniformBuffer(const StructTypeName& InContents, EUniformBufferUsage InUsage) \
{ \
return RHICreateUniformBuffer(&InContents,StaticStruct.GetLayout(),InUsage); \
} \
private: \
typedef StructTypeName zzTThisStruct; \
struct zzFirstMemberId { enum { HasDeclaredResource = 0 }; }; \
static TArray<FUniformBufferStruct::FMember> zzGetMembersBefore(zzFirstMemberId) \
{ \
return TArray<FUniformBufferStruct::FMember>(); \
} \
typedef zzFirstMemberId
/** Declares a member of a uniform buffer struct. */
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(MemberType,MemberName,ArrayDecl,Precision,OptionalShaderType) \
zzMemberId##MemberName; \
public: \
typedef MemberType zzA##MemberName ArrayDecl; \
typedef TUniformBufferTypeInfo<zzA##MemberName>::TAlignedType zzT##MemberName; \
zzT##MemberName MemberName; \
static_assert(TUniformBufferTypeInfo<zzA##MemberName>::BaseType != UBMT_INVALID, "Invalid type " #MemberType " of member " #MemberName "."); \
static_assert(TUniformBufferTypeInfo<zzA##MemberName>::BaseType != UBMT_UAV, "UAV is not yet supported in resource tables for " #MemberName " of type " #MemberType "."); \
private: \
struct zzNextMemberId##MemberName { enum { HasDeclaredResource = zzMemberId##MemberName::HasDeclaredResource || TUniformBufferTypeInfo<zzT##MemberName>::IsResource }; }; \
static TArray<FUniformBufferStruct::FMember> zzGetMembersBefore(zzNextMemberId##MemberName) \
{ \
static_assert(TUniformBufferTypeInfo<zzT##MemberName>::IsResource == 1 || zzMemberId##MemberName::HasDeclaredResource == 0, "All resources must be declared last for " #MemberName "."); \
static_assert(TUniformBufferTypeInfo<zzT##MemberName>::IsResource == 0 || IS_TCHAR_ARRAY(OptionalShaderType), "No shader type for " #MemberName "."); \
/* Route the member enumeration on to the function for the member following this. */ \
TArray<FUniformBufferStruct::FMember> OutMembers = zzGetMembersBefore(zzMemberId##MemberName()); \
/* Add this member. */ \
OutMembers.Add(FUniformBufferStruct::FMember( \
TEXT(#MemberName), \
OptionalShaderType, \
STRUCT_OFFSET(zzTThisStruct,MemberName), \
(EUniformBufferBaseType)TUniformBufferTypeInfo<zzA##MemberName>::BaseType, \
Precision, \
TUniformBufferTypeInfo<zzA##MemberName>::NumRows, \
TUniformBufferTypeInfo<zzA##MemberName>::NumColumns, \
TUniformBufferTypeInfo<zzA##MemberName>::NumElements, \
TUniformBufferTypeInfo<zzA##MemberName>::GetStruct() \
)); \
static_assert( \
(STRUCT_OFFSET(zzTThisStruct,MemberName) & (TUniformBufferTypeInfo<zzA##MemberName>::Alignment - 1)) == 0, \
"Misaligned uniform buffer struct member " #MemberName "."); \
return OutMembers; \
} \
typedef zzNextMemberId##MemberName
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY_EX(MemberType,MemberName,ArrayDecl,Precision) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(MemberType,MemberName,ArrayDecl,Precision,TEXT(""))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(MemberType,MemberName,ArrayDecl) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(MemberType,MemberName,ArrayDecl,EShaderPrecisionModifier::Float,TEXT(""))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(MemberType,MemberName) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(MemberType,MemberName,,EShaderPrecisionModifier::Float,TEXT(""))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EX(MemberType,MemberName,Precision) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(MemberType,MemberName,,Precision,TEXT(""))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_SRV(ShaderType,MemberName) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(FShaderResourceViewRHIParamRef,MemberName,,EShaderPrecisionModifier::Float,TEXT(#ShaderType))
// NOT SUPPORTED YET
//#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_UAV(ShaderType,MemberName) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(FUnorderedAccessViewRHIParamRef,MemberName,,EShaderPrecisionModifier::Float,TEXT(#ShaderType))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_SAMPLER(ShaderType,MemberName) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(FSamplerStateRHIParamRef,MemberName,,EShaderPrecisionModifier::Float,TEXT(#ShaderType))
#define DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_TEXTURE(ShaderType,MemberName) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_EXPLICIT(FTextureRHIParamRef,MemberName,,EShaderPrecisionModifier::Float,TEXT(#ShaderType))
/** Ends a uniform buffer struct declaration. */
#define END_UNIFORM_BUFFER_STRUCT(Name) \
zzLastMemberId; \
static TArray<FUniformBufferStruct::FMember> zzGetMembers() { return zzGetMembersBefore(zzLastMemberId()); } \
} GCC_ALIGN(UNIFORM_BUFFER_STRUCT_ALIGNMENT); \
template<> class TUniformBufferTypeInfo<Name> \
{ \
public: \
enum { BaseType = UBMT_STRUCT }; \
enum { NumRows = 1 }; \
enum { NumColumns = 1 }; \
enum { Alignment = UNIFORM_BUFFER_STRUCT_ALIGNMENT }; \
static const FUniformBufferStruct* GetStruct() { return &Name::StaticStruct; } \
};
#define BEGIN_UNIFORM_BUFFER_STRUCT(StructTypeName,PrefixKeywords) BEGIN_UNIFORM_BUFFER_STRUCT_EX(StructTypeName,PrefixKeywords,{})
#define BEGIN_UNIFORM_BUFFER_STRUCT_WITH_CONSTRUCTOR(StructTypeName,PrefixKeywords) BEGIN_UNIFORM_BUFFER_STRUCT_EX(StructTypeName,PrefixKeywords,;)
/** Finds the FUniformBufferStruct corresponding to the given name, or NULL if not found. */
extern RENDERCORE_API FUniformBufferStruct* FindUniformBufferStructByName(const TCHAR* StructName);