计算直接光照的贡献

1.计算直接光照的贡献,Lumen⽀持的直接光类型包括: PointLight 、SpotLight 、RectLight和DirectionalLight。除
DirectionalLight是逐盏灯计算外外,其它三种类型的光照都是分批执⾏的⸺因为它们光照范围有限,可以只把这⼀批次内影响的
MeshCards找出来,每批次的直接光渲染只对在他们影响范围内的MeshCards⽣效。
2. 计算MeshCards的间接光,间接光的计算可以按光照源数据来源,Trace⽅式两个维度进⾏拆分
默认的,Lumen使⽤VoxelLighting做为间接光的来源并使⽤分块Trace复⽤来计算MeshCard的间接照明。所以在这⼉采样到的数据
是上⼀帧或更早的历史数据。
关于间接光的Voxel Trace部分的简要说明:
• 使⽤Global Distance Field进⾏加速求交
• 使⽤的是Voxel ConeTrace采样VoxelLighting,默认的每个纹素采样8个Cone,根据Hit距离确定使⽤哪⼀级Mipmap
• 采样的Cone同时会采样天光(如果天光开启)叠加到Lighting结果中
VoxelLighting光照解算过程使⽤MeshDistanceField来加速求交:Lumen选择的是先使⽤当前Voxel Clipmap的Boundbox去剔除掉
不在此范围内的所有Objects及其对应的MeshDistanceField, 再使⽤这些通过剔除的MeshDistanceField包围盒来计算它们⾃⼰所覆
盖了哪些Voxel并把⾃⼰的索引写⼊所有覆盖Voxel的Trace参数中,在接下来的Voxel Trace Pass⾥,每个Voxel仅需处理上⼀步所填
⼊的MeshDistanceFiled即可。Voxel Trace Pass的输出数据是包含HitDistance和HitObjectIndex组合的VisibilityBuffer。
VisibilityData结构如下所⽰:
uint32_t NormalizedHitDistance : 8 ; //相交距离
uint32_t HitObjectIndex : 24 ; //物体ID
1
2
最后的Voxel Shading Pass则从压缩过的VisibilityBuffer中获取到最佳的三张MeshCard来⽤对Voxel解算光照
1.“UniformPlacement DownsampleFactor=%u”:FScreenProbeDownsampleDepthUniformCS
ScreenProbeDownsampleDepthUniformCS(8,8,1) 1
这个shader的dispatch是:
1 FComputeShaderUtils::GetGroupCount(ScreenProbeParameters.ScreenProbeViewSize, FScreenProbeDownsampleDepthU
第⼀个参数是通过:FIntPoint::DivideAndRoundUp(View.ViewRect.Size(),
(int32)ScreenProbeParameters.ScreenProbeDownsampleFactor)
获得的,第⼆个参数是8.所以刚好对应的是视⼝x,y轴每隔16像素放置⼀个probe。通过拿具体index,如果⼩于之前算出来的
probe总数.根据index加上⼀点点jitter算出对应的世界坐标位置。拿到对应的gbuffer数据。就可以以probe的index当key来存储深
度,法线以及(通过获取ProbeHistoryScreenPosition来算出probeworldvelocity.)worldspeed和TranslatedWorldPosition。
2.“AdaptivePlacement DownsampleFactor=%u”:FScreenProbeAdaptivePlacementCS
Dispatch是:(FIntPoint::DivideAndRoundDown(View.ViewRect.Size(), (int32)PlacementDownsampleFactor)
其中第⼆个参数是1⾥的ScreenProbeParameters.ScreenProbeDownsampleFactor/2来的,这⾥还会再⾛⼀次,也就是还会/4,
对应的是16-8-4.
1 [numthreads(8, 8, 1)]
2 void ScreenProbeAdaptivePlacementCS(
拿到对应的screenprobescreeenposition。再拿到对应的gbuffer数据。然后就直接去计算weights权重。
这⾥计算权重⼤概步骤是:1.先计算出对应的probe,也就是步骤1⽣成的,先计算4个⻆的深度,再计算出InterpolationWeights。
然后算出4个probe的世界空间的位置,再计算出到这个⾯的距离,最终可以计算出对应的weights.
3.SetupAdaptiveProbeIndirectArgs:FSetupAdaptiveProbeIndirectArgsCS
1 SetupAdaptiveProbeIndirectArgs:
2 WriteArgs2D(0, AtlasSizeInProbes * PROBE_THREADGROUP_SIZE_2D);
3 WriteArgs2D(1, AtlasSizeInProbes);
4 WriteArgs2D(2, AtlasSizeInProbes * ScreenProbeTracingOctahedronResolution);
5 WriteArgs2D(3, AtlasSizeInProbes * ScreenProbeGatherOctahedronResolution);
6 WriteArgs2D(4, AtlasSizeInProbes * ScreenProbeGatherOctahedronResolutionWithBorder);
7
8 void WriteArgs2D(uint Index, uint2 ThreadCount)
9 {
10 RWScreenProbeIndirectArgs[Index * 3 + 0] = (ThreadCount.x + PROBE_THREADGROUP_SIZE_2D - 1)
11 RWScreenProbeIndirectArgs[Index * 3 + 1] = (ThreadCount.y + PROBE_THREADGROUP_SIZE_2D - 1) / PROBE
12 RWScreenProbeIndirectArgs[Index * 3 + 2] = 1;
13 }
ScreenProbeIndirectArgs刷新⼀些数据。先获取AtlasSizeInProbes这⾥的⼀些size⼤⼩。
⾸先回去计算每⼀个⼋⾯体texel的brdf和lighting的product,然后每个ray的⽅向,确保traceray的时候,每⼀个thread都是饱和
的,然后通过pdf排序,每三个ray的pdf⼩于我们的遮挡剔除阈值的时候,我们refine并且进⾏supersample. 其实就是多加纹素,
也就是raytraceinfo。
4.“ComputeBRDF_PDF” :FScreenProbeComputeBRDFProbabilityDensityFunctionCS:
述了⼊射光线经过某个表⾯反射后如何在各个出射⽅向上分布
groupThreadID就是屏幕空间的probe,他的threadID就是 8x8。probe的半球空间对吧?然后就每个⽅向找到对应的⼀个pixel,去
计算对应的pixel对应的gbuffer得到对应的pixelplane,然后拿⾃⼰的worldpos计算到pixelplane的距离,如果通过⼀个距离判断就
回去计算对应的pdf,其实就是对应的NdotL,也就是对应的uv反算到⼋⾯体上的⽅向dotpixelplane。
5.MarkRadianceProbes(Screenprobes):
计算出radianceprobes⽤了那些ssprobe。RWRadianceProbeIndirectionTexture存⼊对应的index
6.UpdateCacheForUsedProbesCS:
这⾥会根据当前帧数 - 上⼀次使⽤的帧数,如果⼩于设定的保存帧数,那么就会在RWRadianceProbeIndirectionTexture⾥存⼊对
应的lastframeprobeindex。如果这个probe是使⽤的,那么就会将对应帧数存储在rwprobelastusedframe⾥。
f否则会加⼊清除列表。
7.Allocated used probes:主要是为了新的probe在buffer⾥扩容
8. SelectMaxPriorityBucketCS
计算最⼤的历史bucket并且有多少追踪块需要更新
9.GenerateProbeTraceTilesCS
第⼀步回去清掉tracetile的列表,然后对于[0-2]的每个level在tile中⼼测试pdf,标记tracetile如果在阈值以下,
它在开始就会去算对应的probe到相机的距离的平⽅。这⾥的⽐较的阈值是4000 * 4000平⽅厘⽶。
如果⼩于它,那么就回去算对应的numlevels. 依据是2000 * 2000和这个距离⽐较,⼩于它就是3,其他2.
还会再去做⼀次⽐较,设定了traceprobe的(CPU)的buget数值200,gpu乘以4 x2. 如果⼤于这个数值,numlevel为1.这⾥要降
采样,因为有了太多probe需要去更新。根据对应的probeindex去获取对应的brdf,因为之前已经scatter了对应screenprobe的
brdf到radianceprobe上。
整体来说,这个pass的作⽤的dispatch的z就是probeindex的数⽬,然后对应的thread就是 8x8x1.所以这⾥就是甄别具体那些⽅向
要去trace。 其中brdf如果是0的话,就不需要trace,因为这⾥不做贡献。
每个thread的xy也就是0,0到7,7⼀共64.都是对应的tracetilecoord。后续就是根据这个xy当做uv值。通过这个uv值从⼋⾯体发算成
⽅向。再根据这个⽅向去获取对应的brdf,pdf。如果⼤于0或者0.1,那么就代表需要重新tracetile。
question:为啥这⾥是brdf⽽不是lighting去做裁剪?因为lightingpdf只是⼀个预估并且存在噪⾳,上⼀帧不在不代表这帧没有光,
否则就会⼀直没有光。但是brdf是从gbuffer来的,是准确的,固定的⽆噪⾳的。我们可以后续通过brdf是不是0然后去减少他们的
权重在滤波过程去减少⻆落处的⿊块。
1 int32 GLumenIrradianceFieldProbeResolution = 16;
会通过设置的数字去得到⼀个基础tracetile的分辨率。
将level0的tracetile排列以下。当前需不需要重新精炼以下Tracetile,依据的是brdf和回去根据tilebaseindex,去pendingtracetile
这个buffer⾥输⼊packtracetile数据, 控制台默认是0.1,根据tracetilecoord计算出对应的世界椎体⽅向,然后计算出对应的
directionsh,最后计算的对应的pdf和0.1⽐较,⼤于它就是需要精炼,⼩于不需要。
10.SetupTraceFromProbeCS:
Buffer加条件
RWSortProbeTraceTilesIndirectArgs[0] = (NumProbeTraceTiles + SortTraceTilesGroupSize - 1) / SortTraceTiles
RWSortProbeTraceTilesIndirectArgs[1] = 1;
RWSortProbeTraceTilesIndirectArgs[2] = 1;
1
2
3
这种都是为了可以thread运⾏,cs特性拉满
11.TraceFromProbes:
它会先去获取追踪快信息,然后拿到对应的探针纹素坐标。或者对应探针的追踪数据。其实也就是探针的世界中⼼,clipmaxindex
以及探针index。
后续它回去算⼀个conehalfangle,这⾥它是将球的⽴体⻆均匀的分布在所有的椎体上,⽽不是基于⼋⾯体的畸变。
后续就是进⾏椎体追踪。
这⾥开始计算的是worldprobe的trace,
但是会存在遮挡现象,why?
12.FilterProbeRadianceWithGatherCS
滤波降噪嘛,但是这样⾃阴影没了。
具体操作是:会沿着邻居probe的ray direction找到⼀个位置, sqrt(3.0f) /
GetWorldPositionToRadianceProbeCoordScale(ClipmapIndex)。这样获取到voxelradius,然后再去
max(GetRadianceProbeTMin(ClipmapIndex), VoxelRadius)获取遮挡距离。 然后算出邻居的遮挡测试距离
(NeighborWorldPosition + 2 * OcclusionDistance * WorldConeDirection)最后算出到邻居probe的遮挡测试位置的距离,最后
算出对应的遮挡测试位置的probe的index, 然后根据之前⽣成的probedepth去拿到深度,最后⽐较这个深度以及到邻居probe的
遮挡测试位置。 深度⼩,OcclusionWeight 为0 。
⾃阴影没了咋办?这个是因为distantlighting会有⻆度问题,但是distantligthing没有时差所以从来不会被拒绝,导致leak。
NeighborRadianceDepth = min(NeighborRadianceDepth, HitDistance); 因为最后计算出邻居的hitposition,然后算出⾃⼰到这
个点的距离,最后算出到邻居的夹⻆。得出夹⻆权重.最终的权重就是这个angleweight * Ocllusion权重。
13.GenerateRay:
为每⼀个球体纹素计算brdf和lighting的product。我们pdf排序,每三个pdf⼩于我们设定阈值的,我们都会重新精炼然后超采样对
应的ray
14.LightingPDF:先从上⼀帧的screenspaceradianceprobe拿数据,直接将位置投影然后和4个邻居进⾏插值、radiancecache的
ray都已经index了位置和⽅向,所以快。重新投影失败的话,意思就是offscreen,就会去拿radiance probe也就是
worldprobecache。
TraceVoxels:
// 计算追踪的UV.
获取屏幕空间的各类数据
然后计算对应的世界坐标
获取探针追踪的UV
从⼋⾯体图反算成⽅向
记录对应采样位置
ConeTraceLumenSceneVoxels
计算天光然后存储结果。
ConeTraceLumenSceneVoxels:
ConeTraceVoxels:锥体追踪体素
追踪SDF射线,然后追踪全局距离场
确定命中后,回去查找匹配当前步进的椎体宽度的voxel clipmap
如果没有超出有效范围, 则计算Voxel光照.
采样3D纹理VoxelLighting, 获得光照.
• CompositeTraces
CompositeTraces就是根据前⾯步骤⽣成的TraceHit、RayInfo和TraceRadianc⽣成ScreenProbeRadiance、
ScreenProbeHitDistance、ScreenProbeTraceMoving纹理。其使⽤的Compute Shader是LumenScreenProbeFiltering.usf,主
⼊⼝是ScreenProbeCompositeTracesWithScatterCS,具体代码此⽂忽略。
• ComputeIndirect
这个阶段就是利⽤之前⽣成的各种屏幕空间的探针数据(深度、法线、基础⾊、FilteredScreenProbeRadiance、BentNormal)计
算出最终的场景⾮直接光颜⾊(下图):
后续的trace的滤波:
当我们从我们邻居gather的时候,我们可以很快找到对应的⽅向的radiance值,因为ray已经根据位置和⽅向index 在radiance
cache了, 但是当邻居的radiance值可能造成leak的时候,我们需要⼀个启发去⼲掉它。 如何做到呢,我们可以通过重新投影邻居
的rayhits,计算出重新投影和我们考虑的点的⻆度,并且将⻆度⼤的都reject了。
BentNormal
它指向了当前像素⼀个不被其它物体(或⼏何体元)遮挡的平均⽅向,也即光线传⼊的主要⽅向

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值