816 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			816 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								title: Untitled
							 | 
						|||
| 
								 | 
							
								date: 2025-02-11 11:30:34
							 | 
						|||
| 
								 | 
							
								excerpt: 
							 | 
						|||
| 
								 | 
							
								tags: 
							 | 
						|||
| 
								 | 
							
								rating: ⭐
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								# FSortedLightSetSceneInfo
							 | 
						|||
| 
								 | 
							
								有序的光源集合相关定义:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								/** 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的赋值逻辑如下:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								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());
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								...
							 | 
						|||
| 
								 | 
							
								}	
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								获取有序的光源集合
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								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
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								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 
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								// 输入参数.
							 | 
						|||
| 
								 | 
							
								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。
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								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
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								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);//根据当前世界坐标计算LightData.Color *= 大气&云&阴影的衰减值 * IES灯亮度(非IES灯数值为1) 
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										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
							 | 
						|||
| 
								 | 
							
									//取得屏幕空间数据(FGbufferData、AO)
							 | 
						|||
| 
								 | 
							
									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
							 | 
						|||
| 
								 | 
							
										)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										//通过SceneDepth获取的CameraVector以及当前像素的世界坐标
							 | 
						|||
| 
								 | 
							
										const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
							 | 
						|||
| 
								 | 
							
										const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//设置获取光源各种信息
							 | 
						|||
| 
								 | 
							
										FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);
							 | 
						|||
| 
								 | 
							
										UpdateLightDataColor(LightData, InputParams, DerivedParams);//根据当前世界坐标计算LightData.Color *= 大气&云&阴影的衰减值 * IES灯亮度(非IES灯数值为1) 
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									 #if USE_HAIR_COMPLEX_TRANSMITTANCE
							 | 
						|||
| 
								 | 
							
										//针对ShadingModel Hair(同时需要CustomData.a > 0)计算头发散射结果
							 | 
						|||
| 
								 | 
							
										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);//根绝是否开启VSM 分别从VirtualShadowMap 或者 LightAttenuationTexture(上一阶段渲染的ShadowProjction) 获取灯光衰减值。
							 | 
						|||
| 
								 | 
							
										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
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#### GetLightAttenuationFromShadow() => GetPerPixelLightAttenuation()
							 | 
						|||
| 
								 | 
							
								原文:https://zhuanlan.zhihu.com/p/23216110797
							 | 
						|||
| 
								 | 
							
								有提到阴影模糊问题。
							 | 
						|||
| 
								 | 
							
								 FDeferredLightPS::FParameters GetDeferredLightPSParameters()可以看到该Sampler的模式是Point模式。
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								float4 GetPerPixelLightAttenuation(float2 UV)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									return DecodeLightAttenuation(Texture2DSampleLevel(LightAttenuationTexture, LightAttenuationTextureSampler, UV, 0));
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								之后可以仿照GetPerPixelLightAttenuation写一个针对ToonShadow的函数:
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								//对卡通阴影进行降采样抗锯齿  
							 | 
						|||
| 
								 | 
							
								float4 GetPerPixelLightAttenuationToonAA(float2 UV)  
							 | 
						|||
| 
								 | 
							
								{  
							 | 
						|||
| 
								 | 
							
								    int texture_x, texture_y;  
							 | 
						|||
| 
								 | 
							
								    LightAttenuationTexture.GetDimensions(texture_x, texture_y);  
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    float2 texelSize = float2(1.0 / texture_x, 1.0 / texture_y);  
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    float2 sampleOffsets[4] = {  
							 | 
						|||
| 
								 | 
							
								       float2(-1.5,  0.5),  
							 | 
						|||
| 
								 | 
							
								       float2( 0.5,  0.5),  
							 | 
						|||
| 
								 | 
							
								       float2(-1.5, -1.5),  
							 | 
						|||
| 
								 | 
							
								       float2( 0.5, -1.5)  
							 | 
						|||
| 
								 | 
							
								    };  
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    float4 shadowSum = float4(0,0,0,0);  
							 | 
						|||
| 
								 | 
							
								    for (int i = 0; i < 4; i++)  
							 | 
						|||
| 
								 | 
							
								    {  
							 | 
						|||
| 
								 | 
							
								       float2 sampleUV = UV + sampleOffsets[i] * texelSize;  
							 | 
						|||
| 
								 | 
							
								       shadowSum += DecodeLightAttenuation(Texture2DSampleLevel(LightAttenuationTexture, LightAttenuationTextureSampler_Toon, sampleUV, 0));  
							 | 
						|||
| 
								 | 
							
								    }  
							 | 
						|||
| 
								 | 
							
								    return shadowSum * 0.25;  
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								//获取卡通灯光衰减  
							 | 
						|||
| 
								 | 
							
								float4 GetLightAttenuationFromShadowToonAA(in FInputParams InputParams, float SceneDepth, float3 TranslatedWorldPosition)  
							 | 
						|||
| 
								 | 
							
								{  
							 | 
						|||
| 
								 | 
							
								    float4 LightAttenuation = float4(1, 1, 1, 1);  
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if USE_VIRTUAL_SHADOW_MAP_MASK  
							 | 
						|||
| 
								 | 
							
								    if (VirtualShadowMapId != INDEX_NONE)  
							 | 
						|||
| 
								 | 
							
								    {       
							 | 
						|||
| 
								 | 
							
									    float ShadowMask = GetVirtualShadowMapMaskForLight(ShadowMaskBits[InputParams.PixelPos], uint2(InputParams.PixelPos), SceneDepth, VirtualShadowMapId, TranslatedWorldPosition);       
							 | 
						|||
| 
								 | 
							
									    return ShadowMask.xxxx;  
							 | 
						|||
| 
								 | 
							
								    }else  
							 | 
						|||
| 
								 | 
							
								#endif  
							 | 
						|||
| 
								 | 
							
								    {  
							 | 
						|||
| 
								 | 
							
									    return GetPerPixelLightAttenuationToonAA(InputParams.ScreenUV);  
							 | 
						|||
| 
								 | 
							
								    }  
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								### GetDynamicLighting() => GetDynamicLightingSplit()
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								FDeferredLightingSplit GetDynamicLightingSplit(
							 | 
						|||
| 
								 | 
							
									float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID, 
							 | 
						|||
| 
								 | 
							
									FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos, 
							 | 
						|||
| 
								 | 
							
									inout float SurfaceShadow)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FLightAccumulator LightAccumulator = AccumulateDynamicLighting(TranslatedWorldPosition, CameraVector, GBuffer, AmbientOcclusion, ShadingModelID, LightData, LightAttenuation, Dither, SVPos, SurfaceShadow);
							 | 
						|||
| 
								 | 
							
									return LightAccumulator_GetResultSplit(LightAccumulator);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								LightAccumulator_GetResultSplit():针对Subsurface,`RetDiffuse.a =  In.ScatterableLightLuma;` 或者 `RetDiffuse.a = Luminance(In.ScatterableLight);`
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								FDeferredLightingSplit LightAccumulator_GetResultSplit(FLightAccumulator In)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									float4 RetDiffuse;
							 | 
						|||
| 
								 | 
							
									float4 RetSpecular;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (VISUALIZE_LIGHT_CULLING == 1)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										// a soft gradient from dark red to bright white, can be changed to be different
							 | 
						|||
| 
								 | 
							
										RetDiffuse = 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * In.EstimatedCost;
							 | 
						|||
| 
								 | 
							
										RetSpecular = 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * In.EstimatedCost;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									else
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										RetDiffuse = float4(In.TotalLightDiffuse, 0);
							 | 
						|||
| 
								 | 
							
										RetSpecular = float4(In.TotalLightSpecular, 0);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										//针对Subsurface会额外对RetDiffuse的Alpha设置数值 ScatterableLight的亮度数值
							 | 
						|||
| 
								 | 
							
										if (SUBSURFACE_CHANNEL_MODE == 1 )
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if (View.bCheckerboardSubsurfaceProfileRendering == 0)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
							 | 
						|||
| 
								 | 
							
												RetDiffuse.a = In.ScatterableLightLuma;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										else if (SUBSURFACE_CHANNEL_MODE == 2)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
							 | 
						|||
| 
								 | 
							
											// 3 add,  1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
							 | 
						|||
| 
								 | 
							
											RetDiffuse.a = Luminance(In.ScatterableLight);
							 | 
						|||
| 
								 | 
							
											// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FDeferredLightingSplit Ret;
							 | 
						|||
| 
								 | 
							
									Ret.DiffuseLighting = RetDiffuse;
							 | 
						|||
| 
								 | 
							
									Ret.SpecularLighting = RetSpecular;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return Ret;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								#### AccumulateDynamicLighting
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								FLightAccumulator AccumulateDynamicLighting(
							 | 
						|||
| 
								 | 
							
									float3 TranslatedWorldPosition, half3 CameraVector, FGBufferData GBuffer, half AmbientOcclusion, uint ShadingModelID,
							 | 
						|||
| 
								 | 
							
									FDeferredLightData LightData, half4 LightAttenuation, float Dither, uint2 SVPos, 
							 | 
						|||
| 
								 | 
							
									inout float SurfaceShadow)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FLightAccumulator LightAccumulator = (FLightAccumulator)0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									half3 V = -CameraVector;
							 | 
						|||
| 
								 | 
							
									half3 N = GBuffer.WorldNormal;
							 | 
						|||
| 
								 | 
							
									//针对开启CLEAR_COAT_BOTTOM_NORMAL的清漆ShadingModel进行Normal处理
							 | 
						|||
| 
								 | 
							
									BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
							 | 
						|||
| 
								 | 
							
										N = OctahedronToUnitVector(oct1);			
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									float3 L = LightData.Direction;	// Already normalized
							 | 
						|||
| 
								 | 
							
									float3 ToLight = L;
							 | 
						|||
| 
								 | 
							
									float3 MaskedLightColor = LightData.Color;//灯光颜色
							 | 
						|||
| 
								 | 
							
									float LightMask = 1;
							 | 
						|||
| 
								 | 
							
									// 获取辐射光源的衰减值,衰减方法根据LightData.bInverseSquared,会分别使用新版衰减方法InverseSquared 或者 旧方法。如果是SpotLight与RectLight就乘以SpotLight、RectLight对应的形状衰减数值。
							 | 
						|||
| 
								 | 
							
									if (LightData.bRadialLight)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L );
							 | 
						|||
| 
								 | 
							
										MaskedLightColor *= LightMask;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									LightAccumulator.EstimatedCost += 0.3f;		// running the PixelShader at all has a cost
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									BRANCH
							 | 
						|||
| 
								 | 
							
									if( LightMask > 0 )//如果不是完全死黑就计算阴影部分逻辑
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FShadowTerms Shadow;
							 | 
						|||
| 
								 | 
							
										Shadow.SurfaceShadow = AmbientOcclusion;//GBuffer中的AO
							 | 
						|||
| 
								 | 
							
										Shadow.TransmissionShadow = 1;
							 | 
						|||
| 
								 | 
							
										Shadow.TransmissionThickness = 1;
							 | 
						|||
| 
								 | 
							
										Shadow.HairTransmittance.OpaqueVisibility = 1;
							 | 
						|||
| 
								 | 
							
										const float ContactShadowOpacity = GBuffer.CustomData.a;//TODO:修正ToonStandard对应的逻辑
							 | 
						|||
| 
								 | 
							
										//
							 | 
						|||
| 
								 | 
							
										GetShadowTerms(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity,
							 | 
						|||
| 
								 | 
							
											LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow);
							 | 
						|||
| 
								 | 
							
										SurfaceShadow = Shadow.SurfaceShadow;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										LightAccumulator.EstimatedCost += 0.3f;		// add the cost of getting the shadow terms
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if SHADING_PATH_MOBILE
							 | 
						|||
| 
								 | 
							
										const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
							 | 
						|||
| 
								 | 
							
										
							 | 
						|||
| 
								 | 
							
										FDirectLighting Lighting = (FDirectLighting)0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										half NoL = max(0, dot(GBuffer.WorldNormal, L));
							 | 
						|||
| 
								 | 
							
									#if TRANSLUCENCY_NON_DIRECTIONAL
							 | 
						|||
| 
								 | 
							
										NoL = 1.0f;
							 | 
						|||
| 
								 | 
							
									#endif
							 | 
						|||
| 
								 | 
							
										Lighting = EvaluateBxDF(GBuffer, N, V, L, NoL, Shadow);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										Lighting.Specular *= LightData.SpecularScale;
							 | 
						|||
| 
								 | 
							
												
							 | 
						|||
| 
								 | 
							
										LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
							 | 
						|||
| 
								 | 
							
										LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
							 | 
						|||
| 
								 | 
							
								#else // SHADING_PATH_MOBILE
							 | 
						|||
| 
								 | 
							
										BRANCH
							 | 
						|||
| 
								 | 
							
										if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);//判断是否需要SubsurfaceProfile计算
							 | 
						|||
| 
								 | 
							
										#if NON_DIRECTIONAL_DIRECT_LIGHTING // 非平行直接光
							 | 
						|||
| 
								 | 
							
											float Lighting;
							 | 
						|||
| 
								 | 
							
											if( LightData.bRectLight )//面光源
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												FRect Rect = GetRect( ToLight, LightData );
							 | 
						|||
| 
								 | 
							
												Lighting = IntegrateLight( Rect );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
											else //点光源以及胶囊光源
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
							 | 
						|||
| 
								 | 
							
												Lighting = IntegrateLight( Capsule, LightData.bInverseSquared );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											float3 LightingDiffuse = Diffuse_Lambert( GBuffer.DiffuseColor ) * Lighting;//Lambert照明 * 积分结果
							 | 
						|||
| 
								 | 
							
											LightAccumulator_AddSplit(LightAccumulator, LightingDiffuse, 0.0f, 0, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation);
							 | 
						|||
| 
								 | 
							
										#else
							 | 
						|||
| 
								 | 
							
											FDirectLighting Lighting;
							 | 
						|||
| 
								 | 
							
											if (LightData.bRectLight)//面光源
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												FRect Rect = GetRect( ToLight, LightData );
							 | 
						|||
| 
								 | 
							
												const FRectTexture SourceTexture = ConvertToRectTexture(LightData);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												#if REFERENCE_QUALITY
							 | 
						|||
| 
								 | 
							
													Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );
							 | 
						|||
| 
								 | 
							
												#else
							 | 
						|||
| 
								 | 
							
													Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
							 | 
						|||
| 
								 | 
							
												#endif
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
											else //点光源以及胶囊光源
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												#if REFERENCE_QUALITY
							 | 
						|||
| 
								 | 
							
													Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
							 | 
						|||
| 
								 | 
							
												#else
							 | 
						|||
| 
								 | 
							
													Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
							 | 
						|||
| 
								 | 
							
												#endif
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											Lighting.Specular *= LightData.SpecularScale;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											//累加Diffuse + Specular光照结果(Diffuse项还会作为散射进行计算,根绝散射模式不同赋予 FLightAccumulator.ScatterableLightLuma 或者 FLightAccumulator.ScatterableLight)
							 | 
						|||
| 
								 | 
							
											LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
							 | 
						|||
| 
								 | 
							
											//散射项计算
							 | 
						|||
| 
								 | 
							
											LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											LightAccumulator.EstimatedCost += 0.4f;		// add the cost of the lighting computations (should sum up to 1 form one light)
							 | 
						|||
| 
								 | 
							
										#endif
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								#endif // SHADING_PATH_MOBILE
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									return LightAccumulator;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								光源新衰减公式,相关计算位于`GetLocalLightAttenuation()`:
							 | 
						|||
| 
								 | 
							
								$$Falloff = \frac{saturate(1-(distance/lightRadius)^4)^2}{distance^2 + 1}$$
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								光源旧衰减公式,相关函数位于DynamicLightingCommon.ush中的`RadialAttenuation()`
							 | 
						|||
| 
								 | 
							
								$$Falloff = (1 - saturate(length(WorldLightVector)))^ {FalloffExponent}$$
							 | 
						|||
| 
								 | 
							
								##### GetShadowTerms()
							 | 
						|||
| 
								 | 
							
								```c++
							 | 
						|||
| 
								 | 
							
								void GetShadowTerms(float SceneDepth, half4 PrecomputedShadowFactors, uint ShadingModelID, float ContactShadowOpacity, FDeferredLightData LightData, float3 TranslatedWorldPosition, half3 L, half4 LightAttenuation, float Dither, inout FShadowTerms Shadow)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									float ContactShadowLength = 0.0f;
							 | 
						|||
| 
								 | 
							
									const float ContactShadowLengthScreenScale = GetTanHalfFieldOfView().y * SceneDepth;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									BRANCH
							 | 
						|||
| 
								 | 
							
									if (LightData.ShadowedBits)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										// 重新映射ShadowProjection结果
							 | 
						|||
| 
								 | 
							
										// Remapping the light attenuation buffer (see ShadowRendering.cpp)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// LightAttenuation: Light function + per-object shadows in z, per-object SSS shadowing in w, 
							 | 
						|||
| 
								 | 
							
										// Whole scene directional light shadows in x, whole scene directional light SSS shadows in y
							 | 
						|||
| 
								 | 
							
										// Get static shadowing from the appropriate GBuffer channel
							 | 
						|||
| 
								 | 
							
								#if ALLOW_STATIC_LIGHTING
							 | 
						|||
| 
								 | 
							
										half UsesStaticShadowMap = dot(LightData.ShadowMapChannelMask, half4(1, 1, 1, 1));
							 | 
						|||
| 
								 | 
							
										half StaticShadowing = lerp(1, dot(PrecomputedShadowFactors, LightData.ShadowMapChannelMask), UsesStaticShadowMap);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
										half StaticShadowing = 1.0f;
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (LightData.bRadialLight || SHADING_PATH_MOBILE)//RadialLight或者是移动端使用以下逻辑。bRadialLight一般是 PointLight or SpotLight。径向衰减(radial attenuation):指光照强度随距离光源的远近而衰减的特性(通常遵循平方反比定律)。
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											// Remapping the light attenuation buffer (see ShadowRendering.cpp)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											Shadow.SurfaceShadow = LightAttenuation.z * StaticShadowing;//RadialLight灯光的阴影项计算不受AO影响,赋值Light function + per-object的ShadowProjection
							 | 
						|||
| 
								 | 
							
											// SSS uses a separate shadowing term that allows light to penetrate the surface
							 | 
						|||
| 
								 | 
							
											//@todo - how to do static shadowing of SSS correctly?
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionShadow = LightAttenuation.w * StaticShadowing;//per-object SSS shadowing 
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionThickness = LightAttenuation.w;//per-object SSS shadowing 
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										else
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											// Remapping the light attenuation buffer (see ShadowRendering.cpp)
							 | 
						|||
| 
								 | 
							
											// Also fix up the fade between dynamic and static shadows
							 | 
						|||
| 
								 | 
							
											// to work with plane splits rather than spheres.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											float DynamicShadowFraction = DistanceFromCameraFade(SceneDepth, LightData);
							 | 
						|||
| 
								 | 
							
											// For a directional light, fade between static shadowing and the whole scene dynamic shadowing based on distance + per object shadows
							 | 
						|||
| 
								 | 
							
											Shadow.SurfaceShadow = lerp(LightAttenuation.x, StaticShadowing, DynamicShadowFraction);//根据计算出动态阴影的衰减值来插值ShadowProject与静态阴影。x:方向光阴影
							 | 
						|||
| 
								 | 
							
											// Fade between SSS dynamic shadowing and static shadowing based on distance
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionShadow = min(lerp(LightAttenuation.y, StaticShadowing, DynamicShadowFraction), LightAttenuation.w);// w:per-object SSS shadowing
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											Shadow.SurfaceShadow *= LightAttenuation.z;//Light function + per-object shadows in z
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionShadow *= LightAttenuation.z;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Need this min or backscattering will leak when in shadow which cast by non perobject shadow(Only for directional light)
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionThickness = min(LightAttenuation.y, LightAttenuation.w);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										FLATTEN
							 | 
						|||
| 
								 | 
							
										if (LightData.ShadowedBits > 1 && LightData.ContactShadowLength > 0)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											ContactShadowLength = LightData.ContactShadowLength * (LightData.ContactShadowLengthInWS ? 1.0f : ContactShadowLengthScreenScale);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if SUPPORT_CONTACT_SHADOWS //接触阴影相关逻辑
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if STRATA_ENABLED == 0
							 | 
						|||
| 
								 | 
							
									if (LightData.ShadowedBits < 2 && (ShadingModelID == SHADINGMODELID_HAIR))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									// World space distance to cover eyelids and eyelashes but not beyond
							 | 
						|||
| 
								 | 
							
									if (ShadingModelID == SHADINGMODELID_EYE)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										ContactShadowLength = 0.5;
							 | 
						|||
| 
								 | 
							
										
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									#if MATERIAL_CONTACT_SHADOWS
							 | 
						|||
| 
								 | 
							
										ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
							 | 
						|||
| 
								 | 
							
									#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									BRANCH
							 | 
						|||
| 
								 | 
							
									if (ContactShadowLength > 0.0)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										float StepOffset = Dither - 0.5;
							 | 
						|||
| 
								 | 
							
										bool bHitCastContactShadow = false;
							 | 
						|||
| 
								 | 
							
										bool bHairNoShadowLight = ShadingModelID == SHADINGMODELID_HAIR && !LightData.ShadowedBits;
							 | 
						|||
| 
								 | 
							
										float HitDistance = ShadowRayCast( TranslatedWorldPosition, L, ContactShadowLength, 8, StepOffset, bHairNoShadowLight, bHitCastContactShadow );//通过RayMarching来计算是否HitContactShadow以及HitDistance。
							 | 
						|||
| 
								 | 
							
												
							 | 
						|||
| 
								 | 
							
										if ( HitDistance > 0.0 )
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											float ContactShadowOcclusion = bHitCastContactShadow ? LightData.ContactShadowCastingIntensity : LightData.ContactShadowNonCastingIntensity;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if STRATA_ENABLED == 0
							 | 
						|||
| 
								 | 
							
											// Exponential attenuation is not applied on hair/eye/SSS-profile here, as the hit distance (shading-point to blocker) is different from the estimated 
							 | 
						|||
| 
								 | 
							
											// thickness (closest-point-from-light to shading-point), and this creates light leaks. Instead we consider first hit as a blocker (old behavior)
							 | 
						|||
| 
								 | 
							
											BRANCH
							 | 
						|||
| 
								 | 
							
											if (ContactShadowOcclusion > 0.0 && 
							 | 
						|||
| 
								 | 
							
												IsSubsurfaceModel(ShadingModelID) &&
							 | 
						|||
| 
								 | 
							
												ShadingModelID != SHADINGMODELID_HAIR &&
							 | 
						|||
| 
								 | 
							
												ShadingModelID != SHADINGMODELID_EYE &&
							 | 
						|||
| 
								 | 
							
												ShadingModelID != SHADINGMODELID_SUBSURFACE_PROFILE)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												// Reduce the intensity of the shadow similar to the subsurface approximation used by the shadow maps path
							 | 
						|||
| 
								 | 
							
												// Note that this is imperfect as we don't really have the "nearest occluder to the light", but this should at least
							 | 
						|||
| 
								 | 
							
												// ensure that we don't darken-out the subsurface term with the contact shadows
							 | 
						|||
| 
								 | 
							
												float Density = SubsurfaceDensityFromOpacity(ContactShadowOpacity);
							 | 
						|||
| 
								 | 
							
												ContactShadowOcclusion *= 1.0 - saturate( exp( -Density * HitDistance ) );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
											
							 | 
						|||
| 
								 | 
							
											float ContactShadow = 1.0 - ContactShadowOcclusion;
							 | 
						|||
| 
								 | 
							
											//根据是否命中赋予对应的ContactShadow亮度数值,之后乘以Shadow.SurfaceShadow与Shadow.TransmissionShadow。
							 | 
						|||
| 
								 | 
							
											Shadow.SurfaceShadow *= ContactShadow;
							 | 
						|||
| 
								 | 
							
											Shadow.TransmissionShadow *= ContactShadow;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Shadow.HairTransmittance = LightData.HairTransmittance;
							 | 
						|||
| 
								 | 
							
									Shadow.HairTransmittance.OpaqueVisibility = Shadow.SurfaceShadow;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								```
							 |