Init
This commit is contained in:
13
03-UnrealEngine/Rendering/RenderFeature/Debug/DebugView.md
Normal file
13
03-UnrealEngine/Rendering/RenderFeature/Debug/DebugView.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: DebugView
|
||||
date: 2024-03-26 21:48:13
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
- NaniteVisualize.cpp
|
||||
- RenderDebugViewMode()
|
||||
- NaniteDebugViews.usf
|
||||
|
||||
UE4.26参考:https://zhuanlan.zhihu.com/p/668782106
|
105
03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md
Normal file
105
03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: FRenderCommandFence
|
||||
date: 2025-06-25 23:11:25
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
在 Unreal Engine 5 (UE5) 中,`FRenderCommandFence` 是一个关键的**线程同步工具**,主要用于协调**游戏线程(Game Thread)** 和**渲染线程(Render Thread)** 之间的执行顺序。它的核心作用是确保渲染相关的操作(如资源创建、更新或销毁)在特定时间点前完成,避免多线程环境下的竞态条件。
|
||||
|
||||
---
|
||||
|
||||
### 核心作用
|
||||
1. **跨线程同步**
|
||||
- 游戏线程向渲染线程提交渲染命令后,这些命令不会立即执行(渲染线程有自己的任务队列)。
|
||||
- `FRenderCommandFence` 允许游戏线程**阻塞等待**,直到所有已提交的渲染命令(包括栅栏之前的所有命令)在渲染线程中执行完毕。
|
||||
2. **确保资源安全**
|
||||
- 当游戏线程需要操作渲染资源(如纹理、网格)时,必须确保渲染线程不再使用这些资源(例如释放 `UTexture`)。
|
||||
- 通过栅栏同步,可以安全地销毁或修改资源,避免访问无效内存。
|
||||
|
||||
---
|
||||
|
||||
### 关键方法
|
||||
- **`BeginFence(bool bSyncToRHIAndGPU = false)`**
|
||||
- 在渲染命令队列中插入一个“栅栏标记”,表示同步点。调用后游戏线程可以继续执行其他任务。
|
||||
- `bSyncToRHIAndGPU` - 是否等待 RHI 线程或 GPU,否则只等待渲染线程。
|
||||
- **`Wait(bool bProcessGameThreadTasks = false)`**
|
||||
- 阻塞游戏线程,直到渲染线程处理到栅栏位置(即执行完栅栏之前的所有渲染命令)。
|
||||
- `bProcessGameThreadTasks`: 若为 `true`,等待期间允许游戏线程处理其他任务(如消息泵)。
|
||||
- **`IsFenceComplete()`**
|
||||
- 非阻塞检查栅栏是否已完成(渲染线程是否已越过栅栏点)。
|
||||
|
||||
---
|
||||
|
||||
### 典型使用场景
|
||||
|
||||
#### 1. 安全释放渲染资源
|
||||
```c++
|
||||
// 游戏线程代码
|
||||
void ReleaseTexture(UTexture* Texture)
|
||||
{
|
||||
// 将资源释放命令提交到渲染线程
|
||||
ENQUEUE_RENDER_COMMAND(ReleaseTextureCommand)(
|
||||
[Texture](FRHICommandListImmediate& RHICmdList) {
|
||||
Texture->ReleaseResource();
|
||||
}
|
||||
);
|
||||
|
||||
// 创建栅栏并等待释放完成
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 阻塞直到渲染线程执行完 ReleaseResource()
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 确保渲染操作完成后再继续
|
||||
```c++
|
||||
// 更新动态纹理后确保数据已提交到GPU
|
||||
void UpdateDynamicTexture()
|
||||
{
|
||||
// 提交更新命令到渲染线程
|
||||
ENQUEUE_RENDER_COMMAND(UpdateTexture)(
|
||||
[...](...) { /* 更新纹理数据 */ }
|
||||
);
|
||||
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 等待纹理更新完成
|
||||
// 此时可以安全读取纹理或进行后续操作
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 异步加载资源时同步
|
||||
```c++
|
||||
void LoadAssetAsync()
|
||||
{
|
||||
StreamableManager.RequestAsyncLoad(..., [this]()
|
||||
{
|
||||
// 资源加载完成后,确保渲染线程初始化完毕
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 等待渲染线程处理完资源初始化
|
||||
OnAssetLoaded();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 注意事项
|
||||
1. **性能影响**
|
||||
`Wait()` 会阻塞游戏线程,过度使用可能导致帧率下降。应仅在必要时同步(如资源卸载)。
|
||||
2. **替代方案**
|
||||
- 对于非紧急任务,可用 `AsyncTask` 或 `TaskGraph` 异步处理。
|
||||
- 使用 `FGraphEvent` 实现无阻塞等待。
|
||||
3. **与 `FlushRenderingCommands()` 的区别**
|
||||
`FlushRenderingCommands()` 会强制**立即刷新**整个渲染命令队列(更重操作),而 `FRenderCommandFence` 只等待到特定同步点,更轻量可控。
|
||||
|
||||
---
|
||||
|
||||
### 总结
|
||||
|
||||
`FRenderCommandFence` 是 UE5 多线程架构中的**安全阀**,通过它开发者可以:
|
||||
✅ 确保渲染操作在指定时间点前完成
|
||||
✅ 安全操作渲染资源(创建/更新/销毁)
|
||||
✅ 避免游戏线程与渲染线程的竞态条件
|
9
03-UnrealEngine/Rendering/RenderFeature/Fur制作方案.md
Normal file
9
03-UnrealEngine/Rendering/RenderFeature/Fur制作方案.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Fur制作方案
|
||||
date: 2023-08-15 11:17:42
|
||||
excerpt:
|
||||
tags: Fur 毛发
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
https://xbdev.net/directx3dx/specialX/Fur/
|
20
03-UnrealEngine/Rendering/RenderFeature/LOD相关代码笔记.md
Normal file
20
03-UnrealEngine/Rendering/RenderFeature/LOD相关代码笔记.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: LOD相关代码笔记
|
||||
date: 2023-12-28 15:13:44
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# UE5中的带有LOD名称的相关代码
|
||||
- Plugin
|
||||
- Mesh LOD Toolset
|
||||
- `Engine\Plugins\Experimental\MeshLODToolset\Source\MeshLODToolset\Public\Graphs\GenerateStaticMeshLODProcess.h`
|
||||
- Proxy LOD Plugin
|
||||
- `Engine\Plugins\Experimental\ProxyLODPlugin\Source\ProxyLOD\PrivateProxyLODPlugin.cpp`
|
||||
- Skeletal Mesh Simplifier
|
||||
- `Engine\Plugins\Experimental\SkeletalReduction\Source\Private\SkeletalMeshReductionPlugin.cpp`
|
||||
|
||||
# UGenerateStaticMeshLODProcess
|
||||
- FGenerateStaticMeshLODAssetOperatorOp
|
||||
- FGenerateStaticMeshLODAssetOperatorOp::CalculateResult()
|
||||
- void FGenerateMeshLODGraph::EvaluateResult
|
@@ -0,0 +1,185 @@
|
||||
---
|
||||
title: Lumen学习笔记(1)——官方文档与视频笔记
|
||||
date: 2022-08-26 14:50:26
|
||||
excerpt:
|
||||
tags: Lumen
|
||||
rating: ⭐⭐
|
||||
---
|
||||
## 前言
|
||||
其他lumen学习视频:https://www.youtube.com/watch?v=CFKNoeUPQGQ
|
||||
Lumen | Inside Unreal https://www.youtube.com/watch?v=QdV_e-U7_pQ
|
||||
|
||||
Lumen的设计目标:
|
||||
- 大型开放世界
|
||||
- 需要流式加载
|
||||
- 需要控制百万级的实例物体
|
||||
- 室内环境
|
||||
- 实现实时GI十分困难
|
||||
|
||||
因为Lumen的关系,UE5改进了距离场系统,分辨率是UE4的2倍。这也让DFAO获益。
|
||||
|
||||
### 效果支持
|
||||
- 反射
|
||||
- 反射效果会带有GI,同时支持Clear Coat材质。
|
||||
- GI
|
||||
- 支持对半透明物体、体积雾提供GI效果。但与不透明物体相比想过会差很多
|
||||
|
||||
### UE5.0版本Lumen限制
|
||||
- 流明全局照明不能与光照贴图中的[静态照明](https://docs.unrealengine.com/5.0/en-US/static-light-mobility-in-unreal-engine)一起使用。Lumen Reflections 应该在虚幻引擎 5 的未来版本中扩展为与光照贴图中的全局照明一起使用,这将为使用静态照明技术的项目提供进一步扩大渲染质量的方法。
|
||||
- Lumen Reflections 不支持多重镜面反射。与折射。
|
||||
- 5.1才会支持薄片材质(用于制作树叶)。
|
||||
- Lumen[](https://docs.unrealengine.com/5.0/en-US/single-layer-water-shading-model-in-unreal-engine) 目前不支持 [单层水材质。](https://docs.unrealengine.com/5.0/en-US/single-layer-water-shading-model-in-unreal-engine)
|
||||
- Lumen 目前不支持场景捕捉或分屏。
|
||||
- Lumen与[Forward Shading](https://docs.unrealengine.com/5.0/en-US/forward-shading-renderer-in-unreal-engine)**不**兼容。
|
||||
|
||||
- 对模型有一要求,如果是想汽车这种外壳薄、内部结构复杂的模型。作者说之后会去解决。
|
||||
|
||||
## 开启方式
|
||||
开启Lumen的相关选项:
|
||||
- ProjectSettings-Rendering
|
||||
- Global Illumination -> Dynamic Global Illumination Method -> Lumen
|
||||
- Reflection -> Reflection Method -> Lumen
|
||||
- Software RayTracing -> Generate Mesh Distance Fields -> True
|
||||
|
||||
硬件Lumen支持开启:
|
||||
- ProjectSettings-Rendering
|
||||
- Lumen -> UseHardware RayTracing when available -> True
|
||||
- Hardware RayTracing -> Support Hardware RayTracing -> True
|
||||
|
||||
另外还可以通过PostProcessVolumn的相关选项开启(GI与Reflection),还可以设置质量。
|
||||
|
||||
## 控制方式
|
||||
- 场景内的灯光属性
|
||||
- 材质的BaseColor与Roughness
|
||||
- 曝光
|
||||
|
||||
## 工作原理
|
||||
分硬件光线追踪与软件光线追踪2种方式。同时Lumen也是一种混合方案。
|
||||
|
||||
### Surface Cache
|
||||
起名为Cards,生成方式为Mesh Capture的方式,它会在低分辨率下捕获材质信息。
|
||||
- 可以使用`r.Lumen.Visualize.CardPlacement 1`显示构建方式。
|
||||
- `Show->Visualize->LumenScene`可以查看Surface Cache。当Lumen显示不正常时就可以使用这个工具来查找问题。
|
||||
|
||||

|
||||
|
||||
注意:
|
||||
1. `只能使用简单的内部结构的模型`,不然会导致SurfaceCache生成错误。
|
||||
2. 对于高面数模型需要使用Nanite进行处理,不然效率会非常低。作者建议使用Nanite对场景模型进行高效的LOD设置(哪怕是个小模型)。
|
||||
3. 实例化的静态网格物体必须是Nanite。
|
||||
|
||||
### 软件光线追踪
|
||||
1. 首先使用深度缓存(屏幕空间)进行追踪。
|
||||
2. 如过Ray Miss或者离开屏幕范围,就会使用ComputeShader对SDF进行追踪。(Mesh距离场与全局距离场)
|
||||
3. 将光照的Ray Hits结果写入`Surface Cache`。
|
||||
|
||||
#### 优化
|
||||
`ProjectSettings-Rendering-Lumen-SoftRayTracingMode`有`DetailTracing`与`GlobalTracing`选项,后者会跳过Mesh SDF直接去追踪全局距离场。对于一些模型叠加比较厉害的场景可以使用`GlobalTracing`来提高效率。
|
||||
|
||||
#### 限制
|
||||
1. 目前仅支持StaticMesh、StaticMeshInstance 、Landscape。
|
||||
2. 不支持有WorldPositionOffset修改的材质。
|
||||
|
||||
### 硬件光线追踪
|
||||
- 最高质量,但是性能高消耗+显卡限制。
|
||||
- 需要显卡支持Dx12。
|
||||
|
||||
硬件光线追踪不会直接去追踪原始的Nanite模型(因为API不兼容问题),而是会去追踪Nanite提供的一个简化 几何代理版本。所以如果场景GI效果不佳,可以适当提高Proxy TrianglePercent比例,1% -> 2%~5%。
|
||||
|
||||
#### 限制
|
||||
- EA版中 Instance 数量不能超过100000
|
||||
- 高面数的骨骼模型在Lumen中会非常消耗资源
|
||||
- 模型不能有大量的重叠(对于有大量重叠的场景可以考虑使用软件光线追踪)
|
||||
- 速度比软件光线追踪慢50%,但更加精确
|
||||
|
||||
### Lumen Final Gather
|
||||
RayTracing相当耗费资源,因此只能只能承担1/2 Rays per pixel,但室内场景中需要200+采样才会有可以接受的结果。因为需要尽可能得利用每一次Ray Hit取得的结果。
|
||||
|
||||
一些常用的方法:
|
||||
- Irradiance Fields(二阶球谐与体素GI?)
|
||||
- 独特的扁平外观?distinctive flat look
|
||||
- 缓慢的光照更新速度
|
||||
- 需要人工放置VolumnBox
|
||||
- 屏幕空间降噪器
|
||||
- 性能消耗很大
|
||||
- 降噪在光追之后,效果很有限
|
||||
|
||||
Lumen使用了**屏幕空间辐射度缓存** ,这样允许我们从很小的一组位置进行追踪。通常会将采样降低约1/16,并对每个采样点进行更多的光线追踪采样。(牺牲密度来提高精度) 之后使用插值的方式来填补周围像素。
|
||||

|
||||
|
||||
Lumen还使用**世界空间的辐射度缓存**,也就是探针(数量较少),这些探针将会重复运用于多个像素。使用`r.Lumen.RadianceCache.Visualize 1`可以显示出探针。与VolumeLightMap不同,Lumn并不使用这些探针直接渲染GI效果,而是从像素开始RayTracing。
|
||||

|
||||
|
||||
Lumen与降噪器效果比较:
|
||||

|
||||
|
||||
### Lumen Reflection
|
||||
- 对于较为粗糙的物体(Roughness < 0.4),会使用额外的光线进行光线追踪,会耗费额外资源。
|
||||
- 对于较为光滑的物体(Roughness > 0.4),会重用追踪结果,高光波瓣会收敛在diffuse上。(也就是说GI会考虑到高光)
|
||||
- 降噪采用Spatial与Temporal降噪器
|
||||
|
||||
屏幕上一半的反射是由Lumen Finaly Gather提供的。这样是的Lumen GI与Lumen Reflection能很好的结合在一起,但这样也加重了Lumen的消耗。
|
||||
|
||||
在默认情况下渲染反射效果时(即使打开光线追踪),当光线命中物体会直接从表面缓存获取到所有光照结果。这种方式的渲染效率是Rtx反射的两倍,但效果挺差的。将`Lumen Reflections-Quality`值提高到4时,Lumen会去实际计算反射结果,以提供更准确的效果。
|
||||

|
||||
|
||||
此时多重反弹的反射效果与天光依然由表面缓存提供。
|
||||
|
||||
#### Lumen Reflection vs Ray Traced Reflection
|
||||
- Lumen Reflection Support
|
||||
- Screen Traces
|
||||
- Software Ray Tracing
|
||||
- Dynamic GI In Reflection(表面缓存)
|
||||
- Shadowed Moveable Skylight In Reflection(表面缓存)
|
||||
- Clear Coat 2 Layers of Reflections
|
||||
- Ray Traced Reflections Support
|
||||
- LightMap GI In Reflections
|
||||
- Multi-Bounce Reflections
|
||||
- Future:Lumen Reflections
|
||||
- Bring Over Remaining Feature From Ray Traced Reflection
|
||||
|
||||
## 最佳实践
|
||||
- Emissive
|
||||
1. 使用Lumen时,不要使用自发光材质模型代替灯光。(容易产生大量噪点)
|
||||
2. 使用较大的自发光材质模型做出一些效果。比如夜晚房间中的电视机效果。以及场景中微弱补光。
|
||||
3. 使用较小的自发光材质模型+灯光。
|
||||
- BaseColor
|
||||
1. BaseColor与GI影响很大。
|
||||
2. 非常暗以及糟乱BaseColor会产生很烂的GI效果。
|
||||
3. 作者建议使用材质集+材质参数来调整MegaScane资产BaseColor贴图的亮度,这样可以保证所有材质的亮度保持统一。
|
||||
- Indirect Lighting Intensity(灯光的选项)
|
||||
1. 现在不支持屏幕空间追踪。(预览版)
|
||||
2. 将其设置为1外的值会出现伪影。(预览版)
|
||||
- Performance
|
||||
1. Epic 质量为游戏主机上30帧的设置。
|
||||
2. High质量为游戏主机上60帧的设置。
|
||||
|
||||
## 其他
|
||||
1. 是否支持物理灯光?作者认为是支持的,同时内部也有一些项目使用。但没有经过严格测试。可以确定Emissive材质物体会直接失效,因为Emissive物体也需要提高到相应的亮度才能显示。
|
||||
2. 目前Lumen Reflection 不支持 LightMap,而且LightMap还会额外占用显存。
|
||||
|
||||
|
||||
# 王祢的讲解视频(详细的算法解释)
|
||||
- UnrealCircleNanite技术简介 | Epic Games China 王祢
|
||||
- UOD2021虚幻引擎5渲染特性解析 Lumen | Epic Games 王祢
|
||||
|
||||
1. RadianceCaching
|
||||
|
||||
# 中文直播
|
||||
[中文直播]第42期 | Lumen高品质渲染解析 | Epic 纪大伟 https://www.bilibili.com/video/BV15d4y1V7p9?vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
Ctrl+L调整灯光,勾选灯光选项。
|
||||
|
||||
## MeshCrad
|
||||
1. MeshCrad构建数量,数量过多会卡。
|
||||
2. `r.Lumen.Visualize.CardPlacement 1`显示构建的MeshCard。
|
||||
|
||||
## SurfaceCache
|
||||
- 黄色:模型完全没有SurfaceCache
|
||||
- 粉色:只有部分模型有SurfaceCache
|
||||
|
||||
解决错误
|
||||
1. 在后期盒子里调整SceneDetail
|
||||
2. 把错误的模型拆开。
|
||||
|
||||
## 灯光矫正
|
124
03-UnrealEngine/Rendering/RenderFeature/Lumen学习笔记(2)——原理笔记.md
Normal file
124
03-UnrealEngine/Rendering/RenderFeature/Lumen学习笔记(2)——原理笔记.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Lumen学习笔记(2)——代码分析
|
||||
date: 2023-02-12 10:50:24
|
||||
excerpt:
|
||||
tags: Lumen
|
||||
rating: ⭐
|
||||
---
|
||||
## 前言
|
||||
本文的内容为丛越的知乎文章的知识点浓缩。
|
||||
|
||||
- **SIGGRAPH2022 Lumen**:https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-Lumen-Wright%20et%20al.pdf
|
||||
- **UE5.1 Lumen Indirect Diffuse Lighting技术分析 - GOOD REAL的文章 - 知乎**:https://zhuanlan.zhihu.com/p/696232022
|
||||
## Lumen相关可视化命令
|
||||
- r.Lumen.Visualize.CardPlacement 1:开启Lumen Card的显示。
|
||||
- r.Lumen.RadianceCache.Visualize 1:开启World Space Probe。
|
||||
- r.Lumen.ScreenProbeGather.VisualizeTraces 1:开启屏幕空间探针追踪可视化。
|
||||
|
||||
## Software Lumen的加速结构(距离场)
|
||||
使用SDF(Signed Distance Field)作为存软件方案的加速结构。分为Mesh DF与Global DF。
|
||||
|
||||
## Surface Cache
|
||||
Surface Cache 是一系列运行时生成的图集(Atlas),以很低的分辨率存储了整个场景物体表面的材质属性。
|
||||
Lumen 采用 Cube Map 理念仅生成**6 个轴对齐方向**(上、下、左、右、前、后)的 **Material Attribute**,在 Tracing 时将 Hit Point 简单的投影到这 6 个方向,对 Surface Cache 执行采样,就可以以极高的性能获取 Material Attribute 进行 Lighting。
|
||||
|
||||
在UE5中被称之为**Card**。
|
||||

|
||||
|
||||
## Radiance Cache
|
||||
对于 Surface Cache 上的每个 Texel 来说,Direct Lighting 的结果就是该点的 Radiance,如果把 Surface 上的所有 Texel 的 Radiance 保存下来,这样就包含了整个场景的光照信息,这就是 Radiance Cache。有了 Radiance Cache,就可以在 Tracing 时直接进行采样作为 Tracing 方向对应的 Radiance,将这些 Radiance 收集起来积分计算出 Irradiance,从而得到最终的光照值。还可以基于此生成 Indirect Lighting 并与历史帧累积起来,这样就实现了 Lumen 所宣称的无限反弹(Infinite Bounces)的全局光照能力。
|
||||
|
||||
从左到右分别为 Albedo、Normal、Depth、Radiance(Direct Lighting) 以及最终的渲染画面。
|
||||

|
||||
|
||||
## Lumen Scene
|
||||
可以在`ViewMode-Lumen-LumenScene`中查看。
|
||||
Lumen Scene 包含了 DF 描述的场景几何表达以及 Surface Cache 描述的场景材质表达,是一个完整的系统。
|
||||
|
||||

|
||||
|
||||
## Screen Space Probe(针对MF Tracing)
|
||||
Lumen 是一个基于 Probe 的 RTGI 系统,通过 Probe 执行 Ray Tracing。
|
||||
|
||||
Screen Space Probe 仅用于**DF Tracing**,在**1.8** 米范围内进行**Mesh DF Tracing**,那么对于更远的距离会使用**Global DF Tracing**(以及屏幕空间外的区域)。但是却**无法**同 Mesh DF 一样从 Surface Cache 上**获取 Material Attribute**,原因正如上所述,**Global DF** 是由 **Mesh DF** 合并而来,全局只有一份,缺少了 **Mesh DF** 信息,而 Surface Cache 又与 Mesh 相关,因此无法通过 Global DF 获取 Mesh 对应的 Surface Cache 数据。
|
||||
|
||||
Lumen作者解释时还说了下图中的东西。我个人理解为:
|
||||
>首先进行屏幕空间的追踪,即直接获取到SurfaceCache的材质与照度数据,如果失败则依次追踪**Mesh DF**与**Global DF**。
|
||||
|
||||

|
||||
|
||||
那么 Lumen 又是如何对 Global DF 进行 Lighting 的呢?这就要引入 [[#Voxel Lighting]]。
|
||||
|
||||
### Screen Space Probe放置方法
|
||||
首先每隔 16 个像素均匀的放置,然后进行自适应放置,检查 Probe 网格中的像素是否可被插值,即 4 个 Corner Probe 是否被当前像素遮挡,如果是则在当前像素位置上增加一个 Probe,如此迭代,每次迭代增加一倍分辨率,直到插值差值失败的像素很少或达到指定的分辨率阈值。如下图所示,橘红色为差值失败的像素:
|
||||

|
||||
|
||||
## Voxel Lighting
|
||||
Lumen的**Voxel Lighting**存储的是较粗颗粒度的**空间光照信息**(应该6轴向的光照信息)。从采样**Radiance Cache**获得。为了精确,采用了和**Surface Cache**一样的世界坐标6轴向对齐方案。最后通过采样3轴向的光照数据,再根据Ray方向进行权重插值取得结果。
|
||||

|
||||
为了进一步优化性能减少内存,Lumen 还为 Voxel Lighting 生成了 Clipmap,用于覆盖不同范围,这样可以根据采样点所在的 Clipmap 获取光照。通过 Clipmap,Voxel Lighting 可以覆盖从最小 50 米到最大 6.4 公里的范围,默认最大覆盖 400 米。
|
||||
|
||||
**Voxel Lighting**的另一个用途是用于生成 **Indirect Lighting**,这就是**Radiosity Indirect Lighting**。
|
||||
|
||||
## Radiosity Indirect Lighting
|
||||
实际上严格来说 Lumen 并不是 Ray Tracing,而是 Ray Casting,因为光线在与 SDF 相交后并没有再次 Bounce,因此最多只能产生一次 Bounce 的 Indirect Lighting,为了弥补这一点,Lumen 使用 Radiosity 来生成 Indirect Lighting。
|
||||
|
||||
传统的 Radiosity 方法需要将场景划分为 Patch,而 Lumen 已经拥有了粗粒度的 Global DF 以及粗粒度的 Voxel Lighting,因此可以**直接从Surface Cache 上射出光线**,与 **Global DF 进行 Ray Tracing 求交**,交点采样**上一帧的 Voxel Lighting** 后转换为**二阶球谐**,最后再根据 Normal 计算**Diffuse Transfer 球谐系数**,计算出最终的 Indirect Radiance。
|
||||
|
||||
这个**Indirect Radiance也保存在Radiance Cache中**,称为**Indirect Lighting**,并与Direct Lighting 合并计算得到最终的 Final Lighting,而下一祯的 Voxel Lighting 又来自于这一祯的 Radiance Cache,因此后续所有的 Lumen 光照流程自然具有了间接光照。
|
||||
|
||||
## World Space Probe(当Global DF求交失败时进行采样)
|
||||
尽管 Voxel Lighting 可以Trace到更远距离的光照,但对于远光(**Distant Lighting**)会更容易出现噪点,因为**小而亮**的特征产生的噪声会随着该特征的距离增加而增加,另外还存在着长距离 Tracing 的性能问题,并且距离的长短不均匀变化也会导致 Tracing 性能的不稳定。
|
||||
|
||||
Lumen 使用了单独的采样方案来解决这个问题,这就是**Word Space Probe**。通过在世界空间中布置 Probe,向各个方向上对 **Global DF Tracing**,对 Voxel Lighting 进行采样并缓存下来,这样就得到了 **Word Space Probe 的 Radiance Cache**。
|
||||
|
||||
乍看起来这与传统的 Irradiance Field 很相似,但不同之处在于 Word Space Probe 在**Screen Probe 周围分布 8 个 Probe**,因此是稀疏的放置的,另外由于 World Space Probe 与视角相关,为了优化性能也使用了 Clipmap,每个 Clipmap 的 World Probe 数量相同,但范围不同,默认 4 级最大可设置为 6 级。下图为 World Space Probe 的可视化视图,可以明显看到 Probe 在 Clipmap 中的稀疏分布:
|
||||

|
||||
当 **Global DF Tracing 失败时**,可以快速查找 Screen Space Probe 周围的 8 个 Word Space Probe,在 Ray 方向上**对 Word Space Probe Radiance Cache 采样,三线性插值混合这些 Radiance**,从而得到最终的 Distant Lighting,这种方式使光照更加稳定,大大缓解了噪声。此外,还可以与采样的 Voxel Lighting 进行插值混合,使光照过渡更加平滑。下图是 Radiance Cache 对比图,左图关闭,右图开启:
|
||||

|
||||
|
||||
## Importance Sampling
|
||||
Lumen 引入了业界降噪常用的方法:重要性采样(Importance Sampling),通过使用上一帧的 Radiance Cache 作为生成当前帧的光线方向的引导,使光线尽可能朝向光源方向追踪,这样就可以在不增加光线预算的情况下实现了降噪,如下所示:
|
||||

|
||||
|
||||
## Shadow
|
||||
阴影的本质就是光照的可见性。Lumen 为 Surface Cache 标记 Shadow Mask,这样在 Lighting 时直接乘以这个 Mask 即可。Shadow Mask 可以直接使用之前已经生成的Virtual Shadow Map。
|
||||
|
||||
但是这并不够完整,原因在于 **(Virtual)Shadow Map 的生成是 Camera Visibility相关**的,而 **Surface Cache 与 Camera Visibility 无关**,这会 Surface Cache 的 Shadow Mask 缺失,因此**需要对那些没有 Mask 的 Surface 再执行一次 Ray Tracing 来判断光源的可见性**。因为 Lumen Scene 已经具有 SDF 表达,因此可直接使用 Mesh Global DF Ray Tracing,其实本质上就是在**Surface Cache 上进行 DF Shadowing**。
|
||||
|
||||
## 降噪
|
||||
同目前主流的 RTGI 一样,Lumen 也采用了基于 Temporal Spatial Filter 来降噪,Lumen 的 Temporal Filter 发生在整个 Lumen Pipeline 的最后阶段,而 Spatial Filter 则在各个流程之中已经进行。例如 Screen Space Radiance Cache,访问时在 Probe 空间执行 3x3 的滤波,由于 Radiance Caceh 是 1/16 屏幕大小的,因此 3x3 的 Kernel 相当于屏幕空间 48x48 的 Kernel 大小,这样就以很低的成本实现大 Kernel 的 Filtering。下面是仅使用 Spatial Filter 的对比图,降噪效果明显:
|
||||

|
||||
|
||||
## Reflections
|
||||
为了优化性能 Lumen 实现两种 Reflections 机制,当 **Roughness 大于 0.4 时**重用**Screen Space Radiance Cache**的结果,因为这时高光的 GGX Lobe 已经汇聚到 Diffuse 上,而且会自动利用已经做完的 Sample 和 Filtering 结果。当**Roughness < 0.4 的反射使用额外的光线进行 Tracing**,过程与Indirect Diffuse 类似,也包含了三种 Trace:
|
||||
1. Screen Ray Marching,采样上一帧的 Scene Color。
|
||||
2. Mesh DF Tracing ,采样 Screen Space Radiance Cache。
|
||||
3. Global DF Tracing ,采样 Voxel Lighting 和 World Space Radiance Cache。
|
||||
|
||||
最后使用双边滤波器进行降噪输出 。
|
||||
|
||||
## Translucency Volume GI
|
||||
Lumen 还为半透明材质及体积雾的 Light Scattering 提供了低分辨率的 GI。
|
||||
|
||||
Lumen 将 Frustum 体素化为 Froxel,对可见的 Froxel 执行 Global DF Ray Tracing,采样当前帧的 Voxel Lighting 和 World Space Radiance Cache 获取 Radiance,使用 3D Texture 存储每个 Froxel 的 Radiance,然后使用一定次数的 Spatial Filtering 降噪,最后进行积分转换为二阶球谐系数,用于半透明材质及体积雾来计算 Lighting。
|
||||
|
||||
## 丛越整理的的流程
|
||||
1. Lumen Scene Update
|
||||
1. 根据**Mesh Cards**生成**Surface Cache**。
|
||||
2. Lumen Scene Lighting
|
||||
1. Surface Cache Direct Lighting
|
||||
2. Radiosity Indirect Lighting
|
||||
3. Direct Lighting + Indirect Lighting 合并生成**Screen Space Probe Radiance Cache**。
|
||||
4. 体素化相机范围内场景并根据**Surface Cache Lighting**生成**Voxel Lighting**
|
||||
5. 根据 Voxel Lighting 生成 Translucency Volume Lighting。
|
||||
3. Final Gather
|
||||
1. 根据**G-Buffer**放置**Screen Space Probe**
|
||||
2. 在每个**Screen Space Probe**周围放置 **World Space Probe** 并根据 **Voxel Lighting** 生成 **World Space Probe Radiance Cache**
|
||||
3. **Screen Tracing**,采样前一帧的 Scene Color。
|
||||
4. 在近距离范围内对每个**Screen Probe**执行 **Mesh DF Ray Tracing**,采样**Screen Space Probe Radiance Cache**。
|
||||
5. 在中远距离范围内对每个**Screen Probe** 执行**Global DF Ray Tracing**,采样**三个方向的 Voxel Lighting**,并同时采样 **8 个 World Space Probe Radiance Cache** 与**Voxel Lighting** 混合。
|
||||
6. 插值、积分和时序过滤,最终得到 **Scene Indirect Diffuse**。
|
||||

|
||||
|
||||

|
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Lumen学习笔记(3)——代码分析
|
||||
date: 2023-02-10 15:32:29
|
||||
excerpt:
|
||||
tags: Lumen
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# Lumen
|
||||
Lumen的逻辑开始于DeferredShadingRenderer.cpp的`RenderLumenSceneLighting()`
|
||||
|
||||
|
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: Untitled
|
||||
date: 2024-12-28 21:56:29
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
相关论文地址:
|
||||
- Reflective Shadow Maps: https://users.soe.ucsc.edu/~pang/160/s13/proposal/mijallen/proposal/media/p203-dachsbacher.pdf
|
||||
- Light Propagation Volumes: https://ericpolman.com/
|
||||
- VXGI:
|
||||
- https://on-demand.gputechconf.com/gtc/2014/preseintations/S4552-rt-voxel-based-global-illumination-gpus.pdf
|
||||
- https://docs.nvidia.com/gameworks/content/gameworkslibrary/visualfx/vxgi/product.html
|
||||
- https://developer.download.nvidia.com/assets/gameworks/downloads/secure/GDC18_Slides/Advances%20in%20Real-Time%20Voxel-Based%20GI%20-%20GDC%202018.pdf?HAwkc19IdhKMFWUs8jSk-If6y4QowlgUUrY8F9SL1-4qnhWWo-u5_gptg4ITWjMJCd1JrFbGmBw4-ztQCJ4M9Bh61am6lFCerKQVApEjN-HQ4chiSb5MLRLY41TyIse6_XW2DpNAe0_4inrYkZqxVb2iGiyZQhxwzfZ40dByP-X8jRYfWhJVsC1aSIqZ8BkooGkjUoUUGw2r7AAxyWVQole9a4je_76g04KtUwlONQ==
|
||||
- Lumen:
|
||||
- [https://youtu.be/2GYXuM10riw](https://youtu.be/2GYXuM10riw)
|
||||
- https://www.advances.realtimerendering.com/s2021/Radiance%20Caching%20for%20real-time%20Global%20Illumination%20(SIGGRAPH%202021).pptx
|
||||
- SSGI: https://www.ea.com/frostbite/news/stochastic-screen-space-reflections
|
||||
- DDGI:
|
||||
- https://zhuanlan.zhihu.com/p/404520592
|
||||
- https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9900-irradiance-fields-rtx-diffuse-global-illumination-for-local-and-cloud-graphics.pdf
|
||||
# HIZ Tracing
|
||||
|
||||
![[HierachicalTracing2.png]]
|
||||
|
||||
![[HierachicalTracing3.png]]
|
||||
|
||||
![[HierachicalTracing1.png]]# Reflective Shadow Maps(RSM,2005)
|
||||
论文地址:https://users.soe.ucsc.edu/~pang/160/s13/proposal/mijallen/proposal/media/p203-dachsbacher.pdf
|
||||
|
||||
Let's inject light in. (Photon Mapping?)
|
||||
解决如何把“光”注入到场景中。
|
||||
|
||||
**Cool Ideas**
|
||||
- Easy to be implemented
|
||||
- Photon Injection with RSM
|
||||
- Cone sampling in mipmap
|
||||
- Low-res Indirect illumination with error check
|
||||
**Cons**
|
||||
- Single bounce
|
||||
- No visibility check for indirect illumination
|
||||
|
||||
# Light Propagation Volumeshj
|
||||
|
||||
**"Freeze" the Radiance in Voxel**
|
||||
Light Injection
|
||||
- Pre-subdivide the scene into a 3D grid
|
||||
- For each grid cell, find enclosed virtual light sources
|
||||
- Sum up their directional radiance distribution
|
||||
- Project to first 2 orders of SHs (4 in total)
|
||||
|
||||
# Sparse Voxel Octree for Real-time Global Illumination (SVOGI)
|
||||
|
||||
## Shading with Cone Tracing in Voxel Tree
|
||||
Pass 2 from the camera
|
||||
- Emit some cones based on diffuse+specular BRDF
|
||||
- Query in octree based on the (growing) size of the cone
|
||||
|
||||
# VXGI(Nvidia UE4 Plugins)
|
||||
**Problems in VXGI**
|
||||
Incorrect Occlusion(opacity)
|
||||
- naively combine the opacity with alpha blending.
|
||||
Light Leaking
|
||||
- when occlusion wall is much smaller than voxel size
|
||||
|
||||
# SSGI
|
||||
SIGGRAPH2015:Advances in Real-Time Rendering course
|
||||
|
||||
**Radiance Sampling in Screen Space**
|
||||
For each fragment:
|
||||
- **Step 1**: compute many reflection
|
||||
rays
|
||||
- **Step 2**: march along ray direction
|
||||
**(in depth gbuffer)**
|
||||
- **Step3**: use color of hit point as
|
||||
indirect lighting
|
||||
![[SSGI1.png]]
|
||||
|
||||
![[SSGI2.png]]
|
||||
中间的RayCast使用RayMarching进行。但使用LinearRayMarching相对比较消耗资源,所以采用HierachicalTracing。
|
||||
|
||||
![[SSGI3.png]]
|
||||
最低层级
|
||||
![[HierachicalTracing1.png]]
|
||||
层级+1,相当于RayMarching2个像素。
|
||||
![[HierachicalTracing2.png]]
|
||||
层级+2,相当于RayMarching4个像素。此时RayHit。
|
||||
![[HierachicalTracing3.png]]
|
||||
回退当前HiZ像素的上一层级。
|
||||
![[HierachicalTracing4.png]]
|
||||
回退当前HiZ像素的上上一层级。
|
||||
![[HierachicalTracing5.png]]
|
||||
找到RayHit位置。
|
||||
![[HierachicalTracing6.png]]
|
||||
|
||||
## Ray Reuse among Neighbor Pixels
|
||||
- Store **hitpoint data**
|
||||
- Assume visibility is the same between neighbors
|
||||
- Regard **ray to neighbor's hitpoint** as valid
|
||||
|
||||
![[ConeTracingWithMipmapFiltering1.png]]
|
||||
|
||||
# Lumen
|
||||
## Phase1: Fast Ray Track in Any Hardward
|
||||
Signed Distance Field(SDF)
|
||||
1. 它是均匀的。
|
||||
2. 在空间上是连续的。
|
||||
|
||||
### Cone Tracing with SDF(ie. Soft Shadow)
|
||||
|
||||
## Phase2:Radiance Injection and Cacheing
|
||||
![[MeshCard1.png]]
|
||||
MeshCard的目的是为了将直接光照存储在模型上(Surface Cache)
|
||||
|
||||
![[GenerateSurfaceCache1.png]]
|
||||
![[GenerateSurfaceCache2.png]]
|
||||
|
||||
最终目的是通过SurfaceCache这4张图渲染出SurfaceCache FinalLighting
|
||||
![[LightingCachePipeline1.png]]
|
||||
1. 计算SurfaceCache DirectLighting
|
||||
2. 通过1计算体素光照。
|
||||
3. 通过体素光照来计算间接照明。
|
||||
4. 最终计算Surface Cache FinalLighting。
|
||||
|
||||
以此进行循环。![[DirectLighting1.png]]
|
||||
|
||||
针对多个光源会渲染对应数量的cache,之后累加在一起。
|
||||
![[MultiLightSurfaceCache.png]]
|
||||
|
||||
### Voxel Lighting to Sample
|
||||
![[VoxelLightingToSample.png]]
|
||||
对于近处的物体可以准确拿到Hit到物体的上一点的Radiance;对于远处的物体,会以相机坐标轴生成一个Voxel形式的表达,之后通过Global SDF拿到对应的Radiance。
|
||||
***PS. 该Voxel存储的数据为:每个面对应方向上被其他直接照明照亮的亮度。***
|
||||
|
||||
![[VoxelClipmap.png]]![[BuildVoxelFaces.png]]
|
||||
其Voxel的计算是基于SDF的。
|
||||
|
||||
![[InjectLightIntoClipmap.png]]
|
||||
|
||||
![[IndirectLighting.png]]
|
||||
在SurfaceCache中 8x8的tile中(行与列间隔4个像素,放置2个探针),进行4次空间Voxel采样。
|
||||
之后进行球谐插值:
|
||||
![[IndirectLighting_SHLerp.png]]![[Per-PixelIndirectLighting.png]]![[CombineLighting.png]]
|
||||
|
||||
## Phase3:Build a lot of probes with Different Kinds
|
||||
![[ScreenProbeStructure.png]]
|
||||
|
||||
每隔 16 * 16 个像素采样一个ScreenSpaceProbe。采样的内容是Radiance与HitDistance,以8面体(Octahedron Mapping)的方式进行存储。
|
||||
|
||||
![[ScreenProbePlacement.png]]![[PlaneDistanceWeightingOfProbeInterpolation.png]]![[DetectNon-InterpolatableCases.png]]![[ScreenProbeAtlas.png]]
|
||||
将重采样的结果(部分区域的屏幕空间探针因为实际空间距离太远,进行插值没有意义,所以需要额外填充探针进行重采样)存在Atlas下面的空出来的区域。
|
||||
|
||||
![[ScreenProbeJitter.png]]
|
||||
|
||||
### 重要性采样
|
||||
![[ApproximateRadianceImportanceFromLastFrameProbes.png]]![[AccumulateNormalDistributionNearby.png]]![[NearbyNormalAccumulation.png]]![[StructuredImportanceSampling.png]]![[FixBudgetImportanceSampling.png]]
|
||||
|
||||
### Denoise
|
||||
![[Denoise_SpatialFilteringForProbe.png]]![[Denoise_GatherRadianceFromNeightbors.png]]![[ClampDistanceMismatching.png]]
|
||||
|
||||
### WorldSpace Probes and Ray Connecting
|
||||
![[WorldSapceRadianceCache.png]]![[WorldSpaceRadianceCache.png]]![[ConnectingRays.png]]![[ConnectingRays2.png]]![[ConnectingRay3.png]]![[PlacementAndCacheing.png]]
|
||||
|
||||
## Phase4:Shading Full Pixels with Screen Space Probes
|
||||
在ScreenSpaceProbes将场景Radiance都收集好。
|
||||
![[ConvertProbeRadianceTo3rdOrderSphericalHarmonic.png]]
|
||||
|
||||
![[FinalIntegarationWithSH.png]]
|
||||
|
||||
## 性能问题
|
||||
![[LumenTrackMethod.png]]
|
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2025-03-27 16:31:21
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
- [游戏引擎随笔 0x29:UE5 Lumen 源码解析(一)原理篇](https://zhuanlan.zhihu.com/p/499713106)
|
||||
- [游戏引擎随笔 0x30:UE5 Lumen 源码解析(二)Surface Cache 篇](https://zhuanlan.zhihu.com/p/516141543)
|
||||
- [游戏引擎随笔 0x31:UE5 Lumen 源码解析(三)光影篇](https://zhuanlan.zhihu.com/p/517756126)
|
||||
- [游戏引擎随笔 0x32:UE5 Lumen 源码解析(四)Radiosity 篇](https://zhuanlan.zhihu.com/p/522165652)
|
||||
- [游戏引擎随笔 0x33:UE5 Lumen 源码解析(五)Voxel Lighting 篇](https://zhuanlan.zhihu.com/p/525521811)
|
||||
- [游戏引擎随笔 0x34:UE5 Lumen 源码解析(六)Importance Sampling 篇](https://zhuanlan.zhihu.com/p/531442379)
|
||||
|
||||
# LumenScreenProbeGather
|
9
03-UnrealEngine/Rendering/RenderFeature/Mesh形变算法(FFD).md
Normal file
9
03-UnrealEngine/Rendering/RenderFeature/Mesh形变算法(FFD).md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2023-04-29 20:20:07
|
||||
excerpt: 摘要
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# Mesh形变算法
|
||||
https://blog.csdn.net/chenweiyu11962/article/details/130437525
|
82
03-UnrealEngine/Rendering/RenderFeature/Nanite学习笔记.md
Normal file
82
03-UnrealEngine/Rendering/RenderFeature/Nanite学习笔记.md
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Nanite学习笔记
|
||||
date: 2022-09-20 17:07:44
|
||||
excerpt:
|
||||
tags: Nanite
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
- UE5.4 Nanite源码阅读(一):NaniteStreaming:https://zhuanlan.zhihu.com/p/29306422635
|
||||
- UE5.4 Nanite源码阅读(二):NaniteCulling:https://zhuanlan.zhihu.com/p/30057230872
|
||||
- UE5.4 Nanite源码阅读(三):NaniteRasterize:https://zhuanlan.zhihu.com/p/30655592664
|
||||
- UE5.4 Nanite源码阅读(四):NaniteShading:https://zhuanlan.zhihu.com/p/30662515655
|
||||
|
||||
# Nanite使用笔记(浓缩自官方文档)
|
||||
## StaticMesh转换成Nanite的方法
|
||||
1. NaniteSettings-EnableNaiteSupport=>true
|
||||
2. Apply Changes
|
||||
3. ImportSettings-Mesh-BuildNanite=>true
|
||||
|
||||
## 功能支持
|
||||
Nanite支持混合模式(Blend Mode)是 **不透明(Opaque)** 类型的材质。其他材质类型则不被允许。
|
||||
|
||||
- 使用遮罩和半透明的混合模式
|
||||
- 延迟贴花
|
||||
- 将Nanite网格体用于网格体贴花。
|
||||
- Nanite支持将贴花投射到其表面。
|
||||
- 线框
|
||||
- 双面材质
|
||||
- 像素深度偏移
|
||||
- 世界位置偏移
|
||||
- 自定义逐实例数据
|
||||
|
||||
如果在材质中使用以下内容并指定给启用了Nanite的网格体,则会导致网格体显示异常:
|
||||
- 顶点插值器节点
|
||||
- 自定义UV
|
||||
|
||||
## 设置属性
|
||||
- **位置精度(Position Precision)**:模型精度,精度越高,占用硬盘空间越多。
|
||||
- **最低驻留(Minimum Residency)**:内存缓存大小,通过将模型缓存在内存中来减少固态硬盘的IO压力。
|
||||
- **保持三角形百分比(Keep Triangle Percent)**:保留的三角形的百分比。减少此百分比可优化磁盘大小。
|
||||
- **优化相对误差(Trim Relative Error)**:优化选项,该选项会移除小于该值的模型细节。默认为全部保留。
|
||||
- **回退三角形百分比(Fallback Triangle Percent)**:设置减少Nanite源网格体时保留的三角形百分比。当无法使用细节丰富的Nanite数据时(例如平台不支持Nanite渲染),或者使用Nanite数据不现实(例如在复杂碰撞中)时。**最低模型的面数比例**,设成100%即为原始模型。
|
||||
- **回退相对误差(Fallback Relative Error)**:设置允许为回退网格体移除的最大相对误差量。所生成回退网格体中视觉影响小于此相对误差量的所有细节将一律移除。
|
||||
|
||||
## 检查Nanite内容工具
|
||||
选择 **工具(Tools)>Nanite工具(Nanite Tools)** ,即可从主菜单打开Nanite工具(Nanite Tools)窗口。
|
||||

|
||||
|
||||
# 对启用了Nanite的网格体使用自定义回退网格体LOD
|
||||
回退网格体在引擎的许多功能中都会用到,例如复杂的逐多边形碰撞、光线追踪、光源烘培等等。它也可用于不支持Nanite的平台。生成回退网格体时,启用了Nanite的网格体始终使用源网格体的 **LOD0** 槽来自动生成回退网格体。但是,有时需要使用手动指定的回退网格体或一系列传统LOD,而不是自动生成的网格体。
|
||||
|
||||
这种控制级别允许你在项目中使用Nanite,同时也可以直接控制你在光线追踪反射中看到的几何体或不支持Nanite的平台中的几何体。按照以下步骤指定你自己的自定义回退网格体,或使用一系列LOD:
|
||||
1. 将 **回退三角形百分比(Fallback Triangle Percent)** 设置为 **0** ,以便回退网格体尽可能小,因为在使用此方法时它将被忽略。
|
||||
2. 使用此[传统LOD设置](https://docs.unrealengine.com/5.1/zh-CN/creating-levels-of-detail-in-blueprints-and-python-in-unreal-engine)程序将一个或多个LOD添加到网格体。
|
||||
3. 使用 **LOD导入(LOD Import)** 下拉菜单,从 **LOD设置(LOD Settings)** 分段 **导入LOD关卡1(Import LOD Level 1)** 。
|
||||
4. 在 **LOD设置(LOD Settings)** 分段下,将 **最低LOD(Minimum LOD)** 设置为 **1** 。这会使得Nanite生成的回退网格体被忽略。
|
||||
|
||||
复杂碰撞是一种特殊情况。使用 **通用设置(General Settings)** 下 **用于碰撞的LOD(LOD for Collision)** 属性,指定用于碰撞的LOD。所有LOD都可用于碰撞,包括LOD 0。
|
||||
|
||||
# 性能优化
|
||||
尽可能避免以下情况:
|
||||
|
||||
### 聚合几何体(毛发、树叶、草之类堆叠的模型)
|
||||
聚合几何体(Aggregate geometry )是指许多不连贯的对象在远处合并成单个体积,例如毛发、树叶和草。这种类型的几何体打破了Nanite的细节级别和遮挡剔除方式。
|
||||
|
||||
Nanite本身是一种分层细节级别结构,它依赖的方法是将小三角形精简为大三角形,在差异小到无法感知时,Nanite会选择较粗糙的三角形。这在连续的表面上效果很好,但在聚合体几何体上效果不佳,从远处看时它们更像是部分不透明的云,而不是固体表面。
|
||||
|
||||
因此,Nanite可能认为它无法像处理常见的固体表面那样大幅度减少聚合几何体,因此,在覆盖相同数量的像素时,会绘制出更多的三角形。
|
||||
|
||||
### 紧密堆叠的表面(地面大量穿插的细节模型)
|
||||
由于各种实际存在的限制,传统网格体的遮挡剔除使得大规模的模型搭建(kitbashing)流程几乎不可能实现。Nanite的高精细遮挡剔除可以实现使用这些类型的工作流,有助于减少开发流程中的麻烦。
|
||||
|
||||
正如上述"聚合几何体"小节中介绍的,导致过度绘制的一种情况是,可见表面与底部隐藏表面的距离过于接近。如果某个几何体妥当地隐藏在可见表面之下,Nanite检测并剔除它的成本是相当低的,甚至可以认为没有开销。然而,如果有一些相互堆叠的几何体,并且都位于最顶部的表面上,Nanite可能无法确定哪个位于上面或下面,导致两个几何体同时被绘制出来。
|
||||
|
||||
这种特殊剔除情况通常最糟糕,因为Nanite不知道哪个表面在最上层,导致绘制出所有内容。像这样的精度误差会随着屏幕尺寸和距离的变化而变化,所以,尽管10厘米的距离足够分开各个层,并且在近处看起来很好,但在更远的位置,距离差可能会小于一个像素,从而导致过度绘制。
|
||||
|
||||
### 面片法线和硬边法线
|
||||
有个值得注意的问题是,在导入细节丰富的网格体时,因为网格体有面片法线,两个不同多边形之间的法线不平滑。此问题很常见,并且容易忽视,应该加以避免,因为网格体中顶点共享不足会导致渲染性能和数据大小的开销变得非常大。
|
||||
|
||||
理想情况下,一个网格体的顶点数量要少于三角形数量。如果这个比例是2:1或更高,那就可能出现问题,尤其是当三角形数量较多时。如果比例为3:1,意味着网格体完全是面状的,每个三角形都有单独的三个顶点,没有一个顶点是和其他三角形共享的。大多数情况下,这是法线不一样导致的,因为法线不平滑。
|
||||
|
||||
考虑到这一点,顶点越多,意味着数据越多。这也意味着顶点变换工作更多,而比率高于2:1时会陷入一些缓慢的渲染路径。在硬表面建模中专门使用不会引起任何问题,没有不用的理由。然而,若意外遇到100%面片极密集的网格体,开销要比预期的高得多。另外,要注意在其他DCC软件包中生成的密集有机型表面的导入法线,其硬法线阈值在低模网格体上可能是合理的,但在Nanite中会增加不必要的开销。
|
File diff suppressed because it is too large
Load Diff
123
03-UnrealEngine/Rendering/RenderFeature/UE5 3DGaussians 插件笔记.md
Normal file
123
03-UnrealEngine/Rendering/RenderFeature/UE5 3DGaussians 插件笔记.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: UE5 3DGaussians 插件笔记
|
||||
date: 2023-12-22 11:44:33
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# c++
|
||||
插件的c++部分主要实现了
|
||||
- FThreeDGaussians——可以理解为一个场景或者根节点
|
||||
- FThreeDGaussiansTree——类似BVH的空间切分树
|
||||
- FThreeDGaussiansData——具体数据
|
||||
- ply点云文件导入,流程如下
|
||||
- FThreeDGaussiansImporterModule::PluginButtonClicked()
|
||||
- LoadPly(),载入`TArray<FThreeDGaussian>`数据。
|
||||
- 进行排序
|
||||
- 初始化一个`TArray<FThreeDGaussianSortPair> unsorted`并且进行排序。
|
||||
- 取得各种排序用参数DO_SPLIT_BY_3D_MORTON_ORDER、DO_SPLIT_BY_DISTANCE、MAX_TEXTURE_WIDHT、MAX_NUM_PARTICLES
|
||||
- 采用莫顿码分割法、距离排序法。
|
||||
- 莫顿码分割法:使用莫顿码进行排序,之后进行空间分割,构建一个三维加速结构。当当前区域点云数量小于MAX_NUM_PARTICLES后调用CreateDatum()。
|
||||
- 距离排序法:根据Position上三个分量中最大绝对值进行排序,之后调用CreateDatum()。
|
||||
- CreateDatum()
|
||||
- Sort3dMortonOrder()排序。
|
||||
- CreateExr()创建Exr Texture文件。
|
||||
- 将上一步创建的文件导入UE。
|
||||
- CreateActorBpSubclass(),创建3DGaussians蓝图Actor,并且查找SetData函数并且将数据塞入。
|
||||
|
||||
## FThreeDGaussians代码
|
||||
```c++
|
||||
struct FThreeDGaussiansData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FThreeDGaussiansData() {}
|
||||
FThreeDGaussiansData(const TArray<UTexture2D*>& textures, const FVector3f& in_minPos, const FVector3f& in_maxPos)
|
||||
{
|
||||
minPos = in_minPos;
|
||||
maxPos = in_maxPos;
|
||||
textureWidth = textures[0]->GetSizeX();
|
||||
position = textures[0];
|
||||
rotation = textures[1];
|
||||
scaleAndOpacity = textures[2];
|
||||
|
||||
for (int i = 3; i < textures.Num(); i++)
|
||||
{
|
||||
sh.Add(textures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") FVector3f minPos = FVector3f::Zero();
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") FVector3f maxPos = FVector3f::Zero();
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 textureWidth = -1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") UTexture2D* position;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") UTexture2D* rotation;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") UTexture2D* scaleAndOpacity;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") TArray<UTexture2D*> sh;
|
||||
};
|
||||
|
||||
/** 类似BVH的控件数据结构 */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FThreeDGaussiansTree
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FThreeDGaussiansTree() {}
|
||||
|
||||
// Axis for split (x=0, y=1, z=2)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 splitAxis = -1;
|
||||
// max value of the position of gaussian in child0 or leaf0 in "splitAxis" axis
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") float splitValue = 0.0f;
|
||||
|
||||
// index of child tree node (Index of TArray<FThreeDGaussiansTree> tree)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 childIndex0 = -1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 childIndex1 = -1;
|
||||
|
||||
// index of child data node (Index of TArray<FThreeDGaussiansData> data)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 leafIndex0 = -1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") int32 leafIndex1 = -1;
|
||||
};
|
||||
|
||||
/* 作为3D高斯数据的载荷 */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FThreeDGaussians
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FThreeDGaussians() {}
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") TArray<FThreeDGaussiansData> data;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "3D Gaussians") TArray<FThreeDGaussiansTree> tree;
|
||||
};
|
||||
```
|
||||
|
||||
# BP_3D_Gaussians_Base
|
||||
- BeginPlay:判断三维加速结构是否还子节点,如果有则开启Tick进行排序。
|
||||
- Tick:根据摄像机位置对三维加速结构进行排序。
|
||||
- ConstructionScript:
|
||||
1. 添加Niagara粒子组件,一个FThreeDGaussiansData生成一个粒子组件。
|
||||
2. 设置Niagara资产:NS_3D_Gaussians_sh0_mesh(勾选mesh选项)、NS_3D_Gaussians_sh0(SH角度)、NS_3D_Gaussians_sh1、NS_3D_Gaussians_sh2、NS_3D_Gaussians_sh3
|
||||
3. 设置粒子材质属性:
|
||||
1. AlbedoTint
|
||||
2. 剔除设置:CropEnabled、CropTranslation、CropRotation、CropExtent
|
||||
3. 数据贴图(FThreeDGaussiansData):texture_width、texture_position、texture_rotation、texture_scaleAndOpacity。
|
||||
4. SH数据贴图(FThreeDGaussiansData):根据角度设置Niagara里texture_sh_X的贴图。
|
||||
5. 社会中剔除空间 CropTranslations、CropRotators、CropExtents、KillTranslations、KillRotators、KillExtents。
|
||||
|
||||
|
||||
# 实现思路
|
||||
## 4D高斯
|
||||
1. 实现一个Niagara Module实现对Texture2DArray贴图采样。
|
||||
2. ~~使用Niagara Cache~~。
|
||||
3. 考虑 TextureStream机制以此节约显存。
|
||||
|
||||
## 使用RVT实现3D高斯 LOD思路
|
||||
AI数据侧:
|
||||
1. 确定点云数据是否可以划分成四叉树的数据结构,也就是将一堆点云按照一个**距离阈值** 进行分割,最终形成一个四叉树。
|
||||
1. 确定是否可以生成金字塔结构贴图(直接写入到Mipmap结构里),或者生成多张基于2的幕长度贴图。
|
||||
|
||||
UE侧:
|
||||
目前已经测试过SVT可以放入到Niagara Texture Sampler中。同时也可以将SVT放到Texture2DArray中。
|
||||
1. 将3D高斯各种贴图制作成SVT之后塞入Texture2DArray,在Niagara中采样。
|
||||
2. 在Niagara中根据Niagara 粒子ID对SVT进行采样。
|
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: VirtualTexture学习笔记
|
||||
date: 2023-07-29 12:26:09
|
||||
excerpt:
|
||||
tags: VirtualTexture
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
# 前言
|
||||
学习文章:
|
||||
- https://zhuanlan.zhihu.com/p/138484024
|
||||
|
||||
**Streaming VT** = 显存优化,但需要额外消耗一些性能。
|
||||
**Runtime VT** = 性能优化,可以预先将材质缓存到一张巨大的虚拟RT上,一般用于地形。
|
||||
|
||||
# VirtualTexture
|
||||
## 1. Software Virtual Texture
|
||||
在MegaTexture的基础上,id Software进一步提出了Virtual Texture的概念,这个概念取自于Virtual Memory,与虚拟内存类似的是,一个很大的Texture将不会全部加载到内存中,而是根据实际需求,将需要的部分加载。与虚拟内存不同的是,它不会阻塞执行,可以使用更高的mipmap来暂时显示,它对基于block的压缩贴图有很好的支持。 基本思路是,会将纹理的mipmap chain分割为相同大小的tile或page,这里的纹理叫虚纹理,然后通过某种映射,映射到一张内存中存在的纹理,这里的纹理是物理纹理,在游戏视野发生变化的时候,一部分物理纹理会被替换出去,一部分物理纹理会被加载。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
这样的机制不仅仅减少了带宽消耗和内存(显存)消耗,也带来了其他好处,比如有利于合批,而不用因为使用不同的Texture而打断合批,这样可以根据需求来组织几何使得更利于Culling,当然合批的好处是states change 变少。LightMap也可以预计算到一张大的Virtual Texture上,用来合批。
|
||||
|
||||
**其关键在于地址映射**,比如四叉树映射:
|
||||

|
||||
|
||||
# RuntimeVirtualTexture
|
||||
其主要目的是Cache材质计算结果。可以参考李文磊的视频:
|
||||
https://www.bilibili.com/video/BV1KK411L7Rg/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
|
||||
使用步骤:
|
||||
1. 创建RuntimeVirtualTexture、RuntimeVirtualTextureActor、Landscape资产。
|
||||
2. 在RuntimeVirtualTextureActor指定RuntimeVirtualTexture与Landscape资产,并且复制Landscape的Bound。
|
||||
3. 在Landscape的RenderToVirtualTexture中指定RuntimeVirtualTexture资产。
|
||||
4. 修改Landscape的材质,分别添加VirtualTexture Input、Export节点。 顺便也可以修改VirtualTexturePassType(某些需求只需要将结果渲染到VT中,无需在MainPass中渲染)。
|
||||
5. 此时Landscape的材质大概率显示不正确,这时需要添加VirtualTextureOutputLevel来计算UV(为了匹配非VT情况,可以加入RuntimeVirtualTextureReplace节点进行判断。
|
||||
|
||||
# UDIMs Virtual Texture(多象限虚拟贴图)
|
||||
https://docs.unrealengine.com/5.0/en-US/streaming-virtual-texturing-in-unreal-engine/
|
||||
参看UDIM Support章节。大致的方法就是把多个贴图按照`BaseName.####.[Support Image Format]`进行命名,####代表了坐标。将这些贴图放到同一目录,之后只需要导入1001坐标的贴图即可自动全部导入。
|
||||

