UE4渲染模塊分析

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)。

  1. 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中的古瓤。

  2. 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順序并逐一介紹.

  1. 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;
}
  1. 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
            );
  1. PASS_2: Issue Occlusion Queries / BeginOcclusionTests
    執(zhí)行遮擋查詢,在繪制下一幀時法竞,InitView會使用這些信息進(jìn)行可見性判斷耙厚。遮擋查詢的原理是通過繪制幾何體的包圍盒進(jìn)行z-depth測試强挫,以粗略地判斷該幾何體是否被遮擋岔霸。
void FDeferredShadingSceneRenderer::RenderOcclusion(FRHICommandListImmediate& RHICmdList, bool bRenderQueries, bool bRenderHZB)
  1. 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);
            }
        }
    }
}
  1. 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();
   }
  1. 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)
  1. 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)
  1. 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)
  1. 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

參考UE4官方文檔

  • 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)類
  1. FShader類
    FShader類的繼承體系如下:
    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
                ...
    
    這些類實(shí)例對象代表一個GPU shader資源(vs,ps,cs),它由UE4的ShaderCompiler系統(tǒng)生成暖侨。同時每個類都對應(yīng)一個ShaderType實(shí)例,用于描述這些類用什么pass shader(即哪個usf源文件). 例如下面幾個Shader類
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;
};
  1. 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.
  2. 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,提供的接口是什么。

  1. 從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);
     }
    
    // 設(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());
         }
     }
    
    另外, FMeshMaterialShader的子類根據(jù)不同pass shader的實(shí)現(xiàn)佑附,會改寫SetParameters()和SetMesh()來設(shè)置gpu shader參數(shù), 例如:FShadowDepthVS.
    SetParameters()在FMeshDrawingPolicy::SetSharedState()中被調(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;
    
    SetMesh()在FMeshDrawingPolicy::SetMeshRenderState()中被調(diào)用
    /**
     * 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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末权均,一起剝皮案震驚了整個濱河市顿膨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叽赊,老刑警劉巖恋沃,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異必指,居然都是意外死亡囊咏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門塔橡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梅割,“玉大人,你說我怎么就攤上這事葛家』Т牵” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵惦银,是天一觀的道長咆课。 經(jīng)常有香客問我,道長扯俱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任喇澡,我火速辦了婚禮迅栅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晴玖。我一直安慰自己读存,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布呕屎。 她就那樣靜靜地躺著让簿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秀睛。 梳的紋絲不亂的頭發(fā)上尔当,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音蹂安,去河邊找鬼椭迎。 笑死锐帜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畜号。 我是一名探鬼主播缴阎,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼简软!你這毒婦竟也來了蛮拔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤痹升,失蹤者是張志新(化名)和其女友劉穎语泽,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體视卢,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踱卵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了据过。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惋砂。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绳锅,靈堂內(nèi)的尸體忽然破棺而出西饵,到底是詐尸還是另有隱情,我是刑警寧澤鳞芙,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布眷柔,位于F島的核電站,受9級特大地震影響原朝,放射性物質(zhì)發(fā)生泄漏驯嘱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一喳坠、第九天 我趴在偏房一處隱蔽的房頂上張望怨喘。 院中可真熱鬧陈惰,春花似錦交汤、人聲如沸琴许。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽负乡。三九已至,卻和暖如春脊凰,著一層夾襖步出監(jiān)牢的瞬間抖棘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钉答,地道東北人础芍。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像数尿,于是被迫代替她去往敵國和親仑性。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容