This commit is contained in:
2025-08-02 12:09:34 +08:00
commit e70b01cdca
2785 changed files with 575579 additions and 0 deletions

View 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

View 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 多线程架构中的**安全阀**,通过它开发者可以:
✅ 确保渲染操作在指定时间点前完成
✅ 安全操作渲染资源(创建/更新/销毁)
✅ 避免游戏线程与渲染线程的竞态条件

View File

@@ -0,0 +1,9 @@
---
title: Fur制作方案
date: 2023-08-15 11:17:42
excerpt:
tags: Fur 毛发
rating: ⭐
---
# 前言
https://xbdev.net/directx3dx/specialX/Fur/

View 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

View File

@@ -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显示不正常时就可以使用这个工具来查找问题。
![Lumen Card Placement Visualization](https://docs.unrealengine.com/5.0/Images/building-virtual-worlds/lighting-and-shadows/global-illumination/lumen/TechOverview/mesh-card-placement-visualization-alt.jpg)
注意:
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并对每个采样点进行更多的光线追踪采样。牺牲密度来提高精度 之后使用插值的方式来填补周围像素。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_DownsampledTracing.png)
Lumen还使用**世界空间的辐射度缓存**,也就是探针(数量较少),这些探针将会重复运用于多个像素。使用`r.Lumen.RadianceCache.Visualize 1`可以显示出探针。与VolumeLightMap不同Lumn并不使用这些探针直接渲染GI效果而是从像素开始RayTracing。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_WorldRadianceCache.png)
Lumen与降噪器效果比较
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_GIComparison.png)
### 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会去实际计算反射结果以提供更准确的效果。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_ReflectionQuality.png)
此时多重反弹的反射效果与天光依然由表面缓存提供。
#### 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
- FutureLumen Reflections
- Bring Over Remaining Feature From Ray Traced Reflection
## 最佳实践
- Emissive
1. 使用Lumen时不要使用自发光材质模型代替灯光。容易产生大量噪点
2. 使用较大的自发光材质模型做出一些效果。比如夜晚房间中的电视机效果。以及场景中微弱补光。
3. 使用较小的自发光材质模型+灯光。![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_Emissive1.png)
- BaseColor
1. BaseColor与GI影响很大。
2. 非常暗以及糟乱BaseColor会产生很烂的GI效果。![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lighting/Lumen_BaseColor.png)
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. 把错误的模型拆开。
## 灯光矫正

View 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**。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230212113939.png)
## 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、RadianceDirect Lighting 以及最终的渲染画面。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230212110227.png)
## Lumen Scene
可以在`ViewMode-Lumen-LumenScene`中查看。
Lumen Scene 包含了 DF 描述的场景几何表达以及 Surface Cache 描述的场景材质表达,是一个完整的系统。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230212112912.png)
## 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**。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230212114305.png)
那么 Lumen 又是如何对 Global DF 进行 Lighting 的呢?这就要引入 [[#Voxel Lighting]]。
### Screen Space Probe放置方法
首先每隔 16 个像素均匀的放置,然后进行自适应放置,检查 Probe 网格中的像素是否可被插值,即 4 个 Corner Probe 是否被当前像素遮挡,如果是则在当前像素位置上增加一个 Probe如此迭代每次迭代增加一倍分辨率直到插值差值失败的像素很少或达到指定的分辨率阈值。如下图所示橘红色为差值失败的像素
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230212113224.png)
## Voxel Lighting
Lumen的**Voxel Lighting**存储的是较粗颗粒度的**空间光照信息**应该6轴向的光照信息。从采样**Radiance Cache**获得。为了精确,采用了和**Surface Cache**一样的世界坐标6轴向对齐方案。最后通过采样3轴向的光照数据再根据Ray方向进行权重插值取得结果。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214145323.png)
为了进一步优化性能减少内存Lumen 还为 Voxel Lighting 生成了 Clipmap用于覆盖不同范围这样可以根据采样点所在的 Clipmap 获取光照。通过 ClipmapVoxel 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 中的稀疏分布:
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214153405.png)
**Global DF Tracing 失败时**,可以快速查找 Screen Space Probe 周围的 8 个 Word Space Probe在 Ray 方向上**对 Word Space Probe Radiance Cache 采样,三线性插值混合这些 Radiance**,从而得到最终的 Distant Lighting这种方式使光照更加稳定大大缓解了噪声。此外还可以与采样的 Voxel Lighting 进行插值混合,使光照过渡更加平滑。下图是 Radiance Cache 对比图,左图关闭,右图开启:
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214163930.png)
## Importance Sampling
Lumen 引入了业界降噪常用的方法重要性采样Importance Sampling通过使用上一帧的 Radiance Cache 作为生成当前帧的光线方向的引导,使光线尽可能朝向光源方向追踪,这样就可以在不增加光线预算的情况下实现了降噪,如下所示:
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214165242.png)
## Shadow
阴影的本质就是光照的可见性。Lumen 为 Surface Cache 标记 Shadow Mask这样在 Lighting 时直接乘以这个 Mask 即可。Shadow Mask 可以直接使用之前已经生成的Virtual Shadow Map。
但是这并不够完整,原因在于 **VirtualShadow 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 的对比图,降噪效果明显:
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214165813.png)
## 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**
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214170223.png)
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230214170237.png)