|
||||
工作流可以参考:
|
||||
- https://www.youtube.com/watch?v=SkUW4JSYrEo
|
||||
- https://www.youtube.com/watch?v=HhT9oEB7xks
|
||||
|
||||
# 其他
|
||||
## 贴图优化
|
||||
具体参看:Managing the Texture Streaming Pool:https://www.youtube.com/watch?v=uk3W8Zhahdg
|
||||
大致步骤:
|
||||
- 使用Tools - Audit - Statistics查看贴图占用显存以及场景中的使用数量。
|
||||
- 通过ViewMode - OptimizationViewModes - RequiredTextureResolution 查看贴图在场景中所需要的占用比例是否合适。
|
||||
- 使用批量编辑功能修改Texture资产的MaximumTextureSize到指定Mipmap层级的分辨率即可。
|
||||
|
||||
另一个种方式就是使用SVT。
|
179
03-UnrealEngine/Rendering/RenderFeature/VirualShadowMap优化笔记.md
Normal file
179
03-UnrealEngine/Rendering/RenderFeature/VirualShadowMap优化笔记.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
title: VirualShadowMap优化笔记
|
||||
date: 2023-02-08 12:29:43
|
||||
excerpt:
|
||||
tags: VSM Nanite
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
VirualShadowMap与Nanite关系很大,如果场景中有很多非Nanite物体就需要将其都穿成Nanite,之后可以用Nanite Tool或者VirualShadowMap Cache显示模型来检查场景,具体可以参考[[Nanite学习笔记]]。具体优化步骤可以参考[[#优化案例]]。
|
||||
|
||||
# 相关命令(某个A站作品使用的参数)
|
||||
## VT
|
||||
- r.VT.MaxUploadsPerFrameInEditor 8
|
||||
- r.VT.MaxUploadsPerFrame 8
|
||||
- r.VT.MaxContinuousUpdatesPerFrameInEditor 2
|
||||
- r.VT.MaxContinuousUpdatesPerFrame 2
|
||||
- r.VT.MaxAnisotropy 8
|
||||
|
||||
## Lumen相关命令
|
||||
- r.MeshDrawCommands.DynamicInstancing 0
|
||||
- r .Shadow.Virtual.NonNanite.IncludeInCoarsePages 0
|
||||
- r.Lumen.Reflections.DownsampleFactor 1.98
|
||||
- r.Lumen.ScreenProbeGather.Temporal.DistanceThreshold 1.7
|
||||
- r.RayTracing.NormalBias 5.0
|
||||
- r.Lumen.ScreenProbeGather.MaxRayIntensity 10
|
||||
- r.Lumen.ScreenProbeGather.ScreenTraces.HZBTraversal 0
|
||||
- r.Lumen.Reflections.HierachicalScreenTraces.MaxIterations 4
|
||||
|
||||
## VirualShadowMap
|
||||
- r.Shadow.RadiusThreshold 0.05
|
||||
- r.ShadowVirtualClip .LastLevel 15
|
||||
- r.Shadow.Virtual.ResolutionLodBiasDirectional -1.1
|
||||
- r.Shadow.Virtual.Clipmap.UseConservativeCulling 0
|
||||
- r.Shadow.Virtual.Cache.MaxMaterialPositionInvalidationRange 3500
|
||||
- r.Shadow.Virtual.ForceOnlyVirtualShadowMaps 1
|
||||
- r.Shadow.Virtual.0.CulingFarCulingFar
|
||||
|
||||
# 其他优化思路
|
||||
其次,local灯光的数量对VSM的性能有较大影响。一方面可以考虑减少不必要的局部光源数量,此外可以尝试使用One Pass Projection功能(实验性),来提高性能。开启方法如下:
|
||||
- `r.UseClusteredDeferredShading 1`
|
||||
- `r.Shadow.Virtual.OnePassProjection 1`
|
||||
|
||||
然后还可以通过调整下列CVar(默认值16)到更小的数字,来强行限制每个像素受到影响的灯光数量,来提高性能。
|
||||
`r.Shadow.Virtual.OnePassProjection.MaxLightsPerPixel`
|
||||
|
||||
此外,还有一些实践思路供参考,例如:
|
||||
减少屏幕上大面积像素受到多个大范围局部灯光的影响(**减少多个局部灯光重叠影响同一片大面积区域**);
|
||||
减小局部灯光的**Source Radius**属性和方向光的**Source Angle**属性可以降低**VSM Ray的数量**;
|
||||
关闭数量庞大的**次要模型的投影**;
|
||||
关闭**超远距离**的背景物体模型的**投影**等;
|
||||
|
||||
# VirualShadowMap使用笔记(浓缩自官方文档)
|
||||
启用**VirualShadowMap**之后:
|
||||
1. 无论距离如何,**Nanite几何体**始终使用**虚拟阴影贴图**渲染阴影,因为这是性能最高的选项,可提供最高质量。可以通过控制台变量 `r.Shadow.Virtual.UseFarShadowCulling 0` 使非Nanite几何体的行为方式与Nanite相同。
|
||||
2. **静态烘焙阴影**将会失效。
|
||||
3. **距离场阴影**主要作用于非Nanite物体(比如大量植被)超出**动态阴影距离可移动光源(Dynamic Shadow Distance Movable Light)** 距离的阴影渲染。
|
||||
4. 局部光源(**点光源和聚光光源**)不受影响,依然会使用**距离场阴影**进行渲染。
|
||||
5. **光线追踪阴影**的优先级仍然高于VSM
|
||||
|
||||
## 相关命令
|
||||
- r.Shadow.Virtual.ResolutionLodBiasLocal:调整分辨率。
|
||||
- r.Shadow.Virtual.NonNanite.IncludeInCoarsePages 0 :尝试禁止非Nanite对象渲染到CoarsePages。
|
||||
- 可视化参数
|
||||
- r.Shadow.Virtual.Visualize [mode] :在Virtual Shadow Map可视化模式下,此命令指定要显示的通道。**Cache** 和 **vpage** 是用于可视化的两个常用选择,**none** 可禁用vsm可视化。
|
||||
- mask
|
||||
- Mip
|
||||
- vpage
|
||||
- cache
|
||||
- raycount
|
||||
- clipmapvirtual
|
||||
- ShowFlag.VisualizeVirtualShadowMap:指定可视化模式时,启用虚拟阴影贴图可视化。
|
||||
- r.Shadow.Virtual.Visualize.Layout:选择虚拟阴影贴图可视化的布局。
|
||||
- **0** 表示全屏
|
||||
- **1** 表示缩略图
|
||||
- **2** 表示分屏
|
||||
- r.Shadow.Virtual.Visualize.DumpLightNames:将带有虚拟阴影贴图的当前光源列表输出到控制台。
|
||||
- r.Shadow.Virtual.Visualize.LightName [光源名称]:按名称指定光源,接受部分或全部匹配。
|
||||
- r.Shadow.Virtual.Cache.DrawInvalidatingBounds 1:显示缓存失效边界。
|
||||
- r.Shadow.Virtual.Cache 0:禁用缓存。
|
||||
|
||||
## 相关可视化
|
||||
- View Modes-Virtual Shadow Map:在大纲中选中光源可以查看对应光源的渲染信息。
|
||||
- Show > Visualize > **仅绘制导致VSM失效的几何体(Draw only Geometry Causing VSM Invalidation)**
|
||||
- r.ShaderPrintEnable 1:显示计数器
|
||||
- r.Shadow.Virtual.ShowStats 1(或2,以仅显示页统计数据)
|
||||
|
||||
## 非Nanite多边形渲染
|
||||
可变形多边形(SkeletalMesh、WorldPositionOffset、PixelDepthOffset)都会使**VirtualShadowCache** 失效。
|
||||
|
||||
在某些情况下,例如草,有时是植被,仅使用[接触阴影](https://docs.unrealengine.com/5.1/zh-CN/contact-shadows-in-unreal-engine)足以替代高分辨率阴影贴图。如果前景中需要细节丰富的阴影,请考虑以下事项以帮助降低性能开销:
|
||||
- 非Nanite对象仍然遵循常规的阴影CPU剔除设置,例如 `r.Shadow.RadiusThreshold`。使用这些来帮助控制将这些对象渲染到虚拟阴影贴图的开销。
|
||||
- 在有大量植被的场景中,强烈建议使用 `r.Shadow.Virtual.NonNanite.IncludeInCoarsePages 0` 禁用粗页中的非Nanite对象。或者,如果不需要,请考虑[完全禁用粗页](https://docs.unrealengine.com/5.1/zh-CN/virtual-shadow-maps-in-unreal-engine#%E7%B2%97%E9%A1%B5)。
|
||||
- 使用网格体LOD在效果不再明显的距离处切换到不使用WPO/PDO的材质。在某些情况下,可以为远处的这些对象**关闭动态阴影投射**,并完全依赖屏幕空间**接触阴影**。
|
||||
|
||||
对于定向光源,还有其他可用选项:
|
||||
- 距离场阴影替代超出 **Dynamic Shadow Distance Movable Light** 距离范围的非Nanite几何体,该距离通过光源的级联阴影贴图(Cascaded Shadow Maps)分段设置。为远处的非Nanite切换到**距离场阴影**,可以大大提高性能,因为此几何体没有Nanite提供的细粒度LOD缩放。
|
||||
- 在某些情况下,创建移除WPO/PDO的材质LOD可能不切实际,但这些转换的最终效果在远处不明显。使用 `r.Shadow.Virtual.Cache.MaxMaterialPositionInvalidationRange` 设置距离(以厘米为单位),超过该距离时,将忽略这些材质的缓存失效。
|
||||
|
||||
# Shadow Map Ray Tracing(阴影贴图光线追踪)
|
||||
一种渲染**软阴影**的方式。可通过以下命令来设置采样数值:
|
||||
- `r.Shadow.Virtual.SMRT.RayCountLocal` :每个像素采样光线数量。
|
||||
- `r.Shadow.Virtual.SMRT.RayCountDirectional`:每个像素采样光线数量。
|
||||
- `r.Shadow.Virtual.SMRT.SamplesPerRayLocal`:每个像素受到VSM的光线追踪影响的数量。
|
||||
- `r.Shadow.Virtual.SMRT.SamplesPerRayDirectional`:每个像素受到VSM的光线追踪影响的数量。
|
||||
- `r.Shadow.Virtual.SMRT.MaxRayAngleFromLight`:通过降低半影的精度来优化性能。
|
||||
- `r.Shadow.Virtual.SMRT.RayLengthScaleDirectional`:通过降低半影的精度来优化性能。
|
||||
|
||||
# ## GPU分析和优化
|
||||
- **RenderVirtualShadowMaps(Nanite)** 包含所有与Nanite几何体渲染到VSM中相关的内容。所有定向光源都在单个Nanite通道中渲染,所有局部光源都在第二个通道中渲染。
|
||||
- **RenderVirtualShadowMaps(非Nanite)** 负责处理非Nanite几何体的渲染。每个可见光源都有一个单独的通道,各种对象和实例拥有单独的绘制调用,这点与传统阴影贴图渲染相同。
|
||||
- **图集(Atlas)** 和 **立方体贴图(Cubemap)** 与其他类似通道(包括VSM通道),都只是渲染传统阴影贴图。在虚拟阴影贴图的路径中,仍有少部分类型的几何体不受支持,它们遵循传统路径。如果没有不受支持的几何体投射阴影,这些通道将不会运行或分配阴影贴图存储。这些通道和相关的开销可以使用cvar `r.Shadow.Virtual.ForceOnlyVirtualShadowMaps 1` 完全禁用,在这种情况下,所有不受支持的几何体类型都完全不会投射阴影。
|
||||
|
||||
## 提升非Nanite性能
|
||||
除了改进缓存之外,还有许多方法可以提高非Nanite阴影渲染的性能。
|
||||
|
||||
- 在你的项目的几何体中**尽可能多**的部分上**启用Nanite**。
|
||||
- Nanite几何体在虚拟阴影贴图中的渲染效率要高得多,无论多边形数量如何,都应该在每个适用的情况下作为首选。
|
||||
- Nanite几何体可以遮挡非Nanite几何体并避免虚假缓存失效。因此,唯一的非Nanite对象应该是Nanite本身不支持的对象,例如变形对象(蒙皮网格体),或使用世界位置偏移(WPO)和像素深度偏移(PDO)的材质。
|
||||
- **非Nanite对象**应具有**完整的网格体LOD层级设置**。
|
||||
- 非Nanite网格体具有LOD设置很重要,否则渲染到小页的开销会非常高。
|
||||
- 如果可以,建议在**距离太远**而使效果不明显时切换到**非变形网格体(无WPO/PDO材质)**。
|
||||
- CPU剔除控制台变量对于渲染到虚拟阴影贴图的非Nanite网格体仍然有用
|
||||
- 使用控制台变量 `r.Shadow.RadiusThreshold`,调整渲染到虚拟阴影贴图中的非Nanite对象的CPU剔除值。这有助于控制远处小型对象的开销。
|
||||
- 将[距离场阴影](https://docs.unrealengine.com/5.1/zh-CN/distance-field-soft-shadows-in-unreal-engine)用于非Nanite对象的远距离阴影投射。
|
||||
- 对于定向光源,在超出某个范围时通常需要切换到距离场阴影,与级联阴影贴图相同。使用虚拟阴影贴图,仅非Nanite几何体将切换到使用距离场阴影,而Nanite几何体仍采用全细节阴影。
|
||||
- 在粗页中禁用非Nanite几何体可以提高性能
|
||||
- 在粗页中禁用非Nanite几何体通常可以实现大幅的性能提升,因为非Nanite几何体在渲染到大页中时通常效率低下。
|
||||
|
||||
## 阴影投射
|
||||
**阴影投射(Shadow Projection)** 类别是使用阴影贴图光线追踪对阴影贴图取样产生的开销。这些通道位于 **光源(Lights) | DirectLighting | UnbatchedLights** 之下,通常每个相关光源都有一个VSM投射通道。产生最高开销的通道一般都是 **VirtualShadowMapProjection** 中的主SMRT循环。其余的开销应该相对较低。
|
||||
|
||||
# 其他
|
||||
## 虚拟现实
|
||||
虚拟阴影贴图尚未完全支持虚拟现实。右眼视角可能存在定向光源瑕疵。
|
||||
|
||||
## 分屏
|
||||
分屏受到的测试极少,性能可能很差。
|
||||
|
||||
## 物理页池溢出
|
||||
使用虚拟阴影贴图,场景中所有光源的所有阴影数据都存储在一个大型纹理池中。默认池大小受 **阴影(Shadow)** 可扩展性设置的影响,但在具有许多使用高分辨率阴影的光源的场景中,可能需要进行调整。
|
||||
|
||||
或者,可能需要在低端硬件上进行调整,以节省显存。
|
||||
|
||||
页池大小可以使用 `r.Shadow.Virtual.MaxPhysicalPages` 进行调整。连续使用 `r.ShaderPrintEnable 1` 和 `r.Shadow.Virtual.ShowStats 2` 启用虚拟阴影贴图统计数据,将显示有关当前页池使用情况的统计数据。
|
||||
|
||||
## 场景捕获
|
||||
在一些情况下,场景捕获组件会导致整个虚拟阴影贴图缓存无效化。具体症状体现为 _Invalidations_ 在虚拟阴影贴图数据中变低,但是缓存的页面也变低(甚至会变为0),缓存的页面会全部变成红色。
|
||||
|
||||
发生该情况,试着隐藏或移除场景中的场景捕获Actor来验证它们是否在导致这个问题。
|
||||
|
||||
## 材质
|
||||
**仅支持简单的次表面材质**。尚未实现次表面轮廓和传输。如果某个材质正在使用它们,则该材质将被遮蔽,就好像它不透明一样。
|
||||
|
||||
### 阴影分辨率
|
||||
与传统阴影贴图相比,虚拟阴影贴图的分辨率显著提升,但浅光源角(或投影锯齿)和非常大的局部光源可能耗尽可用的虚拟分辨率。根据几何体的表面,这可能会呈现为盒状阴影和偏差问题。
|
||||
定向光源裁剪图不太容易耗尽分辨率,但非常窄的摄像机视野最终也会耗尽这些分辨率。
|
||||
|
||||
阴影贴图的投影锯齿并没有简单的解决方案。即使使用虚拟阴影贴图,也必须注意避免最坏的情况,并平衡分辨率和性能。
|
||||
|
||||
# 优化案例
|
||||
## 场景存在问题
|
||||
1. 场景中的很躲静态物体没有启用Nanite,使得VirualShadowMap性能变低。
|
||||
2. 远景的天空球(SM_Skybox_Mesh)、雾气面片、近景3个人 投射阴影。
|
||||
3. 植被问题。
|
||||

|
||||
|
||||
## 解决方法
|
||||
1.让场景模型启用Nanite。
|
||||

|
||||
|
||||
可以通过ViewMode - VirualShadowMap - Cache模式来显示缓存失效情况,,以此来找到非Nanaite物体。
|
||||
|
||||

|
||||
|
||||
之后使用Show > Visualize > 仅绘制导致VSM失效的几何体(Draw only Geometry Causing VSM Invalidation)来精准查找未开启Nanite的物体。
|
||||

|
||||
|
||||
2. 关闭远景天空球(SM_Skybox_Mesh)、雾气面片与近景3个人模型的 Cast Shadow选项。尝试启用远景天空球的Nanite选项。
|
||||
3. 植被分两种情况:有/无WorldPositionOffset。无的话直接转成Nanite即可。有的话建议去除 Cast Shadow选项,并且勾选Contact Shadow。
|
10
03-UnrealEngine/Rendering/RenderFeature/体积渲染相关文章.md
Normal file
10
03-UnrealEngine/Rendering/RenderFeature/体积渲染相关文章.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2025-05-03 18:12:31
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
- 知乎文章
|
||||
- 烟雾的体积渲染方法:https://zhuanlan.zhihu.com/p/656758416
|
68
03-UnrealEngine/Rendering/RenderFeature/平面反射.md
Normal file
68
03-UnrealEngine/Rendering/RenderFeature/平面反射.md
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Untitled
|
||||
date: 2024-07-19 22:09:12
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
|
||||
```c++
|
||||
for (auto It = SceneCaptureComponent->HiddenActors.CreateConstIterator(); It; ++It)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
|
||||
if (Actor)
|
||||
{
|
||||
for (UActorComponent* Component : Actor->GetComponents())
|
||||
{
|
||||
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(Component))
|
||||
{
|
||||
View->HiddenPrimitives.Add(PrimComp->ComponentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SceneCaptureComponent->PrimitiveRenderMode == ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList)
|
||||
{
|
||||
View->ShowOnlyPrimitives.Emplace();
|
||||
|
||||
for (auto It = SceneCaptureComponent->ShowOnlyComponents.CreateConstIterator(); It; ++It)
|
||||
{
|
||||
// If the primitive component was destroyed, the weak pointer will return NULL.
|
||||
UPrimitiveComponent* PrimitiveComponent = It->Get();
|
||||
if (PrimitiveComponent)
|
||||
{
|
||||
View->ShowOnlyPrimitives->Add(PrimitiveComponent->ComponentId);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto It = SceneCaptureComponent->ShowOnlyActors.CreateConstIterator(); It; ++It)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
|
||||
if (Actor)
|
||||
{
|
||||
for (UActorComponent* Component : Actor->GetComponents())
|
||||
{
|
||||
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(Component))
|
||||
{
|
||||
View->ShowOnlyPrimitives->Add(PrimComp->ComponentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SceneCaptureComponent->ShowOnlyComponents.Num() > 0 || SceneCaptureComponent->ShowOnlyActors.Num() > 0)
|
||||
{
|
||||
static bool bWarned = false;
|
||||
|
||||
if (!bWarned)
|
||||
{
|
||||
UE_LOG(LogRenderer, Log, TEXT("Scene Capture has ShowOnlyComponents or ShowOnlyActors ignored by the PrimitiveRenderMode setting! %s"), *SceneCaptureComponent->GetPathName());
|
||||
bWarned = true;
|
||||
}
|
||||
}
|
||||
|
||||
ViewFamily.Views.Add(View);
|
||||
```
|
45
03-UnrealEngine/Rendering/RenderFeature/深度相关.md
Normal file
45
03-UnrealEngine/Rendering/RenderFeature/深度相关.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: 深度相关
|
||||
date: 2024-09-09 16:49:08
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
# 相关代码
|
||||
位于common.ush
|
||||
```c++
|
||||
#define NearDepthValue (HAS_INVERTED_Z_BUFFER ? 1.0f : 0.0f)
|
||||
#define FarDepthValue (HAS_INVERTED_Z_BUFFER ? 0.0f : 1.0f)
|
||||
```
|
||||
|
||||
可以通过**DeviceZ == FarDepthValue**来判断是否处于最远端。
|
||||
```c++
|
||||
float DeviceZ = DepthReadDisabled ? FarDepthValue : LookupDeviceZ(UvBuffer);
|
||||
#endif
|
||||
|
||||
if (DeviceZ == FarDepthValue)
|
||||
{
|
||||
// Get the light disk luminance to draw
|
||||
LuminanceScale = SkyAtmosphere.SkyLuminanceFactor;
|
||||
#if SOURCE_DISK_ENABLED
|
||||
if (SourceDiskEnabled > 0)
|
||||
{
|
||||
PreExposedL += GetLightDiskLuminance(WorldPos, WorldDir, 0);
|
||||
#if SECOND_ATMOSPHERE_LIGHT_ENABLED
|
||||
PreExposedL += GetLightDiskLuminance(WorldPos, WorldDir, 1);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if RENDERSKY_ENABLED==0
|
||||
// We should not render the sky and the current pixels are at far depth, so simply early exit.
|
||||
// We enable depth bound when supported to not have to even process those pixels.
|
||||
OutLuminance = PrepareOutput(float3(0.0f, 0.0f, 0.0f), float3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
//Now the sky pass can ignore the pixel with depth == far but it will need to alpha clip because not all RHI backend support depthbound tests.
|
||||
// And the depthtest is already setup to avoid writing all the pixel closer than to the camera than the start distance (very good optimisation).
|
||||
// Since this shader does not write to depth or stencil it should still benefit from EArlyZ even with the clip (See AMD depth-in-depth documentation)
|
||||
clip(-1.0f);
|
||||
return;
|
||||
#endif
|
||||
```
|
Reference in New Issue
Block a user