222 lines
10 KiB
Markdown
Raw Normal View History

2024-05-15 17:25:57 +08:00
---
title: ToonPostProcess
date: 2024-05-15 16:50:13
excerpt:
tags:
rating: ⭐
---
2024-10-22 17:22:11 +08:00
# FFT
2024-10-21 14:35:55 +08:00
# Bloom
Bloom主要分
- Bloom
- FFTBloom
- LensFlares
2024-10-22 17:22:11 +08:00
BloomThresholdClampMin = "-1.0", UIMax = "8.0"。
相关逻辑位于:
```c++
if (bBloomSetupRequiredEnabled)
{
const float BloomThreshold = View.FinalPostProcessSettings.BloomThreshold;
FBloomSetupInputs SetupPassInputs;
SetupPassInputs.SceneColor = DownsampleInput;
SetupPassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer;
SetupPassInputs.EyeAdaptationParameters = &EyeAdaptationParameters;
SetupPassInputs.LocalExposureParameters = &LocalExposureParameters;
SetupPassInputs.LocalExposureTexture = CVarBloomApplyLocalExposure.GetValueOnRenderThread() ? LocalExposureTexture : nullptr;
SetupPassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture;
SetupPassInputs.Threshold = BloomThreshold;
SetupPassInputs.ToonThreshold = View.FinalPostProcessSettings.ToonBloomThreshold;
DownsampleInput = AddBloomSetupPass(GraphBuilder, View, SetupPassInputs);
}
```
2024-10-21 18:03:20 +08:00
## FFTBloom
2024-10-22 11:39:07 +08:00
***普通Bloom算法只能做到圆形光斑对于自定义形状的就需要使用FFTBloom。***
2024-10-22 17:22:11 +08:00
- FFT Bloom:https://zhuanlan.zhihu.com/p/611582936
- Unity FFT Bloom:https://github.com/AKGWSB/FFTConvolutionBloom
2024-10-22 12:20:22 +08:00
### 频域与卷积定理
图像可以视为二维的信号,而一个信号可以通过 **不同频率** 的 Sine & Cosine 函数的线性叠加来近似得到。对于每个频率的函数,我们乘以一个常数振幅并叠加到最终的结果上,这些振幅叫做 **频谱**。值得注意的是所有的 F_k 都是 **复数**
2024-10-22 11:39:07 +08:00
2024-10-22 12:20:22 +08:00
![](https://pic2.zhimg.com/80/v2-64b4c2d33d90816cc9bfcf875f618d9f_720w.webp)
此时频域上的每个振幅不再代表某个单个的时域样本,而是代表该频段的 Sine & Cosine 函数对时域信号的 **整体** 贡献。频域信号包含了输入图像的全部时域信息,***因此卷积定理告诉我们在时域上对信号做卷积,等同于将源图像与滤波盒图像在频域上的频谱(上图系数 V_k做简单复数 **乘法***
![](https://pic1.zhimg.com/80/v2-abc8c8d19dc3ded6c282075cc4d2f022_720w.webp)
一一对位的乘法速度是远远快于需要循环累加的朴素卷积操作。因此接下来我们的目标就是找到一种方法,建立图像信号与其频域之间的联系。在通信领域通常使用傅里叶变换来进行信号的频、时域转换
### 相关代码
2024-10-22 17:22:11 +08:00
- c++
- AddFFTBloomPass()
- FBloomFinalizeApplyConstantsCS Bloom计算完成
- AddTonemapPass()PassInputs.Bloom = Bloom与PassInputs.SceneColorApplyParamaters
- Shader
-
2024-10-21 18:03:20 +08:00
**FBloomFindKernelCenterCS**用于找到Bloom效果的核Kernel中心(纹理中找到最亮的像素)。用于在一个并记录其位置。主要通过计算Luminance来获取到中心区域而在这里的中心区域可以有多个这也代表着在最终输出的SceneColor里可以有多个【曝点光晕(Bloom)效果】
2024-10-21 14:35:55 +08:00
2024-05-15 17:25:57 +08:00
# 实用代码
代码位于DeferredShadingCommon.ush
```c++
// @param UV - UV space in the GBuffer textures (BufferSize resolution)
FGBufferData GetGBufferData(float2 UV, bool bGetNormalizedNormal = true)
{
#if GBUFFER_REFACTOR
return DecodeGBufferDataUV(UV,bGetNormalizedNormal);
#else
float4 GBufferA = Texture2DSampleLevel(SceneTexturesStruct.GBufferATexture, SceneTexturesStruct_GBufferATextureSampler, UV, 0);
float4 GBufferB = Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0);
float4 GBufferC = Texture2DSampleLevel(SceneTexturesStruct.GBufferCTexture, SceneTexturesStruct_GBufferCTextureSampler, UV, 0);
float4 GBufferD = Texture2DSampleLevel(SceneTexturesStruct.GBufferDTexture, SceneTexturesStruct_GBufferDTextureSampler, UV, 0);
float CustomNativeDepth = Texture2DSampleLevel(SceneTexturesStruct.CustomDepthTexture, SceneTexturesStruct_CustomDepthTextureSampler, UV, 0).r;
// BufferToSceneTextureScale is necessary when translucent materials are rendered in a render target
// that has a different resolution than the scene color textures, e.g. r.SeparateTranslucencyScreenPercentage < 100.
int2 IntUV = (int2)trunc(UV * View.BufferSizeAndInvSize.xy * View.BufferToSceneTextureScale.xy);
uint CustomStencil = SceneTexturesStruct.CustomStencilTexture.Load(int3(IntUV, 0)) STENCIL_COMPONENT_SWIZZLE;
#if ALLOW_STATIC_LIGHTING
float4 GBufferE = Texture2DSampleLevel(SceneTexturesStruct.GBufferETexture, SceneTexturesStruct_GBufferETextureSampler, UV, 0);
#else
float4 GBufferE = 1;
#endif
float4 GBufferF = Texture2DSampleLevel(SceneTexturesStruct.GBufferFTexture, SceneTexturesStruct_GBufferFTextureSampler, UV, 0);
#if WRITES_VELOCITY_TO_GBUFFER
float4 GBufferVelocity = Texture2DSampleLevel(SceneTexturesStruct.GBufferVelocityTexture, SceneTexturesStruct_GBufferVelocityTextureSampler, UV, 0);
#else
float4 GBufferVelocity = 0;
#endif
float SceneDepth = CalcSceneDepth(UV);
return DecodeGBufferData(GBufferA, GBufferB, GBufferC, GBufferD, GBufferE, GBufferF, GBufferVelocity, CustomNativeDepth, CustomStencil, SceneDepth, bGetNormalizedNormal, CheckerFromSceneColorUV(UV));
#endif
}
// Minimal path for just the lighting model, used to branch around unlit pixels (skybox)
uint GetShadingModelId(float2 UV)
{
return DecodeShadingModelId(Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0).a);
}
```
2024-05-15 18:08:22 +08:00
## ShadingModel判断
```c++
bool IsToonShadingModel(float2 UV)
{
uint ShadingModel = DecodeShadingModelId(Texture2DSampleLevel(SceneTexturesStruct.GBufferBTexture, SceneTexturesStruct_GBufferBTextureSampler, UV, 0).a);
return ShadingModel == SHADINGMODELID_TOONSTANDARD
|| ShadingModel == SHADINGMODELID_PREINTEGRATED_SKIN;
}
```
PS.需要Shader添加FSceneTextureShaderParameters/FSceneTextureUniformParameters。
```c++
IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(FSceneTextureUniformParameters, "SceneTexturesStruct", SceneTextures);
BEGIN_SHADER_PARAMETER_STRUCT(FSceneTextureShaderParameters, ENGINE_API)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FMobileSceneTextureUniformParameters, MobileSceneTextures)
END_SHADER_PARAMETER_STRUCT()
2025-01-19 22:02:41 +08:00
```
# ToneMapping
2025-01-21 20:55:35 +08:00
- [现代游戏图形中的sRGB18%灰-中性灰的定义](https://zhuanlan.zhihu.com/p/654557489)
2025-01-19 22:02:41 +08:00
- UE4/UE5和ACES工作流程:https://zhuanlan.zhihu.com/p/660965710
2025-01-21 20:55:35 +08:00
- ACES
- ACES 2065-1:ACES 是成像行业的标准,它创建了一个三角形色域,涵盖了我们可以看到的所有光谱轨迹。
- AP0 / AP1这个“精确”的数字基数包括不可见区域和负数称为 AP0。为了使其更易于使用AP1 已改进为不包含负数。
- ACEScg色域三角形已被重新定义以改善 ACES2065-1 中的不便。虽然我们不得不放弃一些颜色,但它仍然覆盖了相当大的人类可见区域。
- ACEScctACEScc (Color Correction Space) 色彩校正空间的 AP1 版本。ACEScc [色彩空间](https://zhida.zhihu.com/search?content_id=235009763&content_type=Article&match_order=3&q=%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4&zhida_source=entity)定义略大于 ITU Rec.2020 色彩空间,并进行对数编码,以改进色彩校正器和分级工具中的使用,类似于 Cineon 文件的脚趾行为。
- Rec.709:我们的“标准”显示器可以显示的 Rec.709 色域只是非常宽的色彩工作空间的一小部分。
2025-01-19 22:02:41 +08:00
2025-01-20 18:47:35 +08:00
## ToneMapping种类
- ShaderToy效果演示: https://www.shadertoy.com/view/McG3WW
- ACES
- Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
- https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
- PBR Neutral https://modelviewer.dev/examples/tone-mapping
- Uncharted tonemapping
- http://filmicworlds.com/blog/filmic-tonemapping-operators/
- https://www.gdcvault.com/play/1012351/Uncharted-2-HDR
- AgX
- https://github.com/sobotka/AgX
- https://www.shadertoy.com/view/cd3XWr
## UE中的相关实现
2025-01-19 23:15:02 +08:00
UE4版本的笔记[[UE4 ToneMapping]]
2025-01-19 22:02:41 +08:00
TonemapCommon.ush中的FilmToneMap()在CombineLUTsCommon()中调用。其顺序为:
1. AddCombineLUTPass() => PostProcessCombineLUTs.usf
2. AddTonemapPass() => PostProcessTonemap.usf
```c++
void AddPostProcessingPasses()
{
...
{
FRDGTextureRef ColorGradingTexture = nullptr;
if (bPrimaryView)
{
ColorGradingTexture = AddCombineLUTPass(GraphBuilder, View);
}
// We can re-use the color grading texture from the primary view.
else if (View.GetTonemappingLUT())
{
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT());
}
else
{
const FViewInfo* PrimaryView = static_cast<const FViewInfo*>(View.Family->Views[0]);
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, PrimaryView->GetTonemappingLUT());
}
FTonemapInputs PassInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, PassInputs.OverrideOutput);
PassInputs.SceneColor = SceneColorSlice;
PassInputs.Bloom = Bloom;
PassInputs.SceneColorApplyParamaters = SceneColorApplyParameters;
PassInputs.LocalExposureTexture = LocalExposureTexture;
PassInputs.BlurredLogLuminanceTexture = LocalExposureBlurredLogLumTexture;
PassInputs.LocalExposureParameters = &LocalExposureParameters;
PassInputs.EyeAdaptationParameters = &EyeAdaptationParameters;
PassInputs.EyeAdaptationBuffer = EyeAdaptationBuffer;
PassInputs.ColorGradingTexture = ColorGradingTexture;
PassInputs.bWriteAlphaChannel = AntiAliasingMethod == AAM_FXAA || bProcessSceneColorAlpha;
PassInputs.bOutputInHDR = bTonemapOutputInHDR;
SceneColor = AddTonemapPass(GraphBuilder, View, PassInputs);
}
...
}
```
## PostProcessCombineLUTs.usf
2025-01-20 18:47:35 +08:00
相关变量更新函数位于FCachedLUTSettings::GetCombineLUTParameters()
2025-01-19 22:02:41 +08:00
2025-01-20 22:14:30 +08:00
## PostProcessTonemap.usf
## 实现方法
```c++
//BlueRose Modify
FGBufferData SamplerBuffer = GetGBufferData(UV * View.ResolutionFractionAndInv.x, false);
if (SamplerBuffer.CustomStencil > 1.0f && abs(SamplerBuffer.CustomDepth - SamplerBuffer.Depth) < 1)
{
// OutColor = SampleSceneColor(UV);
OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance);
}else
{
OutColor = TonemapCommonPS(UV, InVignette, GrainUV, ScreenPos, FullViewUV, SvPosition, Luminance);
}
//BlueRose Modify End
```