ue4相机_UE4阴影渲染流程简析-上篇

7bf41ea0f78ef3922f60dafbbb0d7057.png

距离上一次写文章过去了很长一段时间了,前面几篇文章主要介绍了一下UE4渲染的主要框架以及数据流,这篇文章主要稍微详细介绍UE4阴影的渲染流程。

由于在之前的文章中我们主要介绍的是Color Pass是怎么处理场景中的物体的,并没有提到Shadow Pass是怎么处理物体的,这里主要介绍阴影相关的处理方法。阴影的处理实在Color Pass处理完之后执行的,入口为InitDynamicShadows函数。

4cb831c8afd2650944269f43563eacb2.png

在InitDynamicShadows函数中,对于场景中的每一个平行光根据一些设置初始化View.VisibleLightInfos.MobileCSMSubjectPrimitives.ShadowSubjectPrimitivesEncountered,将其初始值全部设置为false。

c53cfb23d4c75f7449d7af25cd642fb5.png

在初始化了上面的变量之后,进入FSceneRenderer::InitDynamicShadows函数。在这里我们跳过一些与设置有关的代码,直接进入我们使用到的代码里面,其他的代码可能是在处理其他的一些情况,这里由于我们没有遇到所以先不处理。在FSceneRenderer::InitDynamicShadows函数中我们调用AddViewDependentWholeSceneShadowsForView()函数,接下来我们看看AddViewDependentWholeSceneShadowsForView函数具体做了什么事情。

6c669649965547821a1dabde73576cf3.png

AddViewDependentWholeSceneShadowsForView首先初始化了FadeAlphas,并将其值全部初始化为1。

6148798f79864c7dd657e87b546c6b83.png

4a4d0135effebffe4e945fcb389a1813.png

接着计算根据实际需求和用户设置计算我们最终需要几级级联阴影。接着对于每一级的阴影根据LocalIndex获取ProjectedShadowInitializer,该数据包含了很多阴影渲染需要的很多信息,比如矩阵、FaceDirection、Bound(根据距离分割,Cascade的级数越高该Cascade的距离间隔越大,具体的分割算法这里不详细介绍了,有兴趣的可以参考下图中的函数)以及SubjectBounds等数据。

2c26ce5192bdc206d150f7366c403daf.png

6367fadf21ae41905e193a6c05b9049a.png
计算阴影的分割距离

a6bf37caa5a99298d5e832aaed5279e1.png
详细计算Bound Sphere中心和半径的代码

在进行了上面的操作之后我们就可以得到ProjectedShadowInitializer了,然后就可以将其作为参数传递给SetupWholeSceneProjection函数,用来初始化ProjectedShadowInfo。在SetupWholeSceneProjection函数中会设置绝大部分的渲染阴影需要的数据。

在SetupWholeSceneProjection函数中首先会给自己的成员赋值,保存初始化的数据。

18032a50686694374f931882ad0e0a88.png
成员赋值

接着会计算按照光源的角度该Cascade的最大最小的深度值,并且根据设定的深度值进行Clamp得到clamp之后的值,然后对PreShadowTranslation参数做一下snap,这个主要是避免的在摄像机移动的造成的锯齿或者抖动,对于边缘的像素的处理很重要。

184009db7260fd02c68da9ff13f792a0.png

更新完PreShadowTranslation之后需要计算ShadowBound,然后计算CasterMatrix,根据CasterMatrix计算视锥的6个截面。

0fc23d4ee6df487d3cd5449dd9252bec.png

之后计算ShadowViewMatrix和InvReceiverMatrix以及ReceiverFrustum,然后更新DepthBias,具体的计算方法这里就不详细介绍了。

ec847c5089646deef796fbb931552055.png

完成了上面的操作之后,就收集完了每个光源的每级阴影的Projection信息了。接下来就需要调用InitProjectedShadowVisibility函数检测阴影的可见性了。接下来我们详细的看一下InitProjectedShadowVisibility具体做了什么任务。

在InitProjectedShadowVisibility函数中,针对每一个光源,首先初始化View.VisibleLightInfos的一些数组,比如ProjectedShadowVisibilityMap和ProjectedShadowViewRelevanceMap。

40fb94e2180ae3926052340093a80ed1.png

在完成了上面的初始化操作之后,接着需要计算投射阴影的Pritimive的ViewRelevance,在我的测试场景中是没有ParentSceneInfo的,所以走的else分支。

a13dfd1c81fbaf15573ccb09c974165f.png

接着需要判断这个ProjectedShadowInfo有没有被遮挡,这里使用的是Occlusion Query来判断的,Mobile平台默认应该不会开启。如果被遮挡了,这一级就不会绘制阴影了。这个函数后面的代码与主逻辑关系不大,这里就不介绍相关的代码了。

7677b1a6f7892fe2bf1be90218e1c537.png

接下来需要做的是更新阴影的缓存,这里的阴影缓存指的是每一级的阴影。由于在我们的例子中没有使用到阴影缓存,所以这里暂时不介绍这些代码。

08f70e5184dd31959033e4a0c84fd9ff.png

daa2f135fa5547eb7c4c3783d2ebba4f.png

在处理完阴影缓存之后需要收集产生阴影的Primitvies了,然后对这些Primitvies进行筛选,这些处理逻辑和主相机的筛选逻辑是一样的,只是视锥体不一样而已。关于里面更加详细的步骤会在下篇中详细的介绍。

小结:通过这一部分我们发现其实阴影渲染的处理逻辑和之前介绍的主相机的剔除逻辑相差不大,只是阴影这一块的剔除逻辑相对于主相机更加复杂一点。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值