17 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
Untitled 2025-02-11 11:30:34

FSortedLightSetSceneInfo

有序的光源集合相关定义:

/** Data for a simple dynamic light. */  
class FSimpleLightEntry  
{  
public:  
    FVector3f Color;  
    float Radius;  
    float Exponent;  
    float InverseExposureBlend = 0.0f;  
    float VolumetricScatteringIntensity;  
    bool bAffectTranslucency;  
};

struct FSortedLightSceneInfo  
{  
    union  
    {  
       struct  
       {  
          // Note: the order of these members controls the light sort order!  
          // Currently bHandledByLumen is the MSB and LightType is LSB          /** The type of light. */          uint32 LightType : LightType_NumBits;  
          /** Whether the light has a texture profile. */  
          uint32 bTextureProfile : 1;  
          /** Whether the light uses a light function. */  
          uint32 bLightFunction : 1;  
          /** Whether the light uses lighting channels. */  
          uint32 bUsesLightingChannels : 1;  
          /** Whether the light casts shadows. */  
          uint32 bShadowed : 1;  
          /** Whether the light is NOT a simple light - they always support tiled/clustered but may want to be selected separately. */  
          uint32 bIsNotSimpleLight : 1;  
          /* We want to sort the lights that write into the packed shadow mask (when enabled) to the front of the list so we don't waste slots in the packed shadow mask. */  
          uint32 bDoesNotWriteIntoPackedShadowMask : 1;  
          /**   
           * True if the light doesn't support clustered deferred, logic is inverted so that lights that DO support clustered deferred will sort first in list   
           * Super-set of lights supporting tiled, so the tiled lights will end up in the first part of this range.  
           */          
           uint32 bClusteredDeferredNotSupported : 1;  
          /** Whether the light should be handled by Lumen's Final Gather, these will be sorted to the end so they can be skipped */  
          uint32 bHandledByLumen : 1;  
       } Fields;  
       /** Sort key bits packed into an integer. */  
       int32 Packed;  
    } SortKey;  
  
    const FLightSceneInfo* LightSceneInfo;  
    int32 SimpleLightIndex;  
  
    /** Initialization constructor. */  
    explicit FSortedLightSceneInfo(const FLightSceneInfo* InLightSceneInfo)  
       : LightSceneInfo(InLightSceneInfo),  
       SimpleLightIndex(-1)  
    {       
	    SortKey.Packed = 0;  
		SortKey.Fields.bIsNotSimpleLight = 1;  
    }  
    explicit FSortedLightSceneInfo(int32 InSimpleLightIndex)  
       : LightSceneInfo(nullptr),  
       SimpleLightIndex(InSimpleLightIndex)  
    {   
        SortKey.Packed = 0;  
		SortKey.Fields.bIsNotSimpleLight = 0;  
    }};  
  
/**   
 * Stores info about sorted lights and ranges.   
 * The sort-key in FSortedLightSceneInfo gives rise to the following order:  
 *  [SimpleLights,Clustered,UnbatchedLights,LumenLights] * Note that some shadowed lights can be included in the clustered pass when virtual shadow maps and one pass projection are used. */struct FSortedLightSetSceneInfo  
{  
    int32 SimpleLightsEnd;  
    int32 ClusteredSupportedEnd;  
  
    /** First light with shadow map or */  
    int32 UnbatchedLightStart;  
  
    int32 LumenLightStart;  
  
    FSimpleLightArray SimpleLights;  
    TArray<FSortedLightSceneInfo, SceneRenderingAllocator> SortedLights;  
};

开始获取有序光源集合

UE的光源分配由FDeferredShadingSceneRenderer::Render内的bComputeLightGrid变量决定的bComputeLightGrid的赋值逻辑如下

void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) {
...
	bool bComputeLightGrid = false;

	if (RendererOutput == ERendererOutput::FinalSceneColor)
	{
		if (bUseVirtualTexturing)
		{
			// Note, should happen after the GPU-Scene update to ensure rendering to runtime virtual textures is using the correctly updated scene
			FVirtualTextureSystem::Get().EndUpdate(GraphBuilder, MoveTemp(VirtualTextureUpdater), FeatureLevel);
		}

#if RHI_RAYTRACING
		GatherRayTracingWorldInstancesForView(GraphBuilder, ReferenceView, RayTracingScene, InitViewTaskDatas.RayTracingRelevantPrimitives);
#endif // RHI_RAYTRACING

		bool bAnyLumenEnabled = false;

		{
			if (bUseGBuffer)
			{
				bComputeLightGrid = bRenderDeferredLighting;
			}
			else
			{
				bComputeLightGrid = ViewFamily.EngineShowFlags.Lighting;
			}

			for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
			{
				FViewInfo& View = Views[ViewIndex];
				bAnyLumenEnabled = bAnyLumenEnabled
					|| GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen
					|| GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
			}

			bComputeLightGrid |= (
				ShouldRenderVolumetricFog() ||
				VolumetricCloudWantsToSampleLocalLights(Scene, ViewFamily.EngineShowFlags) ||
				ViewFamily.ViewMode != VMI_Lit ||
				bAnyLumenEnabled ||
				VirtualShadowMapArray.IsEnabled() ||
				ShouldVisualizeLightGrid());
		}
	}
...
}	

