UE4-HISM剔除更新DFShadow
修改基于UE4.27,实际代码因为UE5重构了,还没写UE5版本。
记录一下DFShadow随Cluster裁剪更新的修改过程。
本文仅说明原 UE4.27 修改路径;UE5 的具体行为需按目标分支重新验证。
1. 遇到的问题:
在原 UE4.27 场景中观察到:植被实例距离裁剪后,其距离场阴影仍保留。这里不据此推断 UE5 当前版本是否相同。
造成的原因是UE的DistanceFieldBuffer是基于SceneInfo级别的,一种植被对应一 个FoliageInstanceStaticMeshComponent,一个SceneInfo。因为是静态物体,UE只会在初始化或者被标记修改了之后才会去更新Buffer,而标记更新并不会在运行时进行,编辑器下刷植被删植被会标记更新。运行时植被的Instance被裁剪剔除,并不会标记修改,也就不会更新Buffer。
2. 修改后效果


原笔记配图:旧 HISM 场景中的植被与距离场阴影,用来观察裁剪后的阴影更新行为。
3. 默认的更新逻辑:
DeferredRenderer.Render()
1 | |
PrepareDistanceFieldScene
1 | |
RenderLight
1 | |
主要方法:
1. FHierarchicalStaticMeshSceneProxy::GetDynamicMeshElements
根据植被设置的CullDistance,生成一个视锥六面体剔除掉不显示的Instance。
ClusterNode的结构:
一个例子:81个实例的Cluster树的结构,剔除的时候从上往下,如果上面节点被剔除,下面叶子就可以跳过。
如果使用地形笔刷添加删除植被,ClusterTree就会重建。

2. UpdateGlobalDistanceFieldObjectBuffers
ProcessPrimitiveUpdate() 只在Add和标记修改时触发更新Buffer
上传DistanceFieldObjectBuffer的数据,这个数据数对应Instance的实例数。
DistanceFieldObjectBuffers是被当作ComputeShader的RWBuffer使用,有两个结构体。一个是ObjectBoundingSphere包围球(一个float4坐标+半径),另一个是ObjectData(很多组float,两个矩阵和一些其他数据)。
RWBuffer是没有分成两个结构体,是一个连续的float4数组,通过内存Offset来控制写入。
默认情况呢,就是按照物体级别进行上传,一个物体18个float4,使用ComputeShader TUploadObjectsToBufferCS上传数据。

3. CullObjectsForShadowCS
这个ComputeShader根据上面ObjectBuffers的包围球以及灯光属性的DFShadow距离做剔除。
把需要绘制DFShadow的Object数据再Copy到另一个RWBuffer CulledObjectData里。
修改思路:
1.标记更新(当HISM的剔除结果发生变化)
2.同步剔除数据(Cluster剔除结果存到 Scene->DistanceFieldData)
3.Buffer扩展一个float4,加一个剔除数据(只用到R,GBA后面功能用到),上传ObjectBufferData(扩展一个Float4)
原分支的对象数据载荷为 18 个 float4;本方案额外扩展一个后为 19 个。为避免仅修改剔除标记也重新上传整个扩展记录,增加专门 ComputeShader 更新该字段。尺寸与偏移必须同步核对所有读写端。
4.剔除阴影绘制的Object(多加一条剔除结果的判断)
5.优化更新的地方:
基于上一帧剔除结果的变化做更新
处理编辑器ClusterTree重建的情况
参考链接:
UHierarchicalInstancedStaticMeshComponent解析
https://blog.csdn.net/qq_29523119/article/details/123932029
可见性集合的边界
主相机看不见的实例,仍可能向可见接收面投影,也可能被其他相机、反射或 GI 使用。同步距离裁剪语义与直接复用主视图视锥可见集合,是两种不同操作。
应明确标志是实例级、组件级还是视图级,处理 ClusterTree 重建、实例重排、Buffer 扩容和上一帧数据。只在主相机截图中阴影消失,还不足以证明多视图正确。
总结
这份修改让原分支的距离场阴影响应规定的实例裁剪状态。复用时保留源码版本、数据布局与视图范围,不把它当作所有 UE 版本通用补丁。