View File

@@ -0,0 +1,12 @@
---
title: Lumen学习笔记3——代码分析
date: 2023-02-10 15:32:29
excerpt:
tags: Lumen
rating: ⭐
---
# Lumen
Lumen的逻辑开始于DeferredShadingRenderer.cpp的`RenderLumenSceneLighting()`

View File

@@ -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 MapsRSM2005
论文地址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
# VXGINvidia 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
SIGGRAPH2015Advances 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]]

View File

@@ -0,0 +1,16 @@
---
title: 未命名
date: 2025-03-27 16:31:21
excerpt:
tags:
rating: ⭐
---
# 前言
- [游戏引擎随笔 0x29UE5 Lumen 源码解析(一)原理篇](https://zhuanlan.zhihu.com/p/499713106)
- [游戏引擎随笔 0x30UE5 Lumen 源码解析Surface Cache 篇](https://zhuanlan.zhihu.com/p/516141543)
- [游戏引擎随笔 0x31UE5 Lumen 源码解析(三)光影篇](https://zhuanlan.zhihu.com/p/517756126)
- [游戏引擎随笔 0x32UE5 Lumen 源码解析Radiosity 篇](https://zhuanlan.zhihu.com/p/522165652)
- [游戏引擎随笔 0x33UE5 Lumen 源码解析Voxel Lighting 篇](https://zhuanlan.zhihu.com/p/525521811)
- [游戏引擎随笔 0x34UE5 Lumen 源码解析Importance Sampling 篇](https://zhuanlan.zhihu.com/p/531442379)
# LumenScreenProbeGather

View 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

View 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窗口。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20220920172530.png)
# 对启用了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关卡1Import LOD Level 1** 。
4. 在 **LOD设置LOD Settings** 分段下,将 **最低LODMinimum LOD** 设置为 **1** 。这会使得Nanite生成的回退网格体被忽略。
复杂碰撞是一种特殊情况。使用 **通用设置General Settings** 下 **用于碰撞的LODLOD 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

View 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_sh0SH角度、NS_3D_Gaussians_sh1、NS_3D_Gaussians_sh2、NS_3D_Gaussians_sh3
3. 设置粒子材质属性:
1. AlbedoTint
2. 剔除设置CropEnabled、CropTranslation、CropRotation、CropExtent
3. 数据贴图FThreeDGaussiansDatatexture_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进行采样。

View File

@@ -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,这里的纹理叫虚纹理,然后通过某种映射,映射到一张内存中存在的纹理,这里的纹理是物理纹理,在游戏视野发生变化的时候,一部分物理纹理会被替换出去,一部分物理纹理会被加载。
![](https://pic4.zhimg.com/80/v2-8dd6e9e7750884bbab8d6da5fe221ec3_720w.webp)
这样的机制不仅仅减少了带宽消耗和内存显存消耗也带来了其他好处比如有利于合批而不用因为使用不同的Texture而打断合批这样可以根据需求来组织几何使得更利于Culling当然合批的好处是states change 变少。LightMap也可以预计算到一张大的Virtual Texture上用来合批。
**其关键在于地址映射**,比如四叉树映射:
![](https://pic1.zhimg.com/80/v2-754fb67195882775cb95dcb1d2366ad8_720w.webp)
# 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坐标的贴图即可自动全部导入。
![image.png](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/20230730210018.png)
工作流可以参考:
- https://www.youtube.com/watch?v=SkUW4JSYrEo
- https://www.youtube.com/watch?v=HhT9oEB7xks
# 其他
## 贴图优化
具体参看Managing the Texture Streaming Poolhttps://www.youtube.com/watch?v=uk3W8Zhahdg
大致步骤:
- 使用Tools - Audit - Statistics查看贴图占用显存以及场景中的使用数量。
- 通过ViewMode - OptimizationViewModes - RequiredTextureResolution 查看贴图在场景中所需要的占用比例是否合适。
- 使用批量编辑功能修改Texture资产的MaximumTextureSize到指定Mipmap层级的分辨率即可。
另一个种方式就是使用SVT。

View 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. 植被问题。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%9B%BE%E7%89%871.png)
## 解决方法
1.让场景模型启用Nanite。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%9B%BE%E7%89%872.png)
可以通过ViewMode - VirualShadowMap - Cache模式来显示缓存失效情况,以此来找到非Nanaite物体。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%9B%BE%E7%89%873.png)
之后使用Show > Visualize > 仅绘制导致VSM失效的几何体Draw only Geometry Causing VSM Invalidation来精准查找未开启Nanite的物体。
![800](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Images/%E5%9B%BE%E7%89%874.png)
2. 关闭远景天空球SM_Skybox_Mesh、雾气面片与近景3个人模型的 Cast Shadow选项。尝试启用远景天空球的Nanite选项。
3. 植被分两种情况:有/无WorldPositionOffset。无的话直接转成Nanite即可。有的话建议去除 Cast Shadow选项并且勾选Contact Shadow。

View File

@@ -0,0 +1,10 @@
---
title: 未命名
date: 2025-05-03 18:12:31
excerpt:
tags:
rating: ⭐
---
# 前言
- 知乎文章
- 烟雾的体积渲染方法:https://zhuanlan.zhihu.com/p/656758416

View 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);
```

View 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
```