获取有序的光源集合

void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) {
...
	// 有序的光源集合.
	FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject<FSortedLightSetSceneInfo>();  
	{  
	    RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SortLights);  
	    RDG_GPU_STAT_SCOPE(GraphBuilder, SortLights);  
	    ComputeLightGridOutput = GatherLightsAndComputeLightGrid(GraphBuilder, bComputeLightGrid, SortedLightSet);  
	}
...
}	

PS. 简单光源都可以被分块或分簇渲染,但对于非简单光源,只有满足以下条件的光源才可被分块或分簇渲染:

  • 没有使用光源的附加特性TextureProfile、LightFunction、LightingChannel
  • 没有开启阴影。
  • 非平行光或矩形光。

另外,是否支持分块渲染,还需要光源场景代理的IsTiledDeferredLightingSupported返回true长度为0的点光源才支持分块渲染。

GatherLightsAndComputeLightGrid

FComputeLightGridOutput FDeferredShadingSceneRenderer::GatherLightsAndComputeLightGrid(FRDGBuilder& GraphBuilder, bool bNeedLightGrid, FSortedLightSetSceneInfo& SortedLightSet)
{
	SCOPED_NAMED_EVENT(GatherLightsAndComputeLightGrid, FColor::Emerald);
	FComputeLightGridOutput Result = {};

	bool bShadowedLightsInClustered = ShouldUseClusteredDeferredShading()
		&& CVarVirtualShadowOnePassProjection.GetValueOnRenderThread()
		&& VirtualShadowMapArray.IsEnabled();

	const bool bUseLumenDirectLighting = ShouldRenderLumenDirectLighting(Scene, Views[0]);

	GatherAndSortLights(SortedLightSet, bShadowedLightsInClustered, bUseLumenDirectLighting);
	
	if (!bNeedLightGrid)
	{
		SetDummyForwardLightUniformBufferOnViews(GraphBuilder, ShaderPlatform, Views);
		return Result;
	}

	bool bAnyViewUsesForwardLighting = false;
	bool bAnyViewUsesLumen = false;
	for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		const FViewInfo& View = Views[ViewIndex];
		bAnyViewUsesForwardLighting |= View.bTranslucentSurfaceLighting || ShouldRenderVolumetricFog() || View.bHasSingleLayerWaterMaterial || VolumetricCloudWantsToSampleLocalLights(Scene, ViewFamily.EngineShowFlags) || ShouldVisualizeLightGrid();
		bAnyViewUsesLumen |= GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
	}
	
	const bool bCullLightsToGrid = GLightCullingQuality 
		&& (IsForwardShadingEnabled(ShaderPlatform) || bAnyViewUsesForwardLighting || IsRayTracingEnabled() || ShouldUseClusteredDeferredShading() ||
			bAnyViewUsesLumen || ViewFamily.EngineShowFlags.VisualizeMeshDistanceFields || VirtualShadowMapArray.IsEnabled());

	// Store this flag if lights are injected in the grids, check with 'AreLightsInLightGrid()'
	bAreLightsInLightGrid = bCullLightsToGrid;
	
	Result = ComputeLightGrid(GraphBuilder, bCullLightsToGrid, SortedLightSet);

	return Result;
}
  • GatherAndSortLights收集与排序当前场景中所有的可见光源当前View
  • ComputeLightGrid是在锥体空间frustum space裁剪局部光源和反射探针到3D格子中构建每个视图相关的光源列表和格子。

RenderLights() -> RenderLight()

InternalRenderLight()

DeferredLightVertexShaders

// 输入参数.
struct FInputParams
{
    float2 PixelPos;
    float4 ScreenPosition;
    float2 ScreenUV;
    float3 ScreenVector;
};

// 派生参数.
struct FDerivedParams
{
    float3 CameraVector;
    float3 WorldPosition;
};

