BlueRoseNote/03-UnrealEngine/Rendering/RayTracing/UE5RayTracing渲染管线笔记——(1).md
2023-06-29 11:55:02 +08:00

331 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: UE5RayTracing渲染管线笔记——(1)
date: 2022-08-09 13:55:15
tags: RayTracing
rating: ⭐️⭐️
---
## 任务
- [ ] 查看这个如何针对各个View构建场景
## 渲染事件
RayTracingScene位于LumenSceneUpdate之后。
## 收集场景信息
### 场景信息
在FScene中定了这2个变量来存储RayTracing专用的场景信息
```c#
FRayTracingScene RayTracingScene;
TArray<FLightSceneInfo*, TInlineAllocator<4>> RayTracedLights;
```
FRayTracingScene还存储着FRayTracingGeometryInstance数组、TArray<const FRayTracingGeometry*> GeometriesToBuild、RayTracingSceneBuffer、RayTracingSceneSRV。
### 收集过程
主要的逻辑位于GatherRayTracingWorldInstancesForView()中通过RayTracingCollector来收集场景中的图元。在FSceneRenderer定义了MeshCollector与RayTracingCollector其中MeshCollector的GatherDynamicMeshElements()在计算可见性阶段被调用()。
```c#
// Gather mesh instances, shaders, resources, parameters, etc. and build ray tracing acceleration structure
FRayTracingScene& RayTracingScene = Scene->RayTracingScene;
RayTracingScene.Reset(); // Resets the internal arrays, but does not release any resources.
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
// Prepare the scene for rendering this frame.
GatherRayTracingWorldInstancesForView(GraphBuilder, ReferenceView, RayTracingScene);
```
- GatherRayTracingWorldInstancesForView()
- FGPUScenePrimitiveCollector DummyDynamicPrimitiveCollector;
- 给RayTracingCollector的内部变量赋值RayTracingCollector.AddViewMeshArrays(&View,&View.RayTracedDynamicMeshElements,&View.SimpleElementCollector,&DummyDynamicPrimitiveCollector,ViewFamily.GetFeatureLevel(),&DynamicIndexBufferForInitViews,&DynamicVertexBufferForInitViews,&DynamicReadBufferForInitViews);
- 创建Mesh资源收集器`View.RayTracingMeshResourceCollector = MakeUnique<FRayTracingMeshResourceCollector>(...);`
- 初始化Rtx裁剪变量View.RayTracingCullingParameters.Init(View);
- 创建FRayTracingMaterialGatheringContext MaterialGatheringContext{Scene,&View,ViewFamily,GraphBuilder,*View.RayTracingMeshResourceCollector};
- 声明FRelevantPrimitive结构体实现InstancingKey()用于返回图元类型掩码。并定义FRelevantPrimitive数组长度为场景图元总数。
- 遍历所有图元,
### 加速结构构建
>RayTracingGem中有提到了加速结构的Rebuild与Refit概念。
该步骤会在BasePass()之前调用。DispatchRayTracingWorldUpdates()的注释说:
>异步构建可能会与BasePass重合。 Async AS builds can potentially overlap with BasePass
GeometriesToBuild在GatherRayTracingWorldInstancesForView()被填充之后在DispatchRayTracingWorldUpdates()中通过**GRayTracingGeometryManager.ForceBuildIfPending(GraphBuilder.RHICmdList, RayTracingScene.GeometriesToBuild);**更新。
- FRayTracingGeometryManager GRayTracingGeometryManager全局的场景管理类。
- ForceBuildIfPending():添加需要强制构建的多边形。
- ProcessBuildRequests():在排序请求后,调用**InCmdList.BuildAccelerationStructures(BuildParams);**构建加速结构。Render() 2634 =>DispatchRayTracingWorldUpdates()=>ProcessBuildRequests()=>InCmdList.BuildAccelerationStructures(BuildParams);
加速结构存在一个UAV上以FRayTracingGeometryBuildParams为单位。里面存储了`FRayTracingGeometryRHIRef Geometry、BuildMode、以及TArrayView<const FRayTracingGeometrySegment> Segments;`
### FRayTracingScene
使用这个类来管理Rtx场景。
## RayTracingCommon.h
UE使用宏来简化RayTracingShader的编写。 比如RayTracingShader入口函数
```c++
#ifndef RAY_TRACING_ENTRY_RAYGEN
#define RAY_TRACING_ENTRY_RAYGEN(name)\
[shader("raygeneration")] void name()
#endif // RAY_TRACING_ENTRY_RAYGEN
#ifndef RAY_TRACING_ENTRY_INTERSECTION
#define RAY_TRACING_ENTRY_INTERSECTION(name)\
[shader("intersection")] void name()
#endif //RAY_TRACING_ENTRY_INTERSECTION
#ifndef RAY_TRACING_ENTRY_CLOSEST_HIT
#define RAY_TRACING_ENTRY_CLOSEST_HIT(name, payload_type, payload_name, attributes_type, attributes_name)\
[shader("closesthit")] void name(inout payload_type payload_name, in attributes_type attributes_name)
#endif //RAY_TRACING_ENTRY_CLOSEST_HIT
#ifndef RAY_TRACING_ENTRY_ANY_HIT
#define RAY_TRACING_ENTRY_ANY_HIT(name, payload_type, payload_name, attributes_type, attributes_name)\
[shader("anyhit")] void name(inout payload_type payload_name, in attributes_type attributes_name)
#endif // RAY_TRACING_ENTRY_ANY_HIT
#ifndef RAY_TRACING_ENTRY_MISS
#define RAY_TRACING_ENTRY_MISS(name, payload_type, payload_name)\
[shader("miss")] void name(inout payload_type payload_name)
#endif //RAY_TRACING_ENTRY_MISS
```
所以Name需要与**IMPLEMENT_GLOBAL_SHADER**中定义的Shader入口函数名相同。
RayTracing函数
- FMinimalPayload Payload=TraceVisibilityRay()
- FMaterialClosestHitPayload Payload = TraceMaterialRay()
以及其他工具函数:
- DispatchRaysIndex()
- GetPixelCoord()
## RenderRayTracingReflections
- SortedDeferred
FRayTracingDeferredReflectionsRGS
## RenderDiffuseIndirectAndAmbientOcclusion
RenderRayTracingAmbientOcclusion()
- 遍历每个View
- 计算使用对应方式计算GI。将结果传递给FDiffuseIndirectInputs对象。
- 通过AmbientOcclusionRGS()RayTracing降噪得到AmbientOcclusionMask并传递给FDiffuseIndirectInputs.AmbientOcclusionMask。
- 如果有头发会多渲染头发的AO。
- 调用FDiffuseIndirectCompositePS将之前的渲染结果与GI、AO效果合成在一起。
FDiffuseIndirectCompositePS()
### AmbientOcclusionRGS
RayTracingAmbientOcclusionRGS.usf
- 计算UV、当前像素的FGBufferData以及WorldPosition与CameraDirection
- 对于非SHADINGMODELID_TWOSIDED_FOLIAGE并且开启CONFIG_SHOOT_WITH_GEOMETRIC_NORMAL则重新计算法线
- 通过HalfFOV * WorldDepth * ViewInvSize.z计算像素半径
- 计算通过DDX与DDY计算几何法线。
- 计算3个球形高斯分布没看懂
- 初始化RayTracing相关变量开始RayTracing。如果不开启追踪将**Visibility = 1.0;RayCount = SamplesPerPixel;SamplesPerPixelLocal = 0.0;**
- 使用RandomSequence生成随机样本。之后调用GenerateCosineNormalRay()生成Ray。
1. 调用RandomSequence_GenerateSample2D()取得2维随机样本。默认使用Sobol低差异序列其他还有Halton与Hash随机(https://github.com/skeeto/hash-prospector)
2. 进行余弦-半球采样并转换局部坐标为世界空间。
3. 完成Ray的初始化。
- 调用ApplyCameraRelativeDepthBias()对Ray的起点进行摄像机->像素坐标方向的偏移一个ε,以解决浮点数不精确的问题。
- 计算光线采样权重, **max(dot(WorldNormal, Ray.Direction), 0.05) / max(RayPDF, 0.05);**
- 调用TraceVisibilityRay()进行RayTracing。
- 累加采样结果。Tracing范围内没有遮挡物Visibility就是1否则就是1-IntensityLocal。该值为后处理空间里设定的AO亮度值RayTracingAOIntensity。如果Ray Hit还会设置新的ClosestRayHitDistance值。
- 输出结果。 OcclusionMask=ShadingDotGeometric * (Visibility / RayCount);HitDistance=ClosestRayHitDistance;这两个UAV都是屏幕空间降噪器的贴图变量Shader处理完之后会进行降噪处理
- 否则直接使用世界法线ShadingDotGeometric=1.0
## RenderRayTracingSkyLight
- 初始化FPathTracingSkylight SkylightParameters与FSkyLightData SkyLightData如果天光功能未开启则返回黑色OutSkyLightTexture与OutHitDistanceTexture。
- 使用CVarRayTracingSkyLightScreenPercentage计算ResolutionFraction。
- 创建RDG Texture资源RayTracingSkylight与RayTracingSkyLightHitDistance。
- 调用GenerateSkyLightVisibilityRays()生成Ray样本集256*256格式为`RWStructuredBuffer<SkyLightVisibilityRays>``SkyLightVisibilityRays为方向与PDF值float4 DirectionAndPdf;`。
- FGenerateSkyLightVisibilityRaysCS的流程
1. 计算坐标UAV坐标与SkyLightSamplingStrategyPdf会使用SkylightPdf
- 大概率是在PrepareSkyTexture()中进行了资源绑定SkylightParameters->SkylightPdf = GraphBuilder.RegisterExternalTexture(Scene->PathTracingSkylightPdf, TEXT("PathTracer.SkylightPdf"));
2. 计算每个像素数据。
1. 生成随机序列使用Hilbert curve算法: https://github.com/hcs0/Hackers-Delight/blob/master/hilbert/hil_s_from_xy.c.txt
2. 使用Sobol算子采样来得到样本。
3. 使用样本来计算天光采样结果,这一步会根据上下半球进行区分。
- ```FSkyLightSample {float3 Direction;float3 Radiance;float Pdf;};```
4. 计算最终的半球混合PDFfloat MisWeightOverPdf = 1.0 / lerp(UniformPdf, SkyLightPdf, SkyLightSamplingStrategyPdf);
5. 计算Ray的Index并将结果写入。
- 创建用于输出结果的UAV对象OutSkyLightTexture、OutHitDistanceTexture并且取得SceneTextures。
- 遍历所有View计算天光结果。
- 填充FRayTracingSkyLightRGS::FParameters。如果视口内有头发将会额外绑定HairStrandsVoxelUniformParameters。
- 设置FRayTracingSkyLightRGS变体。
- 计算FIntPoint RayTracingResolution = View.ViewRect.Size() / UpscaleFactor;
- RayTraceDispatch()。
- Denoising
- 如果SceneViewState有效返回SkyLightVisibilityRaysDimensions。
- 合成SkyLight
### FRayTracingSkyLightRGS
FRayTracingSkyLightRGS是一个GlobalShader但因为是一个RayTracing Shader所以宏的类型为
```c++
IMPLEMENT_GLOBAL_SHADER(FRayTracingSkyLightRGS, "/Engine/Private/Raytracing/RaytracingSkylightRGS.usf", "SkyLightRGS", SF_RayGen);
```
TLAS数据位于通过Scene->RayTracingScene->RayTracingSceneSRV。
```c++
PassParameters->TLAS = View.GetRayTracingSceneViewChecked();
FRHIShaderResourceView* FViewInfo::GetRayTracingSceneViewChecked() const
{
FRHIShaderResourceView* Result = nullptr;
check(Family);
if (Family->Scene)
{
if (FScene* Scene = Family->Scene->GetRenderScene())
{
Result = Scene->RayTracingScene.GetShaderResourceViewChecked();
}
}
checkf(Result, TEXT("Ray tracing scene SRV is expected to be created at this point."));
return Result;
}
FRHIShaderResourceView* FRayTracingScene::GetShaderResourceViewChecked() const
{
checkf(RayTracingSceneSRV.IsValid(), TEXT("Ray tracing scene SRV was not created. Perhaps BeginCreate() was not called."));
return RayTracingSceneSRV.GetReference();
}
```
#### AddPass
RayGem的AddPass()标记为ERDGPassFlags::Compute。RHICmdList.RayTraceDispatch()需要RayGem管线状态、Shader、RayTracingSceneRHI、RayTracing资源与分辨率。
FRayTracingPipelineStateInitializer管线状态需要:
- MaxPayloadSizeInBytes
- RayGenShaderTable
- HitGroupTable
- bAllowHitGroupIndexing
```c++
FIntPoint RayTracingResolution = View.ViewRect.Size() / UpscaleFactor;
GraphBuilder.AddPass(
RDG_EVENT_NAME("SkyLightRayTracing %dx%d", RayTracingResolution.X, RayTracingResolution.Y),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, this, &View, RayGenerationShader, RayTracingResolution](FRHIRayTracingCommandList& RHICmdList)
{
//资源绑定将Texture、UniformStruct绑定的工具函数
FRayTracingShaderBindingsWriter GlobalResources;
SetShaderParameters(GlobalResources, RayGenerationShader, *PassParameters);
//取得RayTracing管线状态
FRayTracingPipelineState* Pipeline = View.RayTracingMaterialPipeline;
//如果没开启RayTracing天光材质则重新创建一个RayTracing管线状态。看得出主要需求RayGemShader与HitGroupTable
if (CVarRayTracingSkyLightEnableMaterials.GetValueOnRenderThread() == 0)
{
// Declare default pipeline
FRayTracingPipelineStateInitializer Initializer;
Initializer.MaxPayloadSizeInBytes = RAY_TRACING_MAX_ALLOWED_PAYLOAD_SIZE; // sizeof(FPackedMaterialClosestHitPayload)
FRHIRayTracingShader* RayGenShaderTable[] = { RayGenerationShader.GetRayTracingShader() };
Initializer.SetRayGenShaderTable(RayGenShaderTable);
FRHIRayTracingShader* HitGroupTable[] = { View.ShaderMap->GetShader<FOpaqueShadowHitGroup>().GetRayTracingShader() };
Initializer.SetHitGroupTable(HitGroupTable);
Initializer.bAllowHitGroupIndexing = false; // Use the same hit shader for all geometry in the scene by disabling SBT indexing.
Pipeline = PipelineStateCache::GetAndOrCreateRayTracingPipelineState(RHICmdList, Initializer);
}
FRHIRayTracingScene* RayTracingSceneRHI = View.GetRayTracingSceneChecked();
RHICmdList.RayTraceDispatch(Pipeline, RayGenerationShader.GetRayTracingShader(), RayTracingSceneRHI, GlobalResources, RayTracingResolution.X, RayTracingResolution.Y);
});
```
#### Shader
- 计算DispatchThreadId以及对应的屏幕UV。并且取得对应的FGBufferData。
- 计算出WorldPosition以及CameraDirection。
- 判断是否需要追踪光线: 是否是有限深度 && 当前像素的ShaderModel不是Unlit。如果需要追踪采样数为传入Shader的 SkyLight.SamplesPerPixel否则为0。
- 调用SkyLightEvaluate(),进行光追计算。
- 初始化相关函数。
- 计算天光采样PDF。
- 采样循环
- 根据bDecoupleSampleGeneration()选择执行使用SkyLightVisibilityRays的样本 或者使用随机序列生成样本。
- 如果当前像素的ShadingModel是Hair需要重新计算CurrentWorldNormal。
- 偏移当前光线的深度并计算NoL。
- 设置RayFlags并且调用TraceVisibilityRay()进行光线追踪。返回FMinimalPayload存光线命中距离信息
- 如果命中累加RayDistance与HitCount。如没命中累加BentNormal并且计算FDirectLighting光照计算Hair会用另一套计算方式最后累加ExitantRadiance、DiffuseThroughput、DiffuseExitantRadiance。
- ExitantRadiance、DiffuseThroughput、DiffuseExitantRadiance除以样本数目HitDistance = RayDistance / HitCount
- 如果当前像素的ShadingModel是Hair增加头发多重散射贡献值。
- 合成估算结果。DiffuseExitantRadiance.r = Albedo.r > 0.0 ? DiffuseExitantRadiance.r / Albedo.r : DiffuseExitantRadiance.r;
- 乘以曝光值。
- 返回RWSkyOcclusionMaskUAV[DispatchThreadId]=float4(ClampToHalfFloatRange(DiffuseExitantRadiance.rgb), AmbientOcclusion);RWSkyOcclusionRayDistanceUAV[DispatchThreadId] = float2(HitDistance, SamplesPerPixel);
#### 降噪Denoising
- 调用IScreenSpaceDenoiser接口取得默认降噪器
- 设置IScreenSpaceDenoiser::FDiffuseIndirectInputs的Color与RayHitDistance(OutSkyLightTexture、OutHitDistanceTexture)
- 设置IScreenSpaceDenoiser::FAmbientOcclusionRayTracingConfig的ResolutionFraction与RayCountPerPixel(ResolutionFraction、GetSkyLightSamplesPerPixel(SkyLight))
- 调用DenoiseSkyLight()输出降噪后的结果覆盖OutSkyLightTexture。
```c++
if (GRayTracingSkyLightDenoiser != 0)
{
//取得默认降噪器
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
const IScreenSpaceDenoiser* DenoiserToUse = DefaultDenoiser;// GRayTracingGlobalIlluminationDenoiser == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
//降噪器变量结构体需要使用之前的渲染结果以及Hit距离结果
IScreenSpaceDenoiser::FDiffuseIndirectInputs DenoiserInputs;
DenoiserInputs.Color = OutSkyLightTexture;
DenoiserInputs.RayHitDistance = OutHitDistanceTexture;
{
//初始化RayTracingConfig
IScreenSpaceDenoiser::FAmbientOcclusionRayTracingConfig RayTracingConfig;
RayTracingConfig.ResolutionFraction = ResolutionFraction;
RayTracingConfig.RayCountPerPixel = GetSkyLightSamplesPerPixel(SkyLight);
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(SkyLight) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
View.ViewRect.Width(), View.ViewRect.Height());
//降噪
IScreenSpaceDenoiser::FDiffuseIndirectOutputs DenoiserOutputs = DenoiserToUse->DenoiseSkyLight(
GraphBuilder,
View,
&View.PrevViewInfo,
SceneTextures,
DenoiserInputs,
RayTracingConfig);
//输出结果
OutSkyLightTexture = DenoiserOutputs.Color;
}
}
```
## RenderRayTracingDebug
位于渲染Fog与Translucency、VirtualTextureFeedbackEnd()之后
```c++
#if RHI_RAYTRACING
if (IsRayTracingEnabled())
{
// Path tracer requires the full ray tracing pipeline support, as well as specialized extra shaders.
// Most of the ray tracing debug visualizations also require the full pipeline, but some support inline mode.
if (ViewFamily.EngineShowFlags.PathTracing
&& FDataDrivenShaderPlatformInfo::GetSupportsPathTracing(Scene->GetShaderPlatform()))
{
for (const FViewInfo& View : Views)
{
RenderPathTracing(GraphBuilder, View, SceneTextures.UniformBuffer, SceneTextures.Color.Target);
}
}
else if (ViewFamily.EngineShowFlags.RayTracingDebug)
{
for (const FViewInfo& View : Views)
{
RenderRayTracingDebug(GraphBuilder, View, SceneTextures.Color.Target);
}
}
}
#endif
```
DebugVisualizationMode具有3中模式
- TRAVERSAL使用ComputeShader
- RAY_TRACING_DEBUG_VIZ_PRIMARY_RAYS使用SF_RayGenFRayTracingPrimaryRaysRGS
- DebugVisualizationMode == RAY_TRACING_DEBUG_VIZ_INSTANCES || DebugVisualizationMode == RAY_TRACING_DEBUG_VIZ_TRIANGLES;使用SF_RayGenFRayTracingDebugRGS