// 获取派生参数.
FDerivedParams GetDerivedParams(in FInputParams Input, in float SceneDepth)
{
    FDerivedParams Out;
#if LIGHT_SOURCE_SHAPE > 0
    // With a perspective projection, the clip space position is NDC * Clip.w
    // With an orthographic projection, clip space is the same as NDC
    float2 ClipPosition = Input.ScreenPosition.xy / Input.ScreenPosition.w * (View.ViewToClip[3][3] < 1.0f ? SceneDepth : 1.0f);
    Out.WorldPosition = mul(float4(ClipPosition, SceneDepth, 1), View.ScreenToWorld).xyz;
    Out.CameraVector = normalize(Out.WorldPosition - View.WorldCameraOrigin);
#else
    Out.WorldPosition = Input.ScreenVector * SceneDepth + View.WorldCameraOrigin;
    Out.CameraVector = normalize(Input.ScreenVector);
#endif
    return Out;
}

Texture2D<uint> LightingChannelsTexture;

uint GetLightingChannelMask(float2 UV)
{
	uint2 IntegerUV = UV * View.BufferSizeAndInvSize.xy;
	return LightingChannelsTexture.Load(uint3(IntegerUV, 0)).x;
}

float GetExposure()
{
	return View.PreExposure;
}

向往文章中的SetupLightDataForStandardDeferred()变为InitDeferredLightFromUniforms()。位于LightDataUniform.ush。

FDeferredLightData InitDeferredLightFromUniforms(uint InLightType)
{
	const bool bIsRadial = InLightType != LIGHT_TYPE_DIRECTIONAL;

	FDeferredLightData Out;
	Out.TranslatedWorldPosition = GetDeferredLightTranslatedWorldPosition();
	Out.InvRadius				= DeferredLightUniforms.InvRadius;
	Out.Color					= DeferredLightUniforms.Color;
	Out.FalloffExponent			= DeferredLightUniforms.FalloffExponent;
	Out.Direction				= DeferredLightUniforms.Direction;
	Out.Tangent					= DeferredLightUniforms.Tangent;
	Out.SpotAngles				= DeferredLightUniforms.SpotAngles;
	Out.SourceRadius			= DeferredLightUniforms.SourceRadius;
	Out.SourceLength			= bIsRadial ? DeferredLightUniforms.SourceLength : 0;
	Out.SoftSourceRadius		= DeferredLightUniforms.SoftSourceRadius;
	Out.SpecularScale			= DeferredLightUniforms.SpecularScale;
	Out.ContactShadowLength		= abs(DeferredLightUniforms.ContactShadowLength);
	Out.ContactShadowLengthInWS = DeferredLightUniforms.ContactShadowLength < 0.0f;
	Out.ContactShadowCastingIntensity = DeferredLightUniforms.ContactShadowCastingIntensity;
	Out.ContactShadowNonCastingIntensity = DeferredLightUniforms.ContactShadowNonCastingIntensity;
	Out.DistanceFadeMAD			= DeferredLightUniforms.DistanceFadeMAD;
	Out.ShadowMapChannelMask	= DeferredLightUniforms.ShadowMapChannelMask;
	Out.ShadowedBits			= DeferredLightUniforms.ShadowedBits;
	Out.bInverseSquared			= bIsRadial && DeferredLightUniforms.FalloffExponent == 0; // Directional lights don't use 'inverse squared attenuation'
	Out.bRadialLight			= bIsRadial;
	Out.bSpotLight				= InLightType == LIGHT_TYPE_SPOT;
	Out.bRectLight				= InLightType == LIGHT_TYPE_RECT;

	Out.RectLightData.BarnCosAngle				= DeferredLightUniforms.RectLightBarnCosAngle;
	Out.RectLightData.BarnLength				= DeferredLightUniforms.RectLightBarnLength;
	Out.RectLightData.AtlasData.AtlasMaxLevel	= DeferredLightUniforms.RectLightAtlasMaxLevel;
	Out.RectLightData.AtlasData.AtlasUVOffset	= DeferredLightUniforms.RectLightAtlasUVOffset;
	Out.RectLightData.AtlasData.AtlasUVScale	= DeferredLightUniforms.RectLightAtlasUVScale;

	Out.HairTransmittance		= InitHairTransmittanceData();
	return Out;
}

DeferredLightPixelMain

void DeferredLightPixelMain(
#if LIGHT_SOURCE_SHAPE > 0
	float4 InScreenPosition : TEXCOORD0,
#else
	float2 ScreenUV			: TEXCOORD0,
	float3 ScreenVector		: TEXCOORD1,
#endif
	float4 SVPos			: SV_POSITION,
	out float4 OutColor		: SV_Target0
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
	, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
	, out float3 OutSubSurfaceSceneColor : SV_Target2
#endif
	)
{
	const float2 PixelPos = SVPos.xy;
	OutColor = 0;
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
	OutOpaqueRoughRefractionSceneColor = 0;
	OutSubSurfaceSceneColor = 0;
#endif

	// Convert input data (directional/local light)
	// 计算屏幕UV
	FInputParams InputParams = (FInputParams)0;
	InputParams.PixelPos		= SVPos.xy;
#if LIGHT_SOURCE_SHAPE > 0
	InputParams.ScreenPosition	= InScreenPosition;
	InputParams.ScreenUV		= InScreenPosition.xy / InScreenPosition.w * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
	InputParams.ScreenVector	= 0;
#else
	InputParams.ScreenPosition	= 0;
	InputParams.ScreenUV		= ScreenUV;
	InputParams.ScreenVector	= ScreenVector;
#endif

#if STRATA_ENABLED

	FStrataAddressing StrataAddressing = GetStrataPixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Strata.MaxBytesPerPixel);
	FStrataPixelHeader StrataPixelHeader = UnpackStrataHeaderIn(Strata.MaterialTextureArray, StrataAddressing, Strata.TopLayerTexture);

	BRANCH
	if (StrataPixelHeader.BSDFCount > 0	// This test is also enough to exclude sky pixels
#if USE_LIGHTING_CHANNELS
		//灯光通道逻辑
		&& (GetLightingChannelMask(InputParams.ScreenUV) & DeferredLightUniforms.LightingChannelMask)
#endif
		) 
	{
		//通过SceneDepth获取的CameraVector以及当前像素的世界坐标
		const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
		const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);

		FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);
		UpdateLightDataColor(LightData, InputParams, DerivedParams);

		float3 V =-DerivedParams.CameraVector;
		float3 L = LightData.Direction;	// Already normalized
		float3 ToLight = L;
		float LightMask = 1;
		if (LightData.bRadialLight)
		{
			LightMask = GetLocalLightAttenuation(DerivedParams.TranslatedWorldPosition, LightData, ToLight, L);
		}

		if (LightMask > 0)
		{
			FShadowTerms ShadowTerms = { StrataGetAO(StrataPixelHeader), 1.0, 1.0, InitHairTransmittanceData() };
			float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);

			float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8);
			const uint FakeShadingModelID = 0;
			const float FakeContactShadowOpacity = 1.0f;
			float4 PrecomputedShadowFactors = StrataReadPrecomputedShadowFactors(StrataPixelHeader, PixelPos, SceneTexturesStruct.GBufferETexture);
			GetShadowTerms(SceneDepth, PrecomputedShadowFactors, FakeShadingModelID, FakeContactShadowOpacity,
				LightData, DerivedParams.TranslatedWorldPosition, L, LightAttenuation, Dither, ShadowTerms);

			FStrataDeferredLighting StrataLighting = StrataDeferredLighting(
				LightData,
				V,
				L,
				ToLight,
				LightMask,
				ShadowTerms,
				Strata.MaterialTextureArray,
				StrataAddressing,
				StrataPixelHeader);

			OutColor += StrataLighting.SceneColor;
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
			OutOpaqueRoughRefractionSceneColor += StrataLighting.OpaqueRoughRefractionSceneColor;
			OutSubSurfaceSceneColor += StrataLighting.SubSurfaceSceneColor;
#endif
		}
	}

#else // STRATA_ENABLED

	FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(InputParams.ScreenUV);
	// Only light pixels marked as using deferred shading
	BRANCH if (ScreenSpaceData.GBuffer.ShadingModelID > 0
#if USE_LIGHTING_CHANNELS
		&& (GetLightingChannelMask(InputParams.ScreenUV) & DeferredLightUniforms.LightingChannelMask)
#endif
		)
	{
		const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
		const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);

		FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);
		UpdateLightDataColor(LightData, InputParams, DerivedParams);

	 #if USE_HAIR_COMPLEX_TRANSMITTANCE
		if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_HAIR && ShouldUseHairComplexTransmittance(ScreenSpaceData.GBuffer))
		{
			LightData.HairTransmittance = EvaluateDualScattering(ScreenSpaceData.GBuffer, DerivedParams.CameraVector, -DeferredLightUniforms.Direction);
		}
	#endif

		float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8);

		float SurfaceShadow = 1.0f;
		
		float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);
		float4 Radiance = GetDynamicLighting(DerivedParams.TranslatedWorldPosition, DerivedParams.CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.ShadingModelID, LightData, LightAttenuation, Dither, uint2(InputParams.PixelPos), SurfaceShadow);

		OutColor += Radiance;
	}

#endif // STRATA_ENABLED

	// RGB:SceneColor Specular and Diffuse
	// A:Non Specular SceneColor Luminance
	// So we need PreExposure for both color and alpha
	OutColor.rgba *= GetExposure();
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED 
	// Idem
	OutOpaqueRoughRefractionSceneColor *= GetExposure();
	OutSubSurfaceSceneColor *= GetExposure();
#endif
}
